import { describe, expect, it } from "vitest";
import { applyScannerLearningRules, countScannerLearningRules, deriveScannerLearningRules, deriveScannerLearningRulesFromReviewSamples, shouldFlagArtifactForReview, shouldSaveReviewSample } from "./scannerLearning";
import type { ParsedArtifactCandidate } from "./artifactOcrParser";
import type { CaptureResult } from "../types/global";

function capture(text: string): CaptureResult {
  return {
    id: "test",
    name: "test",
    width: 1920,
    height: 1080,
    dataUrl: "",
    capturedAt: new Date(0).toISOString(),
    ocr: [{ id: "artifact-substats", label: "Substats", text, confidence: 72 }],
  };
}

describe("scannerLearning", () => {
  it("applies deterministic OCR text replacements before parsing", () => {
    const learned = applyScannerLearningRules(capture("CIT DMG+7.0%\nEnergv Recharge+6.5%"));

    expect(learned?.ocr?.[0]?.text).toContain("CRIT DMG+7.0%");
    expect(learned?.ocr?.[0]?.text).toContain("Energy Recharge+6.5%");
  });

  it("marks low confidence or noted parses for review", () => {
    expect(shouldSaveReviewSample({ confidence: 96, notes: [], fields: { name: { confidence: 95 } } })).toBe(false);
    expect(shouldSaveReviewSample({ confidence: 96, notes: ["Artifact name was fuzzy-matched"], fields: { name: { confidence: 95 } } })).toBe(false);
    expect(shouldSaveReviewSample({ confidence: 96, notes: [], fields: { name: { confidence: 62 } } })).toBe(true);
    expect(shouldSaveReviewSample({ confidence: 90, notes: ["Main stat not confidently parsed."], fields: { mainStat: { confidence: 0 } } })).toBe(true);
  });

  it("derives conservative replacements from OCR review samples", () => {
    const reviewCapture: CaptureResult = {
      id: "review",
      name: "review",
      width: 1920,
      height: 1080,
      dataUrl: "",
      capturedAt: new Date(0).toISOString(),
      ocr: [
        { id: "artifact-main-stat", label: "Main", text: "Elemental Masterv\n187", confidence: 62 },
        { id: "artifact-set-effects", label: "Set", text: "Aubade of Morningstar and Moor", confidence: 68 },
      ],
    };

    const learned = deriveScannerLearningRules(reviewCapture, parsedArtifact({
      mainStat: "Elemental Mastery",
      setName: "Aubade of Morningstar and Moon",
    }));

    expect(learned?.textReplacements?.["Elemental Masterv"]).toBe("Elemental Mastery");
    expect(learned?.textReplacements?.Moor).toBe("Moon");
  });

  it("counts learned rules", () => {
    expect(countScannerLearningRules({ textReplacements: { one: "1", two: "2" } })).toBe(2);
  });

  it("does not flag DB review when only non-critical fields are weak", () => {
    expect(shouldFlagArtifactForReview(parsedArtifact({
      confidence: 92,
      notes: [],
      substats: ["CRIT DMG+13.2%", "HP%+15.7%", "DEF%+12.4%", "Energy Recharge+5.2%"],
      fields: {
        ...parsedArtifact().fields,
        mainStat: { value: "HP", confidence: 100, source: "derived" },
        mainValue: { value: "4,780", confidence: 100, source: "derived" },
        name: { value: "Moonlit Offering's Opulent Dream", confidence: 88, source: "fallback" },
        setName: { value: "Aubade of Morningstar and Moon", confidence: 88, source: "fallback" },
        equipped: { value: "Not detected", confidence: 45, source: "missing" },
        substats: { value: "CRIT DMG+13.2%, HP%+15.7%, DEF%+12.4%, Energy Recharge+5.2%", confidence: 82, source: "ocr" },
      },
    }))).toBe(false);
  });

  it("still flags DB review when critical parsing is incomplete", () => {
    expect(shouldFlagArtifactForReview(parsedArtifact({
      confidence: 82,
      notes: ["Main stat not confidently parsed."],
      mainStat: "Unknown main stat",
      fields: {
        ...parsedArtifact().fields,
        mainStat: { value: "", confidence: 0, source: "missing" },
      },
    }))).toBe(true);
  });

  it("can derive startup learning rules from persisted review samples", () => {
    const rules = deriveScannerLearningRulesFromReviewSamples([
      {
        savedAt: new Date(0).toISOString(),
        sample: {
          capture: {
            name: "sample",
            width: 1920,
            height: 1080,
            ocr: [{ id: "artifact-set-effects", label: "Set", text: "Aubade of Morningstar and Moor", confidence: 60 }],
          },
          parsed: parsedArtifact({
            setName: "Aubade of Morningstar and Moon",
          }),
        },
      },
    ]);

    expect(rules.textReplacements?.Moor).toBe("Moon");
  });
});

function parsedArtifact(overrides: Partial<ParsedArtifactCandidate> = {}): ParsedArtifactCandidate {
  return {
    name: "Sample Artifact",
    slot: "Sands of Eon",
    level: 20,
    mainStat: "ATK%",
    mainValue: "46.6%",
    substats: ["CRIT Rate+3.9%"],
    setName: "Aubade of Morningstar and Moon",
    equipped: "Citlali",
    confidence: 84,
    notes: ["Main stat not confidently parsed."],
    fields: {
      name: { value: "Sample Artifact", confidence: 82, source: "fallback" },
      slot: { value: "Sands of Eon", confidence: 96, source: "ocr" },
      level: { value: "20", confidence: 96, source: "ocr" },
      mainStat: { value: "ATK%", confidence: 44, source: "fallback" },
      mainValue: { value: "46.6%", confidence: 94, source: "ocr" },
      setName: { value: "Aubade of Morningstar and Moon", confidence: 92, source: "derived" },
      equipped: { value: "Citlali", confidence: 78, source: "fallback" },
      substats: { value: "CRIT Rate+3.9%", confidence: 76, source: "ocr" },
    },
    ...overrides,
  };
}
