import type { ReviewSampleRecord } from "../types/global";
import type { OcrEvalCase } from "./ocrEvalHarness";

// Turns saved review samples into eval cases so the review queue becomes a
// growing labeled corpus (see docs/ocr-eval.md).
//
// IMPORTANT: a review sample's `parsed` block is the *auto*-parse that was
// flagged for review - it is a label CANDIDATE, not verified ground truth.
// Using it directly as an expectation would be circular (the parser grading
// itself). The intended flow is:
//   1. reviewSampleToEvalCase() extracts the OCR + the parser's current guess.
//   2. A human confirms or corrects `expect` in the produced case.
//   3. The corrected case is committed into src/eval/corpus/.
// The `confirmed` flag records whether step 2 happened.

export interface ReviewSampleEvalCase extends OcrEvalCase {
  /** False until a human has verified/corrected the `expect` values. */
  confirmed: boolean;
}

function ocrMapFromRecord(record: ReviewSampleRecord): Record<string, string> | null {
  const entries = record.sample?.capture?.ocr;
  if (!entries?.length) return null;
  const map: Record<string, string> = {};
  for (const entry of entries) {
    if (typeof entry?.id === "string" && typeof entry?.text === "string") {
      map[entry.id] = entry.text;
    }
  }
  return Object.keys(map).length > 0 ? map : null;
}

function expectFromParsed(parsed: unknown): OcrEvalCase["expect"] {
  if (!parsed || typeof parsed !== "object") return {};
  const candidate = parsed as Record<string, unknown>;
  const expect: OcrEvalCase["expect"] = {};

  const stringField = (key: "name" | "slot" | "mainStat" | "mainValue" | "setName" | "equipped") => {
    const value = candidate[key];
    // Skip the parser's own "Unknown ..." / "Not detected" / "?" sentinels -
    // those are non-answers, not labels a human would confirm.
    if (typeof value === "string" && value && !value.startsWith("Unknown") && value !== "Not detected" && value !== "?") {
      expect[key] = value;
    }
  };

  stringField("name");
  stringField("slot");
  stringField("mainStat");
  stringField("mainValue");
  stringField("setName");
  stringField("equipped");

  if (typeof candidate.level === "number" && Number.isFinite(candidate.level) && candidate.level > 0) {
    expect.level = candidate.level;
  }
  if (Array.isArray(candidate.substats) && candidate.substats.every((entry) => typeof entry === "string")) {
    expect.substats = candidate.substats as string[];
  }

  return expect;
}

export function reviewSampleToEvalCase(record: ReviewSampleRecord, index = 0): ReviewSampleEvalCase | null {
  const ocr = ocrMapFromRecord(record);
  if (!ocr) return null;

  const savedAt = record.savedAt ?? "unknown";
  const idSuffix = savedAt.replace(/[^0-9A-Za-z]/g, "").slice(0, 14) || String(index);
  const capture = record.sample?.capture;

  return {
    id: `review-${idSuffix}-${index}`,
    ocr,
    expect: expectFromParsed(record.sample?.parsed),
    confirmed: false,
    meta: {
      source: "review-sample",
      resolution: capture?.width && capture?.height ? `${capture.width}x${capture.height}` : undefined,
      note: record.sample?.reason,
    },
  };
}

export function reviewSamplesToEvalCases(records: readonly ReviewSampleRecord[] | null | undefined) {
  const cases: ReviewSampleEvalCase[] = [];
  (records ?? []).forEach((record, index) => {
    const evalCase = reviewSampleToEvalCase(record, index);
    if (evalCase) cases.push(evalCase);
  });
  return cases;
}
