import { describe, expect, it } from "vitest";
import { parseArtifactCandidate } from "./artifactOcrParser";
import type { CaptureResult } from "../types/global";

function captureFromOcr(textById: Record<string, string>): CaptureResult {
  return {
    id: "test",
    name: "test capture",
    width: 1920,
    height: 1080,
    dataUrl: "",
    capturedAt: new Date(0).toISOString(),
    ocr: Object.entries(textById).map(([id, text]) => ({
      id,
      label: id,
      text,
      confidence: 80,
    })),
  };
}

describe("parseArtifactCandidate", () => {
  it("matches artifact data from the generated Genshin data package", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "A Note in Spring's Lei\n| Sands of Eon Vi",
      "artifact-main-stat": "Elemental Mastery\n187",
      "artifact-substats": "+20\n+ ATK+29\n+ CRIT DMG+15.5%\n+ CRIT Rate+2.7%\nATK + 15",
      "artifact-set-effects": "A Day Carved From Rising Winds\n2-Piece Set: ATK +18%.",
      "artifact-footer": "WV Equipped: Citlali\naR 0",
    }));

    expect(parsed?.slot).toBe("Sands of Eon");
    expect(parsed?.level).toBe(20);
    expect(parsed?.mainStat).toBe("Elemental Mastery");
    expect(parsed?.setName).toBe("A Day Carved From Rising Winds");
    expect(parsed?.equipped).toBe("Citlali");
  });

  it("recognizes newer characters and artifact sets without hardcoded one-offs", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Moonlit Offering's Opulent Dr\n| Flower of Life 2",
      "artifact-main-stat": "4,780\nAhhh",
      "artifact-substats": "+ Elemental Mastery+54\n+ CRIT Rate+7.4%\n+ ATK+4.7%",
      "artifact-set-effects": "Aubade of Morningstar and Moor\n2-Piece Set: Increases Elemental Mastery by 80.",
      "artifact-footer": "? Equipped: Ineffa\naR 0",
    }));

    expect(parsed?.slot).toBe("Flower of Life");
    expect(parsed?.mainStat).toBe("HP");
    expect(parsed?.mainValue).toBe("4,780");
    expect(parsed?.setName).toBe("Aubade of Morningstar and Moon");
    expect(parsed?.equipped).toBe("Ineffa");
  });

  it("normalizes noisy slot aliases from title OCR before matching", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Viridescent Venerer's Determination\nSands of Eon Vi",
      "artifact-main-stat": "Energy Recharge\n51.8%",
      "artifact-substats": "+ ATK+5.8%\n+ Elemental Mastery+37\n+ HP+11.7%\n+ ATK+54",
      "artifact-set-effects": "Viridescent Venerer:\n2-Piece Set: Anemo DMG Bonus +15%",
      "artifact-footer": "Equipped: Sucrose",
    }));

    expect(parsed?.slot).toBe("Sands of Eon");
    expect(parsed?.mainStat).toBe("Energy Recharge");
  });

  it("uses slot rules and fuzzy set matching for plume artifacts", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Pristine Plume of the Bles\n| Plume of Death",
      "artifact-main-stat": "31 !\npr",
      "artifact-substats": "+20\n+ CRIT DMG+7.0%\n+ DEF+30.6%\n+ Elemental Mastery+40\nATK +5.8%",
      "artifact-set-effects": "Silken Moon's Serenade:\n2-Piece Set: Energy Recharge +20%.",
      "artifact-footer": "Equipped: Aino\nBR",
    }));

    expect(parsed?.slot).toBe("Plume of Death");
    expect(parsed?.mainStat).toBe("ATK");
    expect(parsed?.mainValue).toBe("311");
    expect(parsed?.setName).toBe("Silken Moon's Serenade");
    expect(parsed?.equipped).toBe("Aino");
  });

  it("keeps goblet elemental damage main stats separate from crit substats", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Deep Gallery's Bestowed Banquet\nGoblet of Eonothem",
      "artifact-main-stat": "Cryo DMG Bonus\n46.6%",
      "artifact-substats": "+ CRIT Rate+6.6%\n+ CRIT DMG+12.4%\n+ HP+269\n+ ATK+16.3%",
      "artifact-set-effects": "Finale of the Deep Galleries:\n2-Piece Set: Cryo DMG Bonus +15%",
      "artifact-footer": "Equipped: Skirk",
    }));

    expect(parsed?.slot).toBe("Goblet of Eonothem");
    expect(parsed?.mainStat).toBe("Cryo DMG Bonus");
    expect(parsed?.mainValue).toBe("46.6%");
  });

  it("does not let substats override circlet crit main stats", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Holy Crown of the Believer\nCirclet of Logos",
      "artifact-main-stat": "CRIT Rate\n31.1%",
      "artifact-substats": "+ ATK+4.7%\n+ Elemental Mastery+56\n+ DEF+37\n+ Energy Recharge+11.7%",
      "artifact-set-effects": "Silken Moon's Serenade:\n2-Piece Set: Energy Recharge +20%.",
      "artifact-footer": "Equipped: Chongyun",
    }));

    expect(parsed?.slot).toBe("Circlet of Logos");
    expect(parsed?.mainStat).toBe("CRIT Rate");
    expect(parsed?.mainValue).toBe("31.1%");
  });

  it("keeps percent substats distinct from flat ATK HP and DEF", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Beast Tamer's Talisman\nFlower of Life",
      "artifact-main-stat": "HP\n4,780",
      "artifact-substats": "+ HP+16.3%\n+ CRIT DMG+7.0%\n+ Energy Recharge+6.5%\n+ Elemental Mastery+68",
      "artifact-set-effects": "Scroll of the Hero of Cinder City:\n2-Piece Set: When a nearby party member triggers a Nightsoul Burst",
      "artifact-footer": "Equipped: Citlali",
    }));

    expect(parsed?.substats).toContain("HP%+16.3%");
    expect(parsed?.setName).toBe("Scroll of the Hero of Cinder City");
  });

  it("falls back from artifact piece name to the owning set", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Holy Crown of the Believer\nCirclet of Logos",
      "artifact-main-stat": "CRIT Rate\n31.1%",
      "artifact-substats": "+ ATK+4.7%\n+ Elemental Mastery+56\n+ DEF+37",
      "artifact-set-effects": "unreadable noisy set text",
      "artifact-footer": "Equipped: Chongyun",
    }));

    expect(parsed?.setName).toBe("Silken Moon's Serenade");
  });

  it("falls back from artifact piece name to the owning slot", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Viridescent Venerer's Determination",
      "artifact-main-stat": "Energy Recharge\n51.8%",
      "artifact-substats": "+ ATK+5.8%\n+ Elemental Mastery+37\n+ HP+11.7%\n+ ATK+54",
      "artifact-set-effects": "Viridescent Venerer:\n2-Piece Set: Anemo DMG Bonus +15%",
      "artifact-footer": "Equipped: Sucrose",
    }));

    expect(parsed?.slot).toBe("Sands of Eon");
    expect(parsed?.setName).toBe("Viridescent Venerer");
  });

  it("promotes ATK HP and DEF main stats to percent variants when the value is percent", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Hourglass of Thunder\nSands of Eon",
      "artifact-main-stat": "ATK\n40.7%",
      "artifact-substats": "+17\n+ CRIT DMG+14.8%\n+ Elemental Mastery+21\n+ ATK+53\n+ DEF+19",
      "artifact-set-effects": "Thundering Fury:\n2-Piece Set: Electro DMG Bonus +15%",
      "artifact-footer": "Equipped: Fischl",
    }));

    expect(parsed?.level).toBe(17);
    expect(parsed?.slot).toBe("Sands of Eon");
    expect(parsed?.mainStat).toBe("ATK%");
    expect(parsed?.mainValue).toBe("40.7%");
  });

  it("keeps the main value even when level helps but the stat family is still ambiguous", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Myths of the Night Realm\nSands of Eon",
      "artifact-main-stat": "30.8%",
      "artifact-substats": "+12\n+ Elemental Mastery+16\n+ CRIT DMG+6.2%\n+ DEF+42\n+ HP+9.9%",
      "artifact-set-effects": "Obsidian Codex:\n2-Piece Set: While the equipping character is in Nightsoul's Blessing",
      "artifact-footer": "Equipped: Sandrone",
    }));

    expect(parsed?.level).toBe(12);
    expect(parsed?.mainValue).toBe("30.8%");
    expect(parsed?.mainStat).toBe("Unknown main stat");
  });

  it("cleans equipped footer noise before fuzzy-matching the character", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Gladiator's Nostalgia\nFlower of Life",
      "artifact-main-stat": "HP\n4,780",
      "artifact-substats": "+ Energy Recharge+11.0%\n+ ATK+9.9%\n+ HP+14.6%\n+ CRIT DMG+12.4%",
      "artifact-set-effects": "Gladiator's Finale:\n2-Piece Set: ATK +18%",
      "artifact-footer": "1 gv 0RY If tha anuninnina\nJl Equipped: Bennett\nCEE",
    }));

    expect(parsed?.equipped).toBe("Bennett");
  });

  it("recognizes ATK percent main stats from OCR text on non-fixed slots", () => {
    const sands = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Myths of the Night Realm\nSands of Eon",
      "artifact-main-stat": "ATK\n30.8%",
      "artifact-substats": "+ Elemental Mastery+16\n+ CRIT DMG+6.2%\n+ DEF+42\n+ HP+9.9%",
      "artifact-set-effects": "Obsidian Codex:\n2-Piece Set: While the equipping character is in Nightsoul's Blessing",
      "artifact-footer": "Equipped: Sandrone",
    }));
    const circlet = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Maiden's Fading Beauty\nCirclet of Logos",
      "artifact-main-stat": "ATK\n46.6%",
      "artifact-substats": "+ ATK+29\n+ CRIT DMG+10.9%\n+ CRIT Rate+7.0%",
      "artifact-set-effects": "Maiden Beloved:\n2-Piece Set: Character Healing Effectiveness +15%",
      "artifact-footer": "Equipped: Qiqi",
    }));
    const goblet = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Viridescent Venerer's Vessel\nGoblet of Eonothem",
      "artifact-main-stat": "ATK\n46.6%",
      "artifact-substats": "+ HP+209\n+ CRIT DMG+25.6%\n+ Elemental Mastery+37",
      "artifact-set-effects": "Viridescent Venerer:\n2-Piece Set: Anemo DMG Bonus +15%",
      "artifact-footer": "Equipped: Ganyu",
    }));

    expect(sands?.mainStat).toBe("ATK%");
    expect(sands?.mainValue).toBe("30.8%");
    expect(circlet?.mainStat).toBe("ATK%");
    expect(circlet?.mainValue).toBe("46.6%");
    expect(goblet?.mainStat).toBe("ATK%");
    expect(goblet?.mainValue).toBe("46.6%");
  });
  it("normalizes garbled percent punctuation in stat values", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Pristine Circlet of the Bles\n| Circlet of Logos",
      "artifact-main-stat": "ATK\n46",
      "artifact-substats": "+ Elemental Mastery+20" + String.fromCharCode(0x00b7) + "5%\n+ CRIT DMG+6.3%\n+ ATK+19\n+ DEF+12",
      "artifact-set-effects": "Gladiator's Finale:\n2-Piece Set: ATK +18%",
      "artifact-footer": "Equipped: Aino",
    }));

    expect(parsed?.slot).toBe("Circlet of Logos");
    expect(parsed?.substats).toContain("Elemental Mastery+20.5%");
    expect(parsed?.substats).toContain("CRIT DMG+6.3%");
  });

  it("handles garbled quotation marks in copied text without failing slot and set parsing", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Aloy's " + String.fromCharCode(0x201c) + "Gift" + String.fromCharCode(0x201d) + "\n| Circlet of Logos",
      "artifact-main-stat": "Elemental Mastery\n46.6%",
      "artifact-substats": "+ ATK+4.7\n+ CRIT DMG+12.4%\n+ Energy Recharge+8.1%\n+ HP+11",
      "artifact-set-effects": "Maiden" + String.fromCharCode(0x2019) + "s Beloved:\n2-Piece Set: Energy Recharge +16%",
      "artifact-footer": "Equipped: Shenhe",
    }));

    expect(parsed?.slot).toBe("Circlet of Logos");
    expect(parsed?.mainStat).toBe("Elemental Mastery");
    expect(parsed?.setName).toBe("Maiden Beloved");
  });

  it("recovers substats that overflow into the set effects OCR crop", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Moonlit Offering's Opulent Dr\nFlower of Life",
      "artifact-main-stat": "HP\n4,780",
      "artifact-substats": "+ CRIT DMG+13.2%",
      "artifact-set-effects": "+ HP+15.7%\n+ DEF+12.4%\n+ Energy Recharge+5.2%\nAubade of Morningstar and Moon\n2-Piece Set: Increases Elemental Mastery by 80.",
      "artifact-footer": "Equipped: Venti",
    }));

    expect(parsed?.substats).toEqual([
      "CRIT DMG+13.2%",
      "HP%+15.7%",
      "DEF%+12.4%",
      "Energy Recharge+5.2%",
    ]);
    expect(parsed?.fields.substats.confidence).toBe(96);
  });

  it("keeps a percent main value even when OCR misses the main stat label", () => {
    const parsed = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Moonlit Offering's Final\nSands of Eon",
      "artifact-main-stat": "46.6%\n1S 2.5.8 J\nSe",
      "artifact-substats": "+ ATK+19\n+ Energy Recharge+6.5%\n+ CRIT DMG+18.7%",
      "artifact-set-effects": "Aubade of Morningstar and Moon\n2-Piece Set: Increases Elemental Mastery by 80.",
      "artifact-footer": "",
    }));

    expect(parsed?.mainStat).toBe("Unknown main stat");
    expect(parsed?.mainValue).toBe("46.6%");
    expect(parsed?.fields.mainValue.confidence).toBeGreaterThanOrEqual(80);
  });

  it("derives unique main stats from slot and value when the OCR label is missing", () => {
    const sands = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Revelation's Toll\nSands of Eon",
      "artifact-main-stat": "58.3%\n1S 2.5.8 J\nSe",
      "artifact-substats": "+ CRIT Rate+14.0%\n+ Elemental Mastery+33\n+ Energy Recharge+6.5%",
      "artifact-set-effects": "Night of the Sky's Unveiling:\n2-Piece Set: Increases Elemental Mastery by 80.",
      "artifact-footer": "Equipped: Zibai",
    }));
    const circlet = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Crown of the Saints\nCirclet of Logos",
      "artifact-main-stat": "62.2%",
      "artifact-substats": "+ ATK+18\n+ ATK+10.5%\n+ DEF+17.5%",
      "artifact-set-effects": "Obsidian Codex:\n2-Piece Set: While the equipping character is in Nightsoul's Blessing",
      "artifact-footer": "Equipped: Aino",
    }));

    expect(sands?.mainValue).toBe("58.3%");
    expect(sands?.mainStat).toBe("DEF%");
    expect(circlet?.mainValue).toBe("62.2%");
    expect(circlet?.mainStat).toBe("CRIT DMG");
  });

  it("recovers unique max-value mains from noisy digit fragments but keeps ambiguous 46-values conservative", () => {
    const circlet = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Moonlit Offering's Silver Crown\nCirclet of Logos",
      "artifact-main-stat": "6\n2 D",
      "artifact-substats": "+ ATK+29\n+ ATK+5.8%\n+ Elemental Mastery+77\n- DEF+5.1%",
      "artifact-set-effects": "Aubade of Morningstar and Moon\n2-Piece Set: Increases Elemental Mastery by 80.",
      "artifact-footer": "",
    }));
    const sands = parseArtifactCandidate(captureFromOcr({
      "artifact-title": "Moonlit Offering's Final Hour\nSands of Eon",
      "artifact-main-stat": "46.6% |\nPEE",
      "artifact-substats": "+ ATK+19\n+ Energy Recharge+6.5%\n+ CRIT DMG+18.7%\n- DEF+53",
      "artifact-set-effects": "Aubade of Morningstar and Moon\n2-Piece Set: Increases Elemental Mastery by 80.",
      "artifact-footer": "",
    }));

    expect(circlet?.mainValue).toBe("62.2%");
    expect(circlet?.mainStat).toBe("CRIT DMG");
    expect(sands?.mainValue).toBe("46.6%");
    expect(sands?.mainStat).toBe("Unknown main stat");
  });
});
