import { describe, expect, it } from "vitest";
import {
  buildArtifactPipelineSummary,
  buildInventoryCatalogSummary,
  buildInventoryRows,
  filterInventoryRows,
  sortInventoryRows,
  type InventoryBrowserRow,
} from "./inventoryBrowser";
import type { NativeScannerCatalogStatus, NativeScannerDataStatus } from "../types/global";
import type { StoredArtifactRecord, StoredScanResultEntry } from "../types/storage";

function nativeEntry(overrides: Partial<StoredScanResultEntry> = {}): StoredScanResultEntry {
  return {
    id: "native-1",
    runId: "run-1",
    sequence: 3,
    category: "artifact",
    source: "native-ik-scan",
    imagePath: "artifact-0003.png",
    capturedAt: "2026-07-09T10:00:03.000Z",
    extractionStatus: "parsed",
    extractionConfidence: 93,
    needsReview: false,
    valueStatus: "deferred",
    valueScore: null,
    persistedArtifact: false,
    notes: [],
    artifact: {
      name: "Pristine Plume of the Blessed",
      slot: "Plume of Death",
      level: 20,
      setName: "Silken Moon's Serenade",
      mainStat: "ATK",
      mainValue: "311",
      substats: ["CRIT DMG+7.0%"],
      equipped: "Aino",
    },
    ...overrides,
  };
}

function storedRecord(overrides: Partial<StoredArtifactRecord> = {}): StoredArtifactRecord {
  return {
    id: "store-1",
    name: "Gladiator's Nostalgia",
    slot: "Flower of Life",
    level: 20,
    setName: "Gladiator's Finale",
    mainStat: "HP",
    mainValue: "4,780",
    substats: ["CRIT Rate+3.9%"],
    equipped: "Hu Tao",
    confidence: 91,
    needsReview: false,
    source: "auto-scan",
    firstSeenAt: "2026-07-09T09:00:00.000Z",
    lastSeenAt: "2026-07-09T09:30:00.000Z",
    timesSeen: 2,
    ...overrides,
  };
}

function validIkData(): NativeScannerDataStatus {
  return {
    directory: "C:/app/data/ik-inventorylists",
    version: "6.7.0",
    artifactSets: 61,
    artifactPieces: 289,
    weapons: 247,
    characters: 119,
    materials: 715,
    totalEntries: 1370,
    categories: {
      artifacts: {
        file: "artifacts.json",
        setCount: 61,
        pieceCount: 289,
        supported: true,
        catalogAvailable: true,
        nativeCaptureSupported: true,
        scanStatus: "native_capture",
      },
      weapons: {
        file: "weapons.json",
        count: 247,
        supported: false,
        catalogAvailable: true,
        nativeCaptureSupported: false,
        scanStatus: "catalog_only",
      },
      characters: {
        file: "characters.json",
        count: 119,
        supported: false,
        catalogAvailable: true,
        nativeCaptureSupported: false,
        scanStatus: "catalog_only",
      },
      materials: {
        file: "materials.json",
        count: 715,
        supported: false,
        catalogAvailable: true,
        nativeCaptureSupported: false,
        scanStatus: "catalog_only",
      },
    },
    missing: [],
    valid: true,
    source: "InventoryKamera inventorylists",
  };
}

describe("inventory browser rows", () => {
  it("builds rows from native scan results and stored artifacts", () => {
    const rows = buildInventoryRows({
      nativeRunDir: "C:/runs/run-1",
      nativeResults: [nativeEntry({
        ikMatch: {
          matched: true,
          confidence: 100,
          source: "ik-inventorylists",
          setGood: "SilkenMoonsSerenade",
          artifactGood: "PristinePlumeOfTheBlessed",
          notes: [],
        },
        fieldConfidences: [
          { key: "name", label: "Name", value: "Pristine Plume of the Blessed", confidence: 96, source: "database" },
          { key: "mainStat", label: "Main stat", value: "ATK", confidence: 94, source: "ocr" },
        ],
      })],
      storedArtifacts: [storedRecord()],
    });

    expect(rows).toHaveLength(2);
    expect(rows[0]).toMatchObject({
      source: "native",
      title: "Pristine Plume of the Blessed",
      statusLabel: "Geparst",
      valueLabel: "Wert offen",
      ikLabel: "Matched",
      ikGoodLabel: "SilkenMoonsSerenade / PristinePlumeOfTheBlessed",
      promotionLabel: "Speicherbar",
      promotionClassName: "ok",
      canPromote: true,
      nextActionLabel: "Bereit zur Promotion",
      fieldConfidences: [
        { key: "name", label: "Name", value: "Pristine Plume of the Blessed", confidence: 96, source: "database" },
        { key: "mainStat", label: "Main stat", value: "ATK", confidence: 94, source: "ocr" },
      ],
      sourceLabel: "Native Artifact",
      sequenceLabel: "#3",
      runDir: "C:/runs/run-1",
    });
    expect(rows[1]).toMatchObject({
      source: "store",
      title: "Gladiator's Nostalgia",
      statusLabel: "Duplicate",
      promotionLabel: "Im Store",
      sequenceLabel: "2x",
    });
  });

  it("filters review rows without treating weak value as review", () => {
    const rows = buildInventoryRows({
      nativeResults: [
        nativeEntry({
          ikMatch: {
            matched: true,
            confidence: 100,
            source: "ik-inventorylists",
            setGood: "SilkenMoonsSerenade",
            artifactGood: "PristinePlumeOfTheBlessed",
            notes: [],
          },
        }),
        nativeEntry({
          id: "native-review",
          sequence: 4,
          extractionStatus: "review",
          extractionConfidence: 55,
          needsReview: true,
          valueStatus: "review",
        }),
      ],
    });

    const reviewRows = filterInventoryRows(rows, "review");
    const parsedRows = filterInventoryRows(rows, "parsed");

    expect(reviewRows).toHaveLength(1);
    expect(reviewRows[0].statusLabel).toBe("Review");
    expect(parsedRows).toHaveLength(1);
    expect(parsedRows[0].valueLabel).toBe("Wert offen");
    expect(filterInventoryRows(rows, "native")).toHaveLength(2);
    expect(filterInventoryRows(rows, "promotable")).toHaveLength(1);
  });

  it("shows catalog-only native categories without artifact promotion", () => {
    const rows = buildInventoryRows({
      nativeResults: [
        nativeEntry({
          id: "native-weapon",
          sequence: 5,
          category: "weapon",
          artifact: undefined,
          extractionStatus: "error",
          extractionConfidence: 0,
          needsReview: true,
          valueStatus: "review",
          error: "Native post-capture processing for category 'weapons' is not implemented yet; IK catalog is available only.",
        }),
      ],
    });

    expect(rows[0]).toMatchObject({
      title: "Weapon #5",
      setName: "IK catalog-only",
      slot: "Weapon",
      mainStat: "Not parsed",
      sourceLabel: "Native Weapon",
      promotionLabel: "Blockiert",
      canPromote: false,
    });
  });

  it("sorts newest rows first by timestamp", () => {
    const rows: InventoryBrowserRow[] = [
      buildInventoryRows({ storedArtifacts: [storedRecord()] })[0],
      buildInventoryRows({ nativeResults: [nativeEntry()] })[0],
    ];

    const sorted = sortInventoryRows(rows, "newest");

    expect(sorted[0].source).toBe("native");
    expect(sorted[1].source).toBe("store");
  });
});

describe("inventory catalog summary", () => {
  const data = validIkData();
  const validIkCatalog: NativeScannerCatalogStatus = {
    data,
    artifacts: [
      {
        normalizedName: "silkenmoonsserenade",
        setName: "Silken Moon's Serenade",
        good: "SilkenMoonsSerenade",
        pieces: [],
      },
    ],
    weapons: [{ normalizedName: "favoniussword", good: "FavoniusSword" }],
    characters: [{ normalizedName: "furina", good: "Furina", element: "Hydro", weaponType: 1, constellationName: "" }],
    materials: [{ normalizedName: "mora", good: "Mora" }],
  };

  it("summarizes the vendored IK category coverage", () => {
    const summary = buildInventoryCatalogSummary(data, validIkCatalog);

    expect(summary).toMatchObject({
      ok: true,
      title: "IK 6.7.0",
      detail: "Artifact-Scanner nutzt IK-Artifactdaten aus InventoryKamera inventorylists. Andere IK-Listen sind vorerst nicht im aktiven UI-Scope.",
      className: "ok",
    });
    expect(summary.counts).toEqual([
      { label: "Artifact-Sets", value: "61", sample: "SilkenMoonsSerenade" },
      { label: "Artifact-Pieces", value: "289" },
      { label: "Aktiver Scope", value: "Artifacts", sample: "Waffen, Materialien und Charakterdetails ausgeblendet" },
    ]);
  });

  it("keeps missing IK data visibly unsafe", () => {
    const summary = buildInventoryCatalogSummary({
      ...data,
      valid: false,
      missing: ["materials.json"],
    });

    expect(summary.ok).toBe(false);
    expect(summary.className).toBe("warn");
    expect(summary.detail).toContain("materials.json");
    expect(summary.counts).toEqual([]);
  });
});

describe("artifact pipeline summary", () => {
  it("keeps native artifact evidence separate from value and promotion", () => {
    const promotionSummary = buildInventoryRows({
      nativeResults: [
        nativeEntry({
          ikMatch: {
            matched: true,
            confidence: 100,
            source: "ik-inventorylists",
            setGood: "SilkenMoonsSerenade",
            artifactGood: "PristinePlumeOfTheBlessed",
            notes: [],
          },
        }),
      ],
    });
    const summary = buildArtifactPipelineSummary(
      validIkData(),
      [nativeEntry({
        ikMatch: {
          matched: true,
          confidence: 100,
          source: "ik-inventorylists",
          setGood: "SilkenMoonsSerenade",
          artifactGood: "PristinePlumeOfTheBlessed",
          notes: [],
        },
      })],
      {
        total: 1,
        ready: promotionSummary.filter((row) => row.canPromote).length,
        alreadyStored: 0,
        persisted: 0,
        review: 0,
        blocked: 0,
        title: "Native Promotion",
        detail: "1 speicherbar - 0 im Store - 0 Review - 0 blockiert",
        className: "ok",
        decisions: [],
      },
    );

    expect(summary.cards.map((card) => card.id)).toEqual(["scope", "capture", "processing", "review", "promotion", "evidence"]);
    expect(summary.cards.find((card) => card.id === "scope")).toMatchObject({
      value: "Artifacts only",
      className: "ok",
    });
    expect(summary.cards.find((card) => card.id === "processing")).toMatchObject({
      value: "1/1 geparst",
      className: "ok",
    });
    expect(summary.cards.find((card) => card.id === "evidence")).toMatchObject({
      value: "Kleiner Run",
      className: "warn",
    });
    expect(summary.caption).toContain("keine Value-Auswertung");
  });
});
