/* eslint-disable @typescript-eslint/no-unused-vars */
import { config } from '../../lib/config.js';
import type {
  ErrorClusterDoc,
  DiagnosticInsightDoc,
  RootCauseCategory,
  Evidence,
  SimilarResolvedIssue,
} from './types.js';
import type { ClusterContextSummary } from './telemetry-linking.js';

// ============================================================================
// LLM Prompts
// ============================================================================

interface RootCauseAnalysisPrompt {
  cluster: ErrorClusterDoc;
  context: ClusterContextSummary;
  sampleStackTraces: string[];
  relatedClusters: Array<{ id: string; errorType: string; status: string }>;
  analysisType: 'root_cause' | 'pattern' | 'comparison' | 'trend';
}

function logAnalyzerError(message: string, error: unknown): void {
  process.stderr.write(
    JSON.stringify({
      level: 'error',
      module: 'ai-diagnostics.llm-analyzer',
      message,
      error: error instanceof Error ? error.message : String(error),
    }) + '\n'
  );
}

const ROOT_CAUSE_ANALYSIS_PROMPT_TEMPLATE = `You are an expert software engineer diagnosing production errors. Analyze this error cluster and provide root cause insights.

## Error Cluster
- Error Type: {errorType}
- Message Pattern: {messagePattern}
- Stack Signature: {stackSignature}
- Occurrences: {occurrenceCount}
- Affected Users: {uniqueUsers}
- First Seen: {firstSeenAt}
- Last Seen: {lastSeenAt}

## Context Summary
- Total Occurrences: {totalOccurrences}
- Affected Users Count: {affectedUsersCount}
- Time Range: {timeRange}
- Most Common Screens: {commonScreens}
- Most Common Actions: {commonActions}

## Sample Stack Traces
{sampleStackTraces}

## Related Resolved Clusters
{relatedClusters}

## API Failure Pattern (if any)
{apiFailurePattern}

## Feature Flag Correlations
{featureFlagCorrelations}

Analyze this error and provide:
1. Root cause category (config, dependency, logic, resource, external, or unknown)
2. Specific hypothesis explaining WHY this error occurs
3. Your reasoning process
4. Evidence confidence level (high/medium/low)
5. Suggested investigation steps (3-5 specific actions)
6. Potential fix direction (if identifiable)

Respond in this JSON format:
{
  "rootCauseCategory": "logic",
  "hypothesis": "detailed explanation...",
  "reasoning": "why you think this...",
  "confidence": "high",
  "suggestedInvestigation": ["step 1", "step 2", "step 3"],
  "potentialFixDirection": "suggested fix approach...",
  "evidence": [
    {
      "type": "stack_trace|telemetry_pattern|device_correlation|api_failure|similar_issue|config_mismatch|version_regression",
      "description": "specific evidence...",
      "strength": "strong|moderate|weak"
    }
  ]
}`;

const PATTERN_SUMMARY_PROMPT_TEMPLATE = `Summarize the pattern for this error cluster in 2-3 sentences. Focus on:
1. What triggers the error (user action, system state, timing)
2. The common characteristics across all occurrences
3. Why this error clusters together (same root cause)

Error: {errorType}
Message: {messagePattern}
Occurrences: {occurrenceCount}
Context: {contextSummary}

Provide a concise, engineer-friendly summary.`;

const COMPARATIVE_ANALYSIS_PROMPT_TEMPLATE = `Compare these two error clusters and identify:
1. Key differences in root cause
2. Whether they might be related variants
3. Which cluster is more urgent to fix

Cluster A:
- Type: {typeA}
- Message: {messageA}
- Occurrences: {occurrencesA}
- Status: {statusA}

Cluster B:
- Type: {typeB}
- Message: {messageB}
- Occurrences: {occurrencesB}
- Status: {statusB}

Respond in JSON format with comparison analysis.`;

// ============================================================================
// LLM Client
// ============================================================================

interface LLMResponse {
  rootCauseCategory: RootCauseCategory;
  hypothesis: string;
  reasoning: string;
  confidence: 'high' | 'medium' | 'low';
  suggestedInvestigation: string[];
  potentialFixDirection?: string;
  evidence: Array<{
    type: string;
    description: string;
    strength: string;
  }>;
}

interface LLMCallResult {
  response: LLMResponse;
  modelUsed: string;
  promptTokens: number;
  completionTokens: number;
}

/**
 * Calls Azure OpenAI GPT-4o for analysis
 */
async function callLLM(
  prompt: string,
  options: {
    model?: 'gpt-4o' | 'gpt-4o-mini';
    temperature?: number;
    maxTokens?: number;
  } = {}
): Promise<LLMCallResult> {
  const model = options.model || 'gpt-4o-mini';
  const apiKey = config.AZURE_OPENAI_KEY;
  const endpoint = config.AZURE_OPENAI_ENDPOINT;

  if (!apiKey || !endpoint) {
    throw new Error('Azure OpenAI credentials not configured');
  }

  const url = `${endpoint}/openai/deployments/${model}/chat/completions?api-version=2024-02-01`;

  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'api-key': apiKey,
      },
      body: JSON.stringify({
        model,
        messages: [
          {
            role: 'system',
            content:
              'You are an expert software engineer specializing in debugging production systems. Provide structured, evidence-based analysis.',
          },
          { role: 'user', content: prompt },
        ],
        temperature: options.temperature ?? 0.3,
        max_tokens: options.maxTokens ?? 2000,
        response_format: { type: 'json_object' },
      }),
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Azure OpenAI API error: ${response.status} ${error}`);
    }

    const data = (await response.json()) as {
      choices: Array<{
        message: { content: string };
      }>;
      usage: {
        prompt_tokens: number;
        completion_tokens: number;
      };
      model: string;
    };

    const content = data.choices[0]?.message?.content;
    if (!content) {
      throw new Error('Empty response from LLM');
    }

    const parsed = JSON.parse(content) as LLMResponse;

    return {
      response: parsed,
      modelUsed: data.model || model,
      promptTokens: data.usage?.prompt_tokens || 0,
      completionTokens: data.usage?.completion_tokens || 0,
    };
  } catch (error) {
    logAnalyzerError('LLM call failed', error);
    throw error;
  }
}

// ============================================================================
// Analysis Functions
// ============================================================================

/**
 * Performs root cause analysis on an error cluster
 */
export async function analyzeRootCause(
  params: RootCauseAnalysisPrompt
): Promise<Partial<DiagnosticInsightDoc>> {
  const { cluster, context, sampleStackTraces, relatedClusters, analysisType } = params;

  // Build prompt
  const prompt = ROOT_CAUSE_ANALYSIS_PROMPT_TEMPLATE.replace('{errorType}', cluster.errorType)
    .replace('{messagePattern}', cluster.messageTemplate)
    .replace('{stackSignature}', cluster.stackSignature.slice(0, 500))
    .replace('{occurrenceCount}', cluster.occurrenceCount.toString())
    .replace('{uniqueUsers}', cluster.uniqueUsers.toString())
    .replace('{firstSeenAt}', cluster.firstSeenAt)
    .replace('{lastSeenAt}', cluster.lastSeenAt)
    .replace('{totalOccurrences}', context.totalOccurrences.toString())
    .replace('{affectedUsersCount}', context.affectedUsers.length.toString())
    .replace('{timeRange}', `${context.timeRange.start} to ${context.timeRange.end}`)
    .replace(
      '{commonScreens}',
      context.mostCommonScreens.map(s => `${s.screen} (${s.count})`).join(', ')
    )
    .replace(
      '{commonActions}',
      context.mostCommonActions.map(a => `${a.action} (${a.count})`).join(', ')
    )
    .replace('{sampleStackTraces}', sampleStackTraces.join('\n\n'))
    .replace(
      '{relatedClusters}',
      relatedClusters
        .filter(c => c.status === 'resolved')
        .map(c => `- ${c.errorType} (${c.id})`)
        .join('\n') || 'None found'
    )
    .replace(
      '{apiFailurePattern}',
      context.apiFailurePattern
        ? `${context.apiFailurePattern.endpoint}: ${context.apiFailurePattern.errorPattern} (${context.apiFailurePattern.occurrenceCount} occurrences)`
        : 'No clear API failure pattern'
    )
    .replace(
      '{featureFlagCorrelations}',
      context.featureFlagCorrelations
        .map(f => `- ${f.flag}: ${f.errorCorrelation.toFixed(2)} correlation`)
        .join('\n') || 'No strong correlations found'
    );

  // Call LLM
  const llmResult = await callLLM(prompt, {
    model: 'gpt-4o-mini',
    temperature: 0.3,
    maxTokens: 2000,
  });

  // Map evidence types
  const evidence: Evidence[] = llmResult.response.evidence.map(e => ({
    type: validateEvidenceType(e.type),
    description: e.description,
    strength: validateEvidenceStrength(e.strength),
    data: {},
  }));

  // Calculate confidence score
  const confidenceScore = calculateConfidenceScore(
    llmResult.response.confidence,
    evidence,
    context
  );

  return {
    analysisType,
    rootCauseCategory: llmResult.response.rootCauseCategory,
    hypothesis: llmResult.response.hypothesis,
    reasoning: llmResult.response.reasoning,
    confidence: llmResult.response.confidence,
    confidenceScore,
    evidence,
    suggestedInvestigation: llmResult.response.suggestedInvestigation,
    potentialFixDirection: llmResult.response.potentialFixDirection,
    modelUsed: llmResult.modelUsed,
    promptTokens: llmResult.promptTokens,
    completionTokens: llmResult.completionTokens,
    feedbackStats: { helpful: 0, notHelpful: 0, engineerNotes: [] },
    generatedAt: new Date().toISOString(),
  };
}

/**
 * Generates a pattern summary for a cluster
 */
export async function generatePatternSummary(
  cluster: ErrorClusterDoc,
  context: ClusterContextSummary
): Promise<string> {
  const prompt = PATTERN_SUMMARY_PROMPT_TEMPLATE.replace('{errorType}', cluster.errorType)
    .replace('{messagePattern}', cluster.messageTemplate)
    .replace('{occurrenceCount}', cluster.occurrenceCount.toString())
    .replace(
      '{contextSummary}',
      `Screens: ${context.mostCommonScreens.map(s => s.screen).join(', ')}
Actions: ${context.mostCommonActions.map(a => a.action).join(', ')}`
    );

  try {
    const result = await callLLM(prompt, {
      model: 'gpt-4o-mini',
      temperature: 0.4,
      maxTokens: 300,
    });

    return result.response.hypothesis || 'Pattern analysis pending.';
  } catch {
    return 'Pattern analysis failed. Please review manually.';
  }
}

/**
 * Compares two clusters
 */
export async function compareClusters(
  clusterA: ErrorClusterDoc,
  clusterB: ErrorClusterDoc,
  contextA: ClusterContextSummary,
  contextB: ClusterContextSummary
): Promise<{
  differences: string[];
  related: boolean;
  urgencyComparison: string;
}> {
  const prompt = COMPARATIVE_ANALYSIS_PROMPT_TEMPLATE.replace('{typeA}', clusterA.errorType)
    .replace('{messageA}', clusterA.messageTemplate)
    .replace('{occurrencesA}', clusterA.occurrenceCount.toString())
    .replace('{statusA}', clusterA.status)
    .replace('{typeB}', clusterB.errorType)
    .replace('{messageB}', clusterB.messageTemplate)
    .replace('{occurrencesB}', clusterB.occurrenceCount.toString())
    .replace('{statusB}', clusterB.status);

  try {
    const result = await callLLM(prompt, {
      model: 'gpt-4o-mini',
      temperature: 0.3,
      maxTokens: 800,
    });

    return {
      differences: result.response.suggestedInvestigation || [],
      related: result.response.confidence === 'high',
      urgencyComparison: result.response.potentialFixDirection || 'Compare occurrence counts',
    };
  } catch {
    return {
      differences: [],
      related: false,
      urgencyComparison: 'Unable to compare',
    };
  }
}

// ============================================================================
// Helper Functions
// ============================================================================

function validateEvidenceType(type: string): Evidence['type'] {
  const validTypes: Evidence['type'][] = [
    'stack_trace',
    'telemetry_pattern',
    'device_correlation',
    'api_failure',
    'similar_issue',
    'config_mismatch',
    'version_regression',
  ];
  return validTypes.includes(type as Evidence['type']) ? (type as Evidence['type']) : 'stack_trace';
}

function validateEvidenceStrength(strength: string): 'strong' | 'moderate' | 'weak' {
  const validStrengths = ['strong', 'moderate', 'weak'];
  return validStrengths.includes(strength)
    ? (strength as 'strong' | 'moderate' | 'weak')
    : 'moderate';
}

/**
 * Calculates a numeric confidence score based on multiple factors
 */
function calculateConfidenceScore(
  llmConfidence: string,
  evidence: Evidence[],
  context: ClusterContextSummary
): number {
  let score = 0;

  // Base score from LLM confidence
  switch (llmConfidence) {
    case 'high':
      score += 0.6;
      break;
    case 'medium':
      score += 0.4;
      break;
    case 'low':
      score += 0.2;
      break;
  }

  // Evidence strength bonus
  const strongEvidence = evidence.filter(e => e.strength === 'strong').length;
  const moderateEvidence = evidence.filter(e => e.strength === 'moderate').length;
  score += strongEvidence * 0.1 + moderateEvidence * 0.05;

  // Data volume bonus (more data = higher confidence)
  if (context.totalOccurrences >= 100) score += 0.1;
  else if (context.totalOccurrences >= 10) score += 0.05;

  // Affected users bonus
  if (context.affectedUsers.length >= 50) score += 0.1;
  else if (context.affectedUsers.length >= 10) score += 0.05;

  return Math.min(1.0, score);
}

// ============================================================================
// Retry Logic
// ============================================================================

interface RetryOptions {
  maxRetries?: number;
  baseDelayMs?: number;
  maxDelayMs?: number;
}

export async function callLLMWithRetry<T>(
  fn: () => Promise<T>,
  options: RetryOptions = {}
): Promise<T> {
  const { maxRetries = 3, baseDelayMs = 1000, maxDelayMs = 10000 } = options;

  let lastError: Error | undefined;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error instanceof Error ? error : new Error(String(error));

      // Don't retry on authentication errors
      if (lastError.message.includes('401') || lastError.message.includes('403')) {
        throw lastError;
      }

      if (attempt < maxRetries - 1) {
        // Exponential backoff with jitter
        const delay = Math.min(
          baseDelayMs * Math.pow(2, attempt) + Math.random() * 1000,
          maxDelayMs
        );
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }

  throw lastError;
}
