/**
 * LLM Verifier Execution
 *
 * Uses an LLM to verify code changes.
 */

import { generateText, type LanguageModel } from 'ai';
import { z } from 'zod';
import type { VerifierConfig, VerifierResult, VerificationContext, VerifierIssue } from './types.js';
import { truncateOutput } from './issues.js';
import { createLogger, type Logger } from '../utils/logger.js';

/**
 * Options for LLM verifier
 */
export interface LLMVerifierOptions {
  /** LLM model to use */
  model: LanguageModel;
  /** Logger instance */
  logger?: Logger;
}

/**
 * Expected LLM response schema
 */
const LLMResponseSchema = z.object({
  pass: z.boolean(),
  issues: z.array(z.string()).default([]),
  summary: z.string().default(''),
});

/**
 * Run an LLM-based verifier
 */
export async function runLLMVerifier(
  config: VerifierConfig,
  context: VerificationContext,
  options: LLMVerifierOptions
): Promise<VerifierResult> {
  const logger = options.logger ?? createLogger({ name: 'llm-verifier' });
  const startTime = Date.now();

  if (!config.prompt) {
    return {
      name: config.name,
      passed: false,
      output: '',
      duration: 0,
      issues: [],
      error: 'No prompt specified for LLM verifier',
    };
  }

  logger.debug({ name: config.name }, 'Running LLM verifier');

  // Build the verification prompt
  const systemPrompt = `You are a code verification assistant. Your task is to review code changes and provide structured feedback.

You MUST respond with valid JSON in this exact format:
{
  "pass": true or false,
  "issues": ["issue 1", "issue 2", ...],
  "summary": "Brief summary of your review"
}

If the code meets the criteria, set "pass": true and "issues": [].
If there are problems, set "pass": false and list the issues.`;

  const userPrompt = buildUserPrompt(config.prompt, context);

  try {
    const { text } = await generateText({
      model: options.model,
      system: systemPrompt,
      prompt: userPrompt,
      maxTokens: 2048,
      temperature: 0.1,
    });

    const duration = (Date.now() - startTime) / 1000;
    const output = truncateOutput(text);

    // Parse the response
    const { passed, issues } = parseResponse(text, logger);

    logger.debug({ name: config.name, passed, issueCount: issues.length, duration }, 'LLM verifier completed');

    return {
      name: config.name,
      passed,
      output,
      duration,
      issues,
    };
  } catch (error) {
    const duration = (Date.now() - startTime) / 1000;
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';

    logger.error({ name: config.name, error: errorMessage }, 'LLM verifier failed');

    return {
      name: config.name,
      passed: false,
      output: '',
      duration,
      issues: [{ message: `LLM verification failed: ${errorMessage}`, severity: 'error' }],
      error: errorMessage,
    };
  }
}

/**
 * Build the user prompt with context
 */
function buildUserPrompt(basePrompt: string, context: VerificationContext): string {
  const parts: string[] = [basePrompt];

  if (context.goal) {
    parts.push(`\n\n## Goal\n${context.goal}`);
  }

  if (context.filesChanged && context.filesChanged.length > 0) {
    parts.push(`\n\n## Files Changed\n${context.filesChanged.join('\n')}`);
  }

  if (context.gitDiff) {
    // Truncate diff if too long
    const maxDiffLength = 10000;
    const diff =
      context.gitDiff.length > maxDiffLength
        ? context.gitDiff.slice(0, maxDiffLength) + '\n... (diff truncated)'
        : context.gitDiff;
    parts.push(`\n\n## Git Diff\n\`\`\`diff\n${diff}\n\`\`\``);
  }

  return parts.join('');
}

/**
 * Parse LLM response into structured result
 */
function parseResponse(
  text: string,
  logger: Logger
): { passed: boolean; issues: VerifierIssue[] } {
  // Try to extract JSON from the response
  const jsonMatch = text.match(/\{[\s\S]*\}/);
  if (jsonMatch) {
    try {
      const parsed = JSON.parse(jsonMatch[0]);
      const validated = LLMResponseSchema.safeParse(parsed);

      if (validated.success) {
        return {
          passed: validated.data.pass,
          issues: validated.data.issues.map((msg) => ({
            message: msg,
            severity: 'error' as const,
          })),
        };
      }
    } catch {
      logger.debug('Failed to parse JSON from LLM response, falling back to keyword detection');
    }
  }

  // Fallback: keyword-based detection
  const lower = text.toLowerCase();

  // Positive indicators
  const passIndicators = ['pass', 'approved', 'lgtm', 'looks good', 'no issues'];
  const hasPass = passIndicators.some((indicator) => lower.includes(indicator));

  // Negative indicators
  const failIndicators = ['fail', 'issue', 'bug', 'error', 'problem', 'concern'];
  const hasFail = failIndicators.some((indicator) => lower.includes(indicator));

  // If both or neither, use heuristics
  const passed = hasPass && !hasFail;

  // Extract issues from text if failed
  const issues: VerifierIssue[] = [];
  if (!passed) {
    // Look for numbered lists
    const listMatches = text.match(/^\s*\d+[.)]\s*(.+)$/gm);
    if (listMatches) {
      for (const match of listMatches.slice(0, 10)) {
        const cleaned = match.replace(/^\s*\d+[.)]\s*/, '').trim();
        if (cleaned) {
          issues.push({ message: cleaned, severity: 'error' });
        }
      }
    }

    // If no list items found, use the summary
    if (issues.length === 0) {
      issues.push({ message: 'LLM verification failed (see output for details)', severity: 'error' });
    }
  }

  return { passed, issues };
}
