// End-to-End Test: Weft Sandbox + Ghost Mechanic
// Tests the full flow: Generate -> Preview -> Break -> Diagnose -> Fix

import { WeftSandbox, createSandbox } from '../weft/core/sandbox';
import { GhostMechanic, createGhostMechanic } from '../mechanic/core/diagnoser';
import { DiagnosisRequest, DiagnosisResult, Escalation } from '../mechanic/core/types';
import { orderFormWeftConfig } from './test-artifacts/order-form-weft.config';
import * as fs from 'fs';
import * as path from 'path';

// Test Results
interface TestResult {
  name: string;
  passed: boolean;
  duration: number;
  details: string;
}

const testResults: TestResult[] = [];

function logTest(name: string, passed: boolean, details: string, startTime: number) {
  const duration = Date.now() - startTime;
  testResults.push({ name, passed, duration, details });
  const status = passed ? '✅' : '❌';
  console.log(`${status} ${name} (${duration}ms)`);
  if (!passed) console.log(`   Details: ${details}`);
}

async function runTests() {
  console.log('\n========================================');
  console.log('  WEFT SANDBOX + GHOST MECHANIC E2E TEST');
  console.log('========================================\n');

  // Load buggy component code
  const componentPath = path.join(__dirname, 'test-artifacts/order-form-buggy.tsx');
  const componentCode = fs.readFileSync(componentPath, 'utf-8');

  // Initialize systems
  console.log('[Setup] Initializing Weft Sandbox...');
  const sandbox = createSandbox(orderFormWeftConfig);
  sandbox.start();

  console.log('[Setup] Initializing Ghost Mechanic...');
  const mechanic = createGhostMechanic();

  console.log('[Setup] Complete.\n');

  // ========================================
  // TEST 1: Sandbox Mock Data Provider
  // ========================================
  let startTime = Date.now();
  try {
    const products = sandbox.getMockData('products');
    const passed = Array.isArray(products) && products.length === 3;
    logTest('Sandbox: Mock data provider', passed, 
      passed ? `Loaded ${products.length} products` : 'Failed to load mock data');
  } catch (e) {
    logTest('Sandbox: Mock data provider', false, String(e), startTime);
  }

  // ========================================
  // TEST 2: Sandbox API Stubs
  // ========================================
  startTime = Date.now();
  try {
    const response = await sandbox.handleFetch('POST', '/api/orders', {
      customerId: 'C001',
      items: [{ sku: 'WIDGET-A', quantity: 5 }],
      notes: 'Test order'
    });
    const data = await response.json();
    const passed = response.status === 201 && data.success === true;
    logTest('Sandbox: API stub interception', passed,
      passed ? `Order created: ${data.orderId}` : `Status: ${response.status}`);
  } catch (e) {
    logTest('Sandbox: API stub interception', false, String(e), startTime);
  }

  // ========================================
  // TEST 3: Action Logging
  // ========================================
  startTime = Date.now();
  try {
    sandbox.logClick({ type: 'button', text: 'Add Item', selector: 'button.add-item' });
    sandbox.logInput({ type: 'input', id: 'quantity', selector: '#quantity' }, 5);
    sandbox.logSelect({ type: 'select', id: 'product', selector: '#product-select' }, 'WIDGET-A');
    sandbox.logSubmit(
      { type: 'form', selector: 'form.order-form' },
      { customerId: 'C001', items: [] },
      '/api/orders',
      'POST'
    );
    
    const logs = sandbox.getActionLogs();
    const passed = logs.length >= 4;
    logTest('Sandbox: Action logging', passed,
      passed ? `Logged ${logs.length} actions` : 'Insufficient action logs');
  } catch (e) {
    logTest('Sandbox: Action logging', false, String(e), startTime);
  }

  // ========================================
  // TEST 4: Action Summary
  // ========================================
  startTime = Date.now();
  try {
    const summary = sandbox.getActionSummary();
    const passed = summary.totalActions > 0 && summary.apiCallCount >= 1;
    logTest('Sandbox: Action summary', passed,
      `Total: ${summary.totalActions}, API calls: ${summary.apiCallCount}, Avg latency: ${summary.averageApiLatency}ms`);
  } catch (e) {
    logTest('Sandbox: Action summary', false, String(e), startTime);
  }

  // ========================================
  // TEST 5: API Documentation Export
  // ========================================
  startTime = Date.now();
  try {
    const apiDocs = sandbox.getApiDocs();
    const passed = apiDocs.endpoints.length === 3;
    logTest('Sandbox: API docs export', passed,
      passed ? `Exported ${apiDocs.endpoints.length} endpoints` : 'Wrong endpoint count');
  } catch (e) {
    logTest('Sandbox: API docs export', false, String(e), startTime);
  }

  console.log('\n--- Ghost Mechanic Diagnosis Tests ---\n');

  // ========================================
  // TEST 6: Diagnose "nothing happens" bug
  // ========================================
  startTime = Date.now();
  try {
    const request: DiagnosisRequest = {
      artifactId: 'order-form-v1',
      errorType: 'behavior',
      userDescription: 'When I click Submit Order, nothing happens. The button just stays there with no loading or feedback.',
      componentCode,
      actionLog: sandbox.getActionLogs().map(l => ({
        ...l,
        timestamp: l.timestamp.toISOString()
      }))
    };

    const result = await mechanic.diagnose(request);
    const diagnosis = result as DiagnosisResult;
    const passed = 'confidence' in result && diagnosis.confidence >= 50;
    logTest('Mechanic: Diagnose async/loading issue', passed,
      `Confidence: ${diagnosis.confidence}%, Cause: ${diagnosis.rootCause?.substring(0, 50) || 'N/A'}...`);
  } catch (e) {
    logTest('Mechanic: Diagnose async/loading issue', false, String(e), startTime);
  }

  // ========================================
  // TEST 7: Diagnose state mutation bug
  // ========================================
  startTime = Date.now();
  try {
    const request: DiagnosisRequest = {
      artifactId: 'order-form-v1',
      errorType: 'behavior',
      userDescription: 'When I click Add Item, the item does not appear in the list. The UI is not updating.',
      componentCode
    };

    const result = await mechanic.diagnose(request);
    const diagnosis = result as DiagnosisResult;
    const passed = 'confidence' in result && diagnosis.confidence >= 45;
    logTest('Mechanic: Diagnose state mutation issue', passed,
      `Confidence: ${diagnosis.confidence}%, Cause: ${diagnosis.rootCause?.substring(0, 50) || 'N/A'}...`);
  } catch (e) {
    logTest('Mechanic: Diagnose state mutation issue', false, String(e), startTime);
  }

  // ========================================
  // TEST 8: Diagnose undefined error
  // ========================================
  startTime = Date.now();
  try {
    const request: DiagnosisRequest = {
      artifactId: 'order-form-v1',
      errorType: 'runtime',
      errorMessage: "TypeError: Cannot read property 'price' of undefined",
      userDescription: 'The page crashes when I try to see the total.',
      componentCode,
      browserConsole: [
        "TypeError: Cannot read property 'price' of undefined",
        "    at getTotal (OrderForm.tsx:52:32)"
      ]
    };

    const result = await mechanic.diagnose(request);
    const diagnosis = result as DiagnosisResult;
    const passed = 'confidence' in result && diagnosis.confidence >= 70;
    logTest('Mechanic: Diagnose undefined access', passed,
      `Confidence: ${diagnosis.confidence}%, Category: ${diagnosis.category}`);
  } catch (e) {
    logTest('Mechanic: Diagnose undefined access', false, String(e), startTime);
  }

  // ========================================
  // TEST 9: Fix proposal generation
  // ========================================
  startTime = Date.now();
  try {
    const request: DiagnosisRequest = {
      artifactId: 'order-form-v1',
      errorType: 'behavior',
      userDescription: 'Submit button does nothing',
      componentCode
    };

    const result = await mechanic.diagnose(request);
    const diagnosis = result as DiagnosisResult;
    const passed = diagnosis.proposedFix !== undefined && diagnosis.proposedFix.changes.length > 0;
    logTest('Mechanic: Fix proposal generation', passed,
      passed ? `Generated ${diagnosis.proposedFix!.changes.length} code change(s), Effort: ${diagnosis.proposedFix!.estimatedEffort}` : 'No fix proposed');
  } catch (e) {
    logTest('Mechanic: Fix proposal generation', false, String(e), startTime);
  }

  // ========================================
  // TEST 10: Repair history tracking
  // ========================================
  startTime = Date.now();
  try {
    const history = mechanic.getRepairHistory();
    const successRate = mechanic.getSuccessRate();
    const passed = history.length >= 4;
    logTest('Mechanic: Repair history tracking', passed,
      `${history.length} repairs logged, Success rate: ${successRate.rate}% (${successRate.successful}/${successRate.total})`);
  } catch (e) {
    logTest('Mechanic: Repair history tracking', false, String(e), startTime);
  }

  // ========================================
  // TEST 11: Escalation handling
  // ========================================
  startTime = Date.now();
  try {
    const request: DiagnosisRequest = {
      artifactId: 'order-form-v1',
      errorType: 'unknown',
      userDescription: 'Something weird is happening but I cannot describe it'
      // No code, no logs, no error message - should escalate
    };

    const result = await mechanic.diagnose(request);
    // With minimal info, it should either escalate or ask for more info
    const isEscalation = 'reason' in result;
    const isLowConfidence = 'needsMoreInfo' in result && (result as DiagnosisResult).needsMoreInfo!.length > 0;
    const passed = isEscalation || isLowConfidence;
    logTest('Mechanic: Escalation/info request', passed,
      isEscalation ? `Escalated: ${(result as Escalation).reason}` : 
      isLowConfidence ? `Requested more info: ${(result as DiagnosisResult).needsMoreInfo!.length} questions` : 'Unexpected high confidence');
  } catch (e) {
    logTest('Mechanic: Escalation/info request', false, String(e), startTime);
  }

  // ========================================
  // TEST 12: Full E2E Flow
  // ========================================
  startTime = Date.now();
  try {
    // Simulate user flow
    sandbox.logClick({ type: 'select', selector: '#product' });
    sandbox.logSelect({ type: 'select', selector: '#product' }, 'WIDGET-A');
    sandbox.logInput({ type: 'input', selector: '#quantity' }, 3);
    sandbox.logClick({ type: 'button', text: 'Add Item', selector: 'button.add' });
    sandbox.logClick({ type: 'button', text: 'Submit Order', selector: 'button.submit' });

    // API call
    await sandbox.handleFetch('POST', '/api/orders', {
      customerId: 'C001',
      items: [{ sku: 'WIDGET-A', quantity: 3 }]
    });

    // Export action log
    const logText = sandbox.exportActionsAsText();
    
    // Now diagnose based on actions
    const request: DiagnosisRequest = {
      artifactId: 'order-form-v1',
      errorType: 'behavior',
      userDescription: 'I added an item and clicked submit but nothing changed on screen',
      componentCode,
      actionLog: sandbox.getActionLogs().map(l => ({
        ...l,
        timestamp: l.timestamp.toISOString()
      }))
    };

    const diagnosis = await mechanic.diagnose(request) as DiagnosisResult;
    
    const passed = diagnosis.confidence >= 50 && 
                   diagnosis.proposedFix !== undefined &&
                   sandbox.getActionLogs().length > 5;
    
    logTest('Full E2E: Sandbox -> Log -> Diagnose -> Fix', passed,
      `Actions: ${sandbox.getActionLogs().length}, Confidence: ${diagnosis.confidence}%, Fix: ${diagnosis.proposedFix?.changes.length || 0} changes`);
  } catch (e) {
    logTest('Full E2E: Sandbox -> Log -> Diagnose -> Fix', false, String(e), startTime);
  }

  // ========================================
  // Summary
  // ========================================
  sandbox.stop();

  console.log('\n========================================');
  console.log('  TEST SUMMARY');
  console.log('========================================\n');

  const passed = testResults.filter(t => t.passed).length;
  const failed = testResults.filter(t => !t.passed).length;
  const totalTime = testResults.reduce((sum, t) => sum + t.duration, 0);

  console.log(`Total Tests: ${testResults.length}`);
  console.log(`Passed: ${passed} ✅`);
  console.log(`Failed: ${failed} ${failed > 0 ? '❌' : ''}`);
  console.log(`Total Time: ${totalTime}ms`);
  console.log(`Average Time: ${Math.round(totalTime / testResults.length)}ms per test`);

  if (failed > 0) {
    console.log('\nFailed Tests:');
    testResults.filter(t => !t.passed).forEach(t => {
      console.log(`  - ${t.name}: ${t.details}`);
    });
  }

  console.log('\n========================================\n');

  return { passed, failed, totalTime };
}

// Run tests
runTests().then(results => {
  process.exit(results.failed > 0 ? 1 : 0);
}).catch(err => {
  console.error('Test runner failed:', err);
  process.exit(1);
});
