// Ghost Mechanic - Common Error Patterns
// These are the "babies" - learned patterns that help diagnose issues

import { PatternMatch, DiagnosisRequest } from './types';

export interface ErrorPattern {
  id: string;
  name: string;
  category: string;
  signals: PatternSignal[];
  diagnosis: {
    rootCause: string;
    explanation: string;
    fixTemplate: string;
  };
  confidence: number; // Base confidence when matched
}

export interface PatternSignal {
  type: 'error_message' | 'user_description' | 'stack_trace' | 'code_pattern' | 'action_log' | 'console';
  pattern: string | RegExp;
  weight: number; // How much this signal contributes to confidence
}

// Built-in error patterns - learned from common issues
export const COMMON_PATTERNS: ErrorPattern[] = [
  {
    id: 'async-onclick-no-await',
    name: 'Async onClick without await',
    category: 'async',
    signals: [
      { type: 'user_description', pattern: /nothing happens|button.*doesn.*work|click.*nothing/i, weight: 30 },
      { type: 'code_pattern', pattern: /onClick=\{\(\)\s*=>\s*\w+\(/i, weight: 25 },
      { type: 'code_pattern', pattern: /async function.*fetch|await.*fetch/i, weight: 20 },
      { type: 'action_log', pattern: /CLICK.*no response/i, weight: 15 },
      { type: 'console', pattern: /unhandled promise/i, weight: 10 }
    ],
    diagnosis: {
      rootCause: 'Async function called without await in onClick handler',
      explanation: 'The onClick handler is calling an async function but not awaiting it. The function starts execution but the UI does not reflect loading state or completion.',
      fixTemplate: 'onClick={async () => { setLoading(true); await {{functionName}}({{args}}); setLoading(false); }}'
    },
    confidence: 85
  },
  {
    id: 'missing-loading-state',
    name: 'Missing loading state',
    category: 'state',
    signals: [
      { type: 'user_description', pattern: /no feedback|doesn.*show loading|stuck|frozen/i, weight: 30 },
      { type: 'code_pattern', pattern: /fetch\(|axios\.|api\./i, weight: 20 },
      { type: 'code_pattern', pattern: /(?!.*isLoading|.*loading|.*setLoading)/i, weight: 25 },
      { type: 'action_log', pattern: /API_CALL.*duration.*[5-9]\d{2,}/i, weight: 15 }
    ],
    diagnosis: {
      rootCause: 'No loading state during async operations',
      explanation: 'The component makes API calls but does not show a loading indicator. Users see no feedback while waiting for responses.',
      fixTemplate: 'const [isLoading, setIsLoading] = useState(false); // Add before API calls: setIsLoading(true); // After: setIsLoading(false);'
    },
    confidence: 80
  },
  {
    id: 'form-validation-missing',
    name: 'Missing form validation',
    category: 'validation',
    signals: [
      { type: 'user_description', pattern: /submit.*empty|no validation|accepts anything/i, weight: 35 },
      { type: 'code_pattern', pattern: /<form|onSubmit/i, weight: 20 },
      { type: 'code_pattern', pattern: /(?!.*required|.*validate|.*yup|.*zod)/i, weight: 25 },
      { type: 'action_log', pattern: /SUBMIT.*payload.*null|SUBMIT.*payload.*""/i, weight: 20 }
    ],
    diagnosis: {
      rootCause: 'Form lacks input validation',
      explanation: 'The form accepts and submits data without validating required fields or data formats.',
      fixTemplate: 'Add validation: if (!formData.{{field}}) { setError("{{field}} is required"); return; }'
    },
    confidence: 75
  },
  {
    id: 'undefined-property-access',
    name: 'Accessing property of undefined',
    category: 'runtime',
    signals: [
      { type: 'error_message', pattern: /cannot read propert|undefined is not an object|null is not an object/i, weight: 50 },
      { type: 'stack_trace', pattern: /TypeError.*undefined|TypeError.*null/i, weight: 30 },
      { type: 'console', pattern: /cannot read|undefined/i, weight: 20 }
    ],
    diagnosis: {
      rootCause: 'Attempting to access a property on undefined or null',
      explanation: 'Code is trying to access a property or call a method on a value that is undefined or null, likely due to missing data or async timing.',
      fixTemplate: 'Add optional chaining: {{object}}?.{{property}} or null check: if ({{object}}) { ... }'
    },
    confidence: 90
  },
  {
    id: 'api-endpoint-mismatch',
    name: 'API endpoint mismatch',
    category: 'integration',
    signals: [
      { type: 'error_message', pattern: /404|not found|endpoint.*exist/i, weight: 40 },
      { type: 'action_log', pattern: /API_CALL.*404|API_CALL.*not found/i, weight: 35 },
      { type: 'console', pattern: /fetch.*404|failed to fetch/i, weight: 25 }
    ],
    diagnosis: {
      rootCause: 'Frontend calling non-existent API endpoint',
      explanation: 'The component is making API calls to an endpoint that does not exist or has a different path than expected.',
      fixTemplate: 'Verify endpoint path matches backend route. Expected: {{expected}}, Actual: {{actual}}'
    },
    confidence: 85
  },
  {
    id: 'state-not-updating-ui',
    name: 'State change not updating UI',
    category: 'state',
    signals: [
      { type: 'user_description', pattern: /doesn.*update|ui.*stuck|display.*wrong|not reflecting/i, weight: 35 },
      { type: 'code_pattern', pattern: /useState|setState/i, weight: 20 },
      { type: 'code_pattern', pattern: /\.push\(|\[.*\]\s*=/i, weight: 25 }, // Direct mutation signals
      { type: 'console', pattern: /state.*mutate/i, weight: 20 }
    ],
    diagnosis: {
      rootCause: 'Direct state mutation instead of using setter',
      explanation: 'The code is mutating state directly (e.g., array.push) instead of creating a new reference. React cannot detect direct mutations.',
      fixTemplate: 'Replace mutation with: set{{State}}([...{{state}}, newItem]) or set{{State}}(prev => [...prev, newItem])'
    },
    confidence: 80
  },
  {
    id: 'event-handler-binding',
    name: 'Event handler not bound correctly',
    category: 'props',
    signals: [
      { type: 'error_message', pattern: /this.*undefined|cannot read.*this/i, weight: 40 },
      { type: 'code_pattern', pattern: /onClick=\{\w+\}(?!.*=>|.*\.bind)/i, weight: 35 },
      { type: 'user_description', pattern: /this.*error|handler.*work/i, weight: 25 }
    ],
    diagnosis: {
      rootCause: 'Event handler losing "this" context',
      explanation: 'The event handler is passed as a reference but loses its "this" binding when called.',
      fixTemplate: 'Use arrow function: onClick={() => this.{{method}}()} or bind: onClick={this.{{method}}.bind(this)}'
    },
    confidence: 75
  },
  {
    id: 'missing-key-prop',
    name: 'Missing key prop in list',
    category: 'render',
    signals: [
      { type: 'console', pattern: /each child.*should have.*unique.*key/i, weight: 60 },
      { type: 'code_pattern', pattern: /\.map\(.*=>.*<(?!.*key=)/i, weight: 40 }
    ],
    diagnosis: {
      rootCause: 'List items missing unique key prop',
      explanation: 'When rendering lists with .map(), each element needs a unique "key" prop for React to efficiently update the DOM.',
      fixTemplate: 'Add key prop: {{items}}.map((item, index) => <{{Component}} key={item.id || index} ... />)'
    },
    confidence: 95
  },
  {
    id: 'infinite-loop-useeffect',
    name: 'Infinite loop in useEffect',
    category: 'render',
    signals: [
      { type: 'user_description', pattern: /infinite|keeps running|loop|freezes|crashes/i, weight: 30 },
      { type: 'console', pattern: /maximum update depth|too many re-renders/i, weight: 50 },
      { type: 'code_pattern', pattern: /useEffect\([^,]+\)(?!.*\[)/i, weight: 20 } // useEffect without deps
    ],
    diagnosis: {
      rootCause: 'useEffect missing or incorrect dependency array',
      explanation: 'The useEffect hook is missing a dependency array, causing it to run on every render, or its dependencies change on every render causing an infinite loop.',
      fixTemplate: 'Add dependency array: useEffect(() => { ... }, [{{dependencies}}]); // Empty [] for mount-only'
    },
    confidence: 85
  },
  {
    id: 'cors-error',
    name: 'CORS error',
    category: 'integration',
    signals: [
      { type: 'error_message', pattern: /cors|cross-origin|blocked by cors/i, weight: 60 },
      { type: 'console', pattern: /access-control-allow-origin|cors policy/i, weight: 40 }
    ],
    diagnosis: {
      rootCause: 'Cross-Origin Resource Sharing (CORS) policy blocking request',
      explanation: 'The browser is blocking the API request because the server does not include the proper CORS headers to allow requests from this origin.',
      fixTemplate: 'Backend needs to add: Access-Control-Allow-Origin header. For Next.js API routes, add: res.setHeader("Access-Control-Allow-Origin", "*")'
    },
    confidence: 90
  }
];

// Pattern matching engine
export function matchPatterns(request: DiagnosisRequest): PatternMatch[] {
  const matches: PatternMatch[] = [];

  for (const pattern of COMMON_PATTERNS) {
    let totalWeight = 0;
    let matchedWeight = 0;
    let matchedCount = 0;
    const matchedSignals: string[] = [];

    for (const signal of pattern.signals) {
      totalWeight += signal.weight;
      const signalMatched = checkSignal(signal, request);
      
      if (signalMatched) {
        matchedWeight += signal.weight;
        matchedCount++;
        matchedSignals.push(`${signal.type}: matched`);
      }
    }

    // Calculate confidence based on matched signals
    // Boost confidence if we have multiple matching signals
    const matchRatio = matchedWeight / totalWeight;
    const signalCountBonus = Math.min(20, matchedCount * 5); // Up to 20% bonus for multiple signals
    
    if (matchRatio > 0.2 || matchedCount >= 2) { // At least 20% weight matched OR 2+ signals
      // Base confidence from pattern, scaled by match ratio, plus signal count bonus
      const confidence = Math.min(95, Math.round(pattern.confidence * matchRatio) + signalCountBonus);
      matches.push({
        patternId: pattern.id,
        patternName: pattern.name,
        confidence,
        matchedSignals,
        suggestedFix: pattern.diagnosis.fixTemplate
      });
    }
  }

  // Sort by confidence descending
  return matches.sort((a, b) => b.confidence - a.confidence);
}

function checkSignal(signal: PatternSignal, request: DiagnosisRequest): boolean {
  const pattern = typeof signal.pattern === 'string' 
    ? new RegExp(signal.pattern, 'i') 
    : signal.pattern;

  switch (signal.type) {
    case 'error_message':
      return request.errorMessage ? pattern.test(request.errorMessage) : false;
    
    case 'user_description':
      return request.userDescription ? pattern.test(request.userDescription) : false;
    
    case 'stack_trace':
      return request.stackTrace ? pattern.test(request.stackTrace) : false;
    
    case 'code_pattern':
      return request.componentCode ? pattern.test(request.componentCode) : false;
    
    case 'action_log':
      if (!request.actionLog) return false;
      const logText = JSON.stringify(request.actionLog);
      return pattern.test(logText);
    
    case 'console':
      if (!request.browserConsole) return false;
      return request.browserConsole.some(line => pattern.test(line));
    
    default:
      return false;
  }
}

// Get pattern by ID
export function getPattern(patternId: string): ErrorPattern | undefined {
  return COMMON_PATTERNS.find(p => p.id === patternId);
}

// Get all patterns in a category
export function getPatternsByCategory(category: string): ErrorPattern[] {
  return COMMON_PATTERNS.filter(p => p.category === category);
}
