/**
 * Issue Extraction Utilities
 *
 * Extract structured issues from command output.
 */

import type { VerifierIssue } from './types.js';

/**
 * Maximum number of issues to extract per pattern
 */
const MAX_ISSUES_PER_PATTERN = 5;

/**
 * Maximum total issues to extract
 */
const MAX_TOTAL_ISSUES = 10;

/**
 * Maximum issue message length
 */
const MAX_ISSUE_LENGTH = 200;

/**
 * Patterns for detecting failure indicators in output
 */
const FAILURE_PATTERNS: RegExp[] = [
  // Test frameworks
  /FAIL[:\s]+(.+)/gi,
  /FAILED[:\s]+(.+)/gi,
  /ERROR[:\s]+(.+)/gi,
  /Error[:\s]+(.+)/g,

  // Python exceptions
  /AssertionError[:\s]+(.+)/g,
  /TypeError[:\s]+(.+)/g,
  /ValueError[:\s]+(.+)/g,
  /AttributeError[:\s]+(.+)/g,
  /ImportError[:\s]+(.+)/g,
  /ModuleNotFoundError[:\s]+(.+)/g,

  // JavaScript/TypeScript errors
  /ReferenceError[:\s]+(.+)/g,
  /SyntaxError[:\s]+(.+)/g,
  /TypeError[:\s]+(.+)/g,

  // Special indicators
  /✗\s*(.+)/g,
  /×\s*(.+)/g,
  /\[FAIL\]\s*(.+)/gi,
  /\[ERROR\]\s*(.+)/gi,

  // Linter patterns
  /error\s+TS\d+[:\s]+(.+)/g,
  /error[:\s]+(.+)/gi,
];

/**
 * Pattern for extracting file:line references
 */
const FILE_LINE_PATTERN = /([^\s:]+):(\d+)(?::\d+)?/;

/**
 * Extract issues from command output
 */
export function extractIssues(output: string): VerifierIssue[] {
  const issues: VerifierIssue[] = [];
  const seenMessages = new Set<string>();

  // Try each failure pattern
  for (const pattern of FAILURE_PATTERNS) {
    if (issues.length >= MAX_TOTAL_ISSUES) break;

    // Reset regex lastIndex
    pattern.lastIndex = 0;
    let patternMatches = 0;

    let match: RegExpExecArray | null;
    while ((match = pattern.exec(output)) !== null) {
      if (patternMatches >= MAX_ISSUES_PER_PATTERN) break;
      if (issues.length >= MAX_TOTAL_ISSUES) break;

      const message = truncateMessage(match[1] || match[0]);

      // Skip duplicates
      if (seenMessages.has(message.toLowerCase())) continue;
      seenMessages.add(message.toLowerCase());

      const issue = createIssue(message);
      issues.push(issue);
      patternMatches++;
    }
  }

  // If no patterns matched, extract first few lines
  if (issues.length === 0) {
    const lines = output.split('\n').filter((l) => l.trim());
    const fallbackLines = lines.slice(0, 3);

    for (const line of fallbackLines) {
      if (issues.length >= MAX_TOTAL_ISSUES) break;

      const trimmed = line.trim();
      if (trimmed && !seenMessages.has(trimmed.toLowerCase())) {
        seenMessages.add(trimmed.toLowerCase());
        issues.push(createIssue(truncateMessage(trimmed)));
      }
    }
  }

  return issues;
}

/**
 * Create an issue from a message, extracting file/line if present
 */
function createIssue(message: string): VerifierIssue {
  const issue: VerifierIssue = { message };

  // Try to extract file:line reference
  const fileMatch = FILE_LINE_PATTERN.exec(message);
  if (fileMatch) {
    issue.file = fileMatch[1];
    issue.line = parseInt(fileMatch[2], 10);
  }

  // Determine severity from keywords
  const lowerMessage = message.toLowerCase();
  if (lowerMessage.includes('error') || lowerMessage.includes('fail')) {
    issue.severity = 'error';
  } else if (lowerMessage.includes('warn')) {
    issue.severity = 'warning';
  } else {
    issue.severity = 'info';
  }

  return issue;
}

/**
 * Truncate message to max length
 */
function truncateMessage(message: string): string {
  const trimmed = message.trim();
  if (trimmed.length <= MAX_ISSUE_LENGTH) {
    return trimmed;
  }
  return trimmed.slice(0, MAX_ISSUE_LENGTH - 3) + '...';
}

/**
 * Truncate output to max length
 */
export function truncateOutput(output: string, maxLength: number = 5000): string {
  if (output.length <= maxLength) {
    return output;
  }
  return output.slice(0, maxLength - 20) + '\n... (truncated)';
}

/**
 * Check if output indicates success (heuristic)
 */
export function looksLikeSuccess(output: string): boolean {
  const lower = output.toLowerCase();

  // Positive indicators
  const hasSuccess = /pass(ed)?|success|ok|✓|✔|all tests passed/i.test(output);

  // Negative indicators
  const hasFailure = /fail(ed)?|error|✗|×|\berrors?\b/i.test(output);

  // If we have success indicators and no failure indicators, likely passed
  if (hasSuccess && !hasFailure) return true;

  // If we have failure indicators, likely failed
  if (hasFailure) return false;

  // Default to success if no indicators
  return true;
}

/**
 * Format issues for display
 */
export function formatIssues(issues: VerifierIssue[]): string {
  if (issues.length === 0) return 'No issues found';

  return issues
    .map((issue, i) => {
      let line = `${i + 1}. ${issue.message}`;
      if (issue.file) {
        line += ` (${issue.file}`;
        if (issue.line) line += `:${issue.line}`;
        line += ')';
      }
      return line;
    })
    .join('\n');
}
