/**
 * Stage 6 — Aggregate fingerprint (Haiku).
 *
 * Runs after persistence (Stage 5) on every analysis. Extracts a structured
 * fingerprint used by the cross-ticket aggregate report. Failure-tolerant:
 * the worker logs and moves on; the analysis row stays usable, just won't
 * appear in aggregate reports until re-fingerprinted via the backfill script.
 *
 * Spec: docs/ticket-analyzer-phase2-spec.md → Section D.3
 */

import {
  AggregateFingerprint,
  type DeepAnalysisResponse,
  type OpusResponse,
  type TriageResponse,
} from '@/lib/types/analyzer';
import { callLLMStage, type LLMCallResult } from '@/lib/services/llm/call';
import { HAIKU, type ModelId } from '@/lib/services/llm/models';
import type Anthropic from '@anthropic-ai/sdk';

const STAGE6_MAX_TOKENS = 4_000;

const SYSTEM_PROMPT = `You are extracting a structured fingerprint from a completed ticket analysis to enable cross-ticket aggregation. Your job is precision and consistency, not creativity.

You will receive:
- The Sonnet-tier analysis output (summary, gaps, what_was_done, etc.)
- The triage output from Stage 1 (entities, category)
- Optionally the Opus-tier updates if deep review ran

Produce a fingerprint matching this exact schema. Respond ONLY with JSON, no prose, no fences:

{
  "category": string,
  "subcategories": string[],
  "ticket_type_inferred": string,
  "root_cause_class": "configuration_drift" | "user_error" | "vendor_issue" | "hardware_failure" | "documentation_gap" | "process_gap" | "unknown" | "other",

  "client_name": string,
  "vendors_involved": string[],
  "applications_involved": string[],
  "device_classes": string[],

  "wulf_actions_taken": string[],
  "vendor_cases_opened": number,
  "resolution_path": "resolved_by_wulf" | "resolved_by_vendor" | "resolved_by_client" | "unresolved" | "self_resolved_before_wulf_action",

  "documentation_gaps_observed": [
    { "description": string, "confidence": "low" | "medium" | "high" }
  ],
  "process_gaps_observed": [
    { "description": string, "severity": "low" | "medium" | "high" }
  ],

  "similar_to_signals": string[],
  "tags": string[],

  "generated_by_model": string,
  "generated_at": string
}

Strict rules:
- Use only the listed enum values for root_cause_class and resolution_path.
- documentation_gaps_observed and process_gaps_observed should each have at most 5 entries. Quality over quantity. Each entry's description must be specific enough that two different analyses describing the same underlying gap would produce similar text.
- Tags should be lowercase, hyphenated, and stable. Prefer reusing common tags (vertafore, ams360, m365-licensing, backup-veeam, etc.) over inventing new ones.
- For similar_to_signals, write short observations like "vendor case opened before checking documentation portal" or "status not advanced after customer self-resolution" — patterns the reduce step can cluster.
- generated_by_model should be the literal string "claude-haiku-4-5".
- generated_at should be the current ISO-8601 timestamp with timezone offset.`;

export interface FingerprintInput {
  triage: TriageResponse;
  sonnet: DeepAnalysisResponse;
  opus?: OpusResponse | null;
}

export function buildFingerprintUserPayload(input: FingerprintInput): string {
  const compactSonnet = {
    summary: input.sonnet.summary,
    what_was_done: input.sonnet.what_was_done,
    what_should_have_been_done: input.sonnet.what_should_have_been_done,
    gaps: input.sonnet.gaps,
    next_step: input.sonnet.next_step,
    next_step_rationale: input.sonnet.next_step_rationale,
    post_resolution_analysis: input.sonnet.post_resolution_analysis,
    confidence_score: input.sonnet.confidence_score,
    needs_human_review: input.sonnet.needs_human_review,
    human_review_reasons: input.sonnet.human_review_reasons,
    itglue_docs_referenced: input.sonnet.itglue_docs_referenced.map((d) => ({
      name: d.name,
      doc_type: d.doc_type,
      relevance_reason: d.relevance_reason,
    })),
  };

  const parts = [
    `=== TRIAGE METADATA (Stage 1) ===`,
    JSON.stringify(input.triage, null, 2),
    ``,
    `=== ANALYSIS (Sonnet) ===`,
    JSON.stringify(compactSonnet, null, 2),
  ];

  if (input.opus) {
    parts.push(``, `=== DEEP-REVIEW UPDATES (Opus) ===`, JSON.stringify(input.opus, null, 2));
  }

  return parts.join('\n');
}

export async function runFingerprintStage(
  input: FingerprintInput,
  injectedClient?: Anthropic,
  modelOverride?: ModelId
): Promise<LLMCallResult<AggregateFingerprint>> {
  const model = modelOverride ?? HAIKU;
  const result = await callLLMStage({
    model,
    system: SYSTEM_PROMPT,
    user: buildFingerprintUserPayload(input),
    schema: AggregateFingerprint,
    maxTokens: STAGE6_MAX_TOKENS,
    client: injectedClient,
  });

  // Server-authoritative model and timestamp — model output for these is
  // advisory; we always overwrite with truth.
  return {
    ...result,
    data: {
      ...result.data,
      generated_by_model: model,
      generated_at: new Date().toISOString(),
    },
  };
}

export const _STAGE6_INTERNALS = { SYSTEM_PROMPT, STAGE6_MAX_TOKENS };
