import type { ParsedArtifactCandidate } from "./artifactOcrParser.js";
import { fuzzyFindKnown, simplifyForMatch } from "./fuzzyMatch.js";
import type { ScanResultIkMatch } from "../types/storage.js";

export interface IkArtifactPieceCatalogEntry {
  slot: string;
  artifactName: string;
  good: string;
  normalizedName: string;
}

export interface IkArtifactSetCatalogEntry {
  normalizedName: string;
  setName: string;
  good: string;
  pieces: IkArtifactPieceCatalogEntry[];
}

export interface IkArtifactCatalog {
  artifacts: readonly IkArtifactSetCatalogEntry[];
}

const SLOT_TO_IK: Record<string, string> = {
  "Flower of Life": "flower",
  "Plume of Death": "plume",
  "Sands of Eon": "sands",
  "Goblet of Eonothem": "goblet",
  "Circlet of Logos": "circlet",
};

export function matchParsedArtifactToIk(
  parsed: ParsedArtifactCandidate | null,
  catalog: IkArtifactCatalog | null | undefined,
): ScanResultIkMatch | null {
  if (!parsed) return null;
  if (!catalog?.artifacts?.length) {
    return reviewMatch("IK artifact catalog unavailable.");
  }

  const setMatch = findSetMatch(parsed, catalog.artifacts);
  const pieceMatch = findPieceMatch(parsed, setMatch?.set ? [setMatch.set] : catalog.artifacts);
  const set = setMatch?.set ?? pieceMatch?.set;
  const piece = pieceMatch?.piece;
  const notes: string[] = [];

  if (!set) notes.push(`IK set not matched: ${parsed.setName}.`);
  if (!piece) notes.push(`IK artifact piece not matched: ${parsed.name}.`);
  if (setMatch?.set && pieceMatch?.set && setMatch.set.normalizedName !== pieceMatch.set.normalizedName) {
    notes.push(`IK set/piece mismatch: ${setMatch.set.setName} vs ${pieceMatch.set.setName}.`);
  }

  const parsedSlotKey = SLOT_TO_IK[parsed.slot] ?? "";
  if (!parsedSlotKey) notes.push(`IK slot not mapped: ${parsed.slot}.`);
  if (piece && parsedSlotKey && piece.slot !== parsedSlotKey) {
    notes.push(`IK slot mismatch: parsed ${parsed.slot}, piece is ${piece.slot}.`);
  }

  const matched = Boolean(set && piece && parsedSlotKey && piece.slot === parsedSlotKey && notes.length === 0);
  return {
    matched,
    confidence: matched
      ? Math.round(Math.min(setMatch?.confidence ?? 100, pieceMatch?.confidence ?? 100, 100))
      : Math.round(Math.min(setMatch?.confidence ?? 0, pieceMatch?.confidence ?? 0, 65)),
    source: "ik-inventorylists",
    setKey: set?.normalizedName,
    setGood: set?.good,
    setName: set?.setName,
    artifactKey: piece?.normalizedName,
    artifactGood: piece?.good,
    artifactName: piece?.artifactName,
    slotKey: piece?.slot ?? (parsedSlotKey || undefined),
    notes,
  };
}

function findSetMatch(parsed: ParsedArtifactCandidate, sets: readonly IkArtifactSetCatalogEntry[]) {
  const normalized = simplifyForMatch(parsed.setName);
  const exact = sets.find((set) =>
    set.normalizedName === normalized || simplifyForMatch(set.setName) === normalized || set.good === parsed.setName,
  );
  if (exact) return { set: exact, confidence: 100 };

  const fuzzy = fuzzyFindKnown(parsed.setName, sets.map((set) => set.setName), 0.78);
  if (!fuzzy) return null;
  const set = sets.find((entry) => entry.setName === fuzzy.value);
  return set ? { set, confidence: Math.round(fuzzy.score * 100) } : null;
}

function findPieceMatch(parsed: ParsedArtifactCandidate, sets: readonly IkArtifactSetCatalogEntry[]) {
  const normalized = simplifyForMatch(parsed.name);
  const pieces = sets.flatMap((set) => set.pieces.map((piece) => ({ set, piece })));
  const exact = pieces.find(({ piece }) =>
    piece.normalizedName === normalized || simplifyForMatch(piece.artifactName) === normalized || piece.good === parsed.name,
  );
  if (exact) return { ...exact, confidence: 100 };

  const fuzzy = fuzzyFindKnown(parsed.name, pieces.map(({ piece }) => piece.artifactName), 0.78);
  if (!fuzzy) return null;
  const match = pieces.find(({ piece }) => piece.artifactName === fuzzy.value);
  return match ? { ...match, confidence: Math.round(fuzzy.score * 100) } : null;
}

function reviewMatch(note: string): ScanResultIkMatch {
  return {
    matched: false,
    confidence: 0,
    source: "ik-inventorylists",
    notes: [note],
  };
}
