// Ghost Mechanic - Diagnostic Engine
// The brain of the "baby Tes" - diagnoses issues in Dark Factory artifacts

import {
  DiagnosisRequest,
  DiagnosisResult,
  ProposedFix,
  CodeChange,
  AffectedCode,
  Escalation,
  RepairLog
} from './types';
import { matchPatterns, getPattern, PatternMatch } from './patterns';

export class GhostMechanic {
  private repairHistory: RepairLog[] = [];
  private diagnosisIdCounter: number = 0;
  private fixIdCounter: number = 0;

  constructor() {
    console.log('[GhostMechanic] Initialized. Ready to diagnose.');
  }

  // Main diagnosis entry point
  async diagnose(request: DiagnosisRequest): Promise<DiagnosisResult | Escalation> {
    console.log(`[GhostMechanic] Diagnosing artifact: ${request.artifactId}`);
    console.log(`[GhostMechanic] Error type: ${request.errorType}`);
    console.log(`[GhostMechanic] User says: "${request.userDescription || 'No description'}"`);

    // Step 1: Pattern matching
    const patternMatches = matchPatterns(request);
    console.log(`[GhostMechanic] Found ${patternMatches.length} pattern matches`);

    // Step 2: Check if we have enough confidence
    if (patternMatches.length === 0) {
      return this.escalate('low_confidence', request);
    }

    const topMatch = patternMatches[0];
    
    // If confidence is too low and we're missing key info, ask for more
    if (topMatch.confidence < 40 && !request.componentCode) {
      const needsMoreInfo = this.determineNeededInfo(request, patternMatches);
      if (needsMoreInfo.length > 2) { // Only ask if we really need a lot more info
        return this.createLowConfidenceResult(patternMatches, needsMoreInfo);
      }
    }

    // Step 3: Build diagnosis result
    const diagnosis = await this.buildDiagnosis(request, topMatch, patternMatches);
    
    // Step 4: Log for learning
    this.logRepair(request, diagnosis);

    return diagnosis;
  }

  // Build a full diagnosis from pattern match
  private async buildDiagnosis(
    request: DiagnosisRequest,
    topMatch: PatternMatch,
    allMatches: PatternMatch[]
  ): Promise<DiagnosisResult> {
    const pattern = getPattern(topMatch.patternId)!;
    
    // Extract affected code if we have component code
    const affectedCode = this.extractAffectedCode(request, pattern);
    
    // Generate proposed fix
    const proposedFix = this.generateFix(request, pattern, affectedCode);

    // Build alternative causes from other matches
    const alternativeCauses = allMatches.slice(1, 4).map(match => ({
      description: match.patternName,
      confidence: match.confidence,
      howToVerify: `Check for: ${match.matchedSignals.join(', ')}`
    }));

    return {
      id: `diag-${++this.diagnosisIdCounter}`,
      confidence: topMatch.confidence,
      rootCause: pattern.diagnosis.rootCause,
      explanation: pattern.diagnosis.explanation,
      category: pattern.category as DiagnosisResult['category'],
      affectedCode,
      proposedFix,
      alternativeCauses: alternativeCauses.length > 0 ? alternativeCauses : undefined
    };
  }

  // Extract code snippets that are likely affected
  private extractAffectedCode(request: DiagnosisRequest, pattern: any): AffectedCode[] {
    if (!request.componentCode) return [];

    const affectedCode: AffectedCode[] = [];
    const lines = request.componentCode.split('\n');

    // Look for relevant patterns in code
    const searchPatterns: Record<string, RegExp> = {
      'async': /onClick|onSubmit|handle\w+|async|await|fetch/i,
      'state': /useState|setState|set\w+/i,
      'props': /props\.|\{\s*\w+\s*\}/i,
      'validation': /onSubmit|validate|required/i,
      'render': /return\s*\(|map\s*\(/i,
      'api': /fetch\(|axios|api\./i,
      'integration': /fetch\(|axios|api\.|endpoint/i
    };

    const searchPattern = searchPatterns[pattern.category] || /./;

    for (let i = 0; i < lines.length; i++) {
      if (searchPattern.test(lines[i])) {
        // Get context (2 lines before and after)
        const start = Math.max(0, i - 2);
        const end = Math.min(lines.length - 1, i + 2);
        const snippet = lines.slice(start, end + 1).join('\n');

        affectedCode.push({
          file: 'component.tsx', // Would be actual file in real implementation
          lines: [start + 1, end + 1], // 1-indexed
          snippet,
          highlightLines: [i + 1]
        });

        // Limit to 3 snippets
        if (affectedCode.length >= 3) break;
      }
    }

    return affectedCode;
  }

  // Generate a proposed fix
  private generateFix(
    request: DiagnosisRequest,
    pattern: any,
    affectedCode: AffectedCode[]
  ): ProposedFix {
    // Always provide a fix, even if we can't target specific lines
    const baseFix: ProposedFix = {
      id: `fix-${++this.fixIdCounter}`,
      description: `Apply pattern: ${pattern.diagnosis.fixTemplate}`,
      changes: [],
      testPlan: this.generateTestPlan(pattern, request),
      rollbackPlan: 'Revert the changes if the fix introduces new issues.',
      confidence: affectedCode.length > 0 ? 75 : 60,
      risks: affectedCode.length === 0 ? ['Cannot verify without seeing actual code'] : this.assessRisks(pattern),
      estimatedEffort: 'minor'
    };

    if (affectedCode.length === 0) {
      // Provide generic fix guidance
      baseFix.changes = [{
        file: 'component.tsx',
        type: 'modify',
        explanation: pattern.diagnosis.explanation,
        before: '// Locate the relevant code section',
        after: `// Apply fix: ${pattern.diagnosis.fixTemplate}`
      }];
      return baseFix;
    }

    // Generate specific changes based on pattern
    for (const code of affectedCode) {
      const change = this.generateCodeChange(code, pattern);
      if (change) baseFix.changes.push(change);
    }

    // Update description and confidence based on what we found
    baseFix.description = pattern.diagnosis.rootCause + ' - ' + pattern.diagnosis.explanation.split('.')[0];
    baseFix.confidence = Math.min(90, pattern.confidence + 5);
    baseFix.estimatedEffort = baseFix.changes.length > 2 ? 'moderate' : 'minor';
    baseFix.rollbackPlan = 'Revert the code changes and verify original behavior is restored.';

    return baseFix;
  }

  // Generate a specific code change
  private generateCodeChange(code: AffectedCode, pattern: any): CodeChange | null {
    // This is simplified - real implementation would do AST manipulation
    const before = code.snippet;
    let after = before;

    // Pattern-specific transformations
    switch (pattern.id) {
      case 'async-onclick-no-await':
        // Transform onClick={() => func()} to onClick={async () => { await func(); }}
        after = before.replace(
          /onClick=\{\(\)\s*=>\s*(\w+)\(([^)]*)\)\}/,
          'onClick={async () => { setLoading(true); try { await $1($2); } finally { setLoading(false); } }}'
        );
        break;

      case 'missing-loading-state':
        // Suggest adding loading state
        after = '// Add at component top:\nconst [isLoading, setIsLoading] = useState(false);\n\n' + before;
        break;

      case 'undefined-property-access':
        // Add optional chaining
        after = before.replace(
          /(\w+)\.(\w+)/g,
          '$1?.$2'
        );
        break;

      case 'missing-key-prop':
        // Add key prop to map
        after = before.replace(
          /\.map\(\s*\((\w+)(?:,\s*(\w+))?\)\s*=>\s*<(\w+)/g,
          '.map(($1, $2) => <$3 key={$1.id || $2}'
        );
        break;

      default:
        // Generic suggestion
        after = `// TODO: ${pattern.diagnosis.fixTemplate}\n${before}`;
    }

    if (after === before) return null;

    return {
      file: code.file,
      type: 'modify',
      lineStart: code.lines[0],
      lineEnd: code.lines[1],
      before,
      after,
      explanation: pattern.diagnosis.explanation
    };
  }

  // Generate test plan based on pattern
  private generateTestPlan(pattern: any, request: DiagnosisRequest): string {
    const plans: Record<string, string> = {
      'async': '1. Click the affected button/trigger\n2. Verify loading state appears\n3. Verify action completes successfully\n4. Verify UI updates correctly',
      'state': '1. Perform the action that should update state\n2. Verify UI reflects the new state\n3. Check React DevTools to confirm state value',
      'validation': '1. Submit form with empty/invalid data\n2. Verify validation errors appear\n3. Submit with valid data\n4. Verify success',
      'render': '1. Load the component\n2. Verify no console errors\n3. Verify list renders correctly with unique keys',
      'integration': '1. Open Network tab in DevTools\n2. Trigger the API call\n3. Verify correct endpoint is called\n4. Verify response is handled'
    };

    return plans[pattern.category] || 'Verify the reported issue is resolved by testing the affected functionality.';
  }

  // Assess risks of applying the fix
  private assessRisks(pattern: any): string[] {
    const risks: string[] = [];

    if (pattern.category === 'state') {
      risks.push('State changes may affect other components depending on this data');
    }
    if (pattern.category === 'async') {
      risks.push('Async changes may affect loading states and error handling');
    }
    if (pattern.category === 'integration') {
      risks.push('API changes may require backend updates');
    }

    return risks;
  }

  // Determine what additional info would help diagnosis
  private determineNeededInfo(request: DiagnosisRequest, matches: PatternMatch[]): string[] {
    const needed: string[] = [];

    if (!request.componentCode) {
      needed.push('Please provide the component source code for more accurate diagnosis.');
    }
    if (!request.actionLog || request.actionLog.length === 0) {
      needed.push('Can you share the action log from the Weft Sandbox?');
    }
    if (!request.browserConsole || request.browserConsole.length === 0) {
      needed.push('Are there any errors in the browser console?');
    }
    if (!request.userDescription) {
      needed.push('Can you describe what you expected to happen vs what actually happened?');
    }

    return needed;
  }

  // Create a low-confidence result asking for more info
  private createLowConfidenceResult(matches: PatternMatch[], needsMoreInfo: string[]): DiagnosisResult {
    const topMatch = matches[0];
    const pattern = getPattern(topMatch.patternId);

    return {
      id: `diag-${++this.diagnosisIdCounter}`,
      confidence: topMatch.confidence,
      rootCause: pattern?.diagnosis.rootCause || 'Unable to determine with high confidence',
      explanation: `I found some potential patterns but I'm only ${topMatch.confidence}% confident. More information would help.`,
      category: 'unknown',
      affectedCode: [],
      needsMoreInfo,
      alternativeCauses: matches.slice(0, 3).map(m => ({
        description: m.patternName,
        confidence: m.confidence,
        howToVerify: `Check: ${m.matchedSignals[0] || 'pattern signals'}`
      }))
    };
  }

  // Escalate to human when we can't help
  private escalate(reason: Escalation['reason'], request: DiagnosisRequest): Escalation {
    const escalations: Record<Escalation['reason'], Escalation> = {
      'low_confidence': {
        reason: 'low_confidence',
        explanation: 'I could not find patterns matching this issue. The problem may be specific to your implementation.',
        suggestions: [
          'Try adding console.log statements to trace the execution flow',
          'Check the browser DevTools Network tab for failed requests',
          'Verify the component props are being passed correctly',
          'Review recent changes that might have introduced the issue'
        ]
      },
      'outside_scope': {
        reason: 'outside_scope',
        explanation: 'This issue appears to involve systems outside the generated artifact.',
        suggestions: [
          'Check your backend/database configuration',
          'Verify third-party service credentials',
          'Review infrastructure or deployment settings'
        ]
      },
      'ambiguous_intent': {
        reason: 'ambiguous_intent',
        explanation: 'The original specification for this component is unclear, making it hard to determine correct behavior.',
        suggestions: [
          'Review the original generation spec',
          'Clarify the expected behavior',
          'Consider regenerating with a more specific prompt'
        ]
      },
      'integration_issue': {
        reason: 'integration_issue',
        explanation: 'The issue is in the integration between the generated component and your existing systems.',
        suggestions: [
          'Verify API endpoint paths match your backend',
          'Check authentication/authorization flow',
          'Ensure data formats are compatible'
        ]
      },
      'complex_refactor': {
        reason: 'complex_refactor',
        explanation: 'Fixing this issue requires significant architectural changes that should be carefully planned.',
        suggestions: [
          'Consider regenerating the component with updated requirements',
          'Break down the refactor into smaller changes',
          'Consult with a developer for complex changes'
        ]
      },
      'missing_context': {
        reason: 'missing_context',
        explanation: 'I need more context to diagnose this issue.',
        suggestions: [
          'Provide the component source code',
          'Share any error messages or console output',
          'Describe the steps to reproduce the issue'
        ]
      }
    };

    const escalation = escalations[reason];
    
    // Add support ticket draft
    escalation.supportTicketDraft = this.generateSupportTicket(request, escalation);

    return escalation;
  }

  // Generate a support ticket for human escalation
  private generateSupportTicket(request: DiagnosisRequest, escalation: Escalation): string {
    return `
## Issue Report

**Artifact ID:** ${request.artifactId}
**Error Type:** ${request.errorType}
**Escalation Reason:** ${escalation.reason}

### User Description
${request.userDescription || 'No description provided'}

### Error Message
${request.errorMessage || 'None'}

### Ghost Mechanic Assessment
${escalation.explanation}

### Suggested Next Steps
${escalation.suggestions.map((s, i) => `${i + 1}. ${s}`).join('\n')}

### Technical Details
- Stack Trace: ${request.stackTrace ? 'Provided' : 'Not provided'}
- Console Logs: ${request.browserConsole?.length || 0} entries
- Action Log: ${request.actionLog?.length || 0} entries
- Code Available: ${request.componentCode ? 'Yes' : 'No'}
    `.trim();
  }

  // Log repair for learning
  private logRepair(request: DiagnosisRequest, diagnosis: DiagnosisResult): void {
    const log: RepairLog = {
      id: `repair-${Date.now()}`,
      timestamp: new Date(),
      artifactId: request.artifactId,
      errorType: request.errorType,
      diagnosis,
      outcome: 'pending'
    };
    this.repairHistory.push(log);
  }

  // Record fix outcome (for learning)
  recordOutcome(diagnosisId: string, outcome: RepairLog['outcome'], feedback?: string): void {
    const repair = this.repairHistory.find(r => r.diagnosis.id === diagnosisId);
    if (repair) {
      repair.outcome = outcome;
      repair.userFeedback = feedback;
      repair.timeToFix = Date.now() - repair.timestamp.getTime();
    }
  }

  // Get repair history
  getRepairHistory(): RepairLog[] {
    return [...this.repairHistory];
  }

  // Get success rate
  getSuccessRate(): { total: number; successful: number; rate: number } {
    const completed = this.repairHistory.filter(r => r.outcome !== 'pending');
    const successful = completed.filter(r => r.outcome === 'success');
    return {
      total: completed.length,
      successful: successful.length,
      rate: completed.length > 0 ? Math.round((successful.length / completed.length) * 100) : 0
    };
  }
}

// Factory function
export function createGhostMechanic(): GhostMechanic {
  return new GhostMechanic();
}
