import { createHash } from "node:crypto";
import type { JsonValue, LiveOutput, QueueTask } from "./types";

const resumeIdPattern = /^[A-Za-z0-9._:-]{8,128}$/u;

export type ResumeDeliveryState = "queued_for_existing_thread" | "duplicate_suppressed" | "not_accepted" | "accepted_response_timeout" | "unknown";

export interface ResumeTraceMatch {
  seq: number;
  at: string;
  method: "turn/resume";
  resumeId: string;
  promptChars: number;
  promptHash: string;
  promptOmitted: true;
  source: "output";
}

export interface ResumeTraceConfirmation {
  taskId: string;
  resumeId: string;
  found: boolean;
  accepted: boolean;
  deliveryState: ResumeDeliveryState;
  trace: ResumeTraceMatch | null;
  matches: ResumeTraceMatch[];
  duplicateSuppressionKey: string;
}

export interface ResumeDuplicateDecision {
  duplicate: boolean;
  conflict: boolean;
  confirmation: ResumeTraceConfirmation;
  existingPromptHash: string | null;
  requestedPromptHash: string;
}

export function normalizeResumeId(value: unknown): string | null {
  if (typeof value !== "string") return null;
  const text = value.trim();
  if (!resumeIdPattern.test(text)) return null;
  return text;
}

export function resumePromptHash(prompt: string): string {
  return createHash("sha256").update(prompt, "utf8").digest("hex");
}

export function createResumeId(taskId: string, prompt: string): string {
  const hash = createHash("sha256")
    .update("unidesk-code-queue-resume:v1", "utf8")
    .update("\0", "utf8")
    .update(taskId, "utf8")
    .update("\0", "utf8")
    .update(prompt, "utf8")
    .digest("hex")
    .slice(0, 24);
  return `resume_${hash}`;
}

export function resumeTraceText(resumeId: string, prompt: string): string {
  return `\n[resume id=${resumeId} chars=${prompt.length} sha256=${resumePromptHash(prompt)}] ${prompt}\n`;
}

export function normalizeResumePromptText(text: string): string {
  return text.replace(/^\s*\[resume(?:\s+[^\]]*)?\]\s*/u, "").trimEnd();
}

function resumeTraceMeta(text: string): { promptChars: number | null; promptHash: string | null } {
  const match = text.match(/^\s*\[resume(?:\s+[^\]]*)?\]\s*/u);
  if (match === null) return { promptChars: null, promptHash: null };
  const header = match[0];
  const charsMatch = header.match(/\schars=(\d+)(?=\s|\])/u);
  const hashMatch = header.match(/\ssha256=([a-f0-9]{64})(?=\s|\])/u);
  return {
    promptChars: charsMatch === null ? null : Number(charsMatch[1]),
    promptHash: hashMatch?.[1] ?? null,
  };
}

function outputMatch(item: LiveOutput, resumeId: string): ResumeTraceMatch | null {
  if (item.channel !== "user" || item.method !== "turn/resume" || normalizeResumeId(item.itemId) !== resumeId) return null;
  const prompt = normalizeResumePromptText(item.text);
  const meta = resumeTraceMeta(item.text);
  return {
    seq: item.seq,
    at: item.at,
    method: "turn/resume",
    resumeId,
    promptChars: meta.promptChars ?? prompt.length,
    promptHash: meta.promptHash ?? resumePromptHash(prompt),
    promptOmitted: true,
    source: "output",
  };
}

export function findResumeTraceConfirmation(task: QueueTask, resumeId: string, output: LiveOutput[] = task.output): ResumeTraceConfirmation {
  const matches = output
    .map((item) => outputMatch(item, resumeId))
    .filter((item): item is ResumeTraceMatch => item !== null)
    .sort((left, right) => left.seq - right.seq);
  const trace = matches[0] ?? null;
  return {
    taskId: task.id,
    resumeId,
    found: trace !== null,
    accepted: trace !== null,
    deliveryState: trace !== null ? "queued_for_existing_thread" : "unknown",
    trace,
    matches,
    duplicateSuppressionKey: resumeId,
  };
}

export function resumeDuplicateDecision(task: QueueTask, resumeId: string, prompt: string, output: LiveOutput[] = task.output): ResumeDuplicateDecision {
  const confirmation = findResumeTraceConfirmation(task, resumeId, output);
  const requestedPromptHash = resumePromptHash(prompt);
  const existingPromptHash = confirmation.trace?.promptHash ?? null;
  return {
    duplicate: confirmation.found && existingPromptHash === requestedPromptHash,
    conflict: confirmation.found && existingPromptHash !== requestedPromptHash,
    confirmation,
    existingPromptHash,
    requestedPromptHash,
  };
}

export function resumeTraceConfirmationJson(confirmation: ResumeTraceConfirmation): JsonValue {
  return {
    taskId: confirmation.taskId,
    resumeId: confirmation.resumeId,
    found: confirmation.found,
    accepted: confirmation.accepted,
    deliveryState: confirmation.deliveryState,
    trace: confirmation.trace as unknown as JsonValue,
    matchCount: confirmation.matches.length,
    matches: confirmation.matches.slice(0, 5) as unknown as JsonValue,
    matchesTruncated: confirmation.matches.length > 5,
    duplicateSuppressionKey: confirmation.duplicateSuppressionKey,
    promptOmitted: true,
  };
}
