import type { Artifact } from "../types/domain";
import {
  buildScanResultPromotionSummary,
  type ScanResultPromotionDecision,
  type ScanResultPromotionSummary,
} from "./scanResultPromotion";
import type { NativeScannerCatalogStatus, NativeScannerDataStatus } from "../types/global";
import type { ScanResultFieldConfidence, StoredArtifactRecord, StoredScanResultEntry } from "../types/storage";

export type InventoryBrowserFilter = "all" | "review" | "parsed" | "stored" | "native" | "promotable";
export type InventoryBrowserSort = "newest" | "name" | "status";
export type InventoryBrowserSource = "native" | "store" | "snapshot";

export interface InventoryBrowserRow {
  id: string;
  source: InventoryBrowserSource;
  title: string;
  setName: string;
  slot: string;
  mainStat: string;
  mainValue: string;
  levelLabel: string;
  substats: string[];
  equipped: string;
  locked?: boolean;
  reviewDecision?: "approved" | "rejected";
  confidence: number;
  needsReview: boolean;
  statusLabel: string;
  statusClassName: string;
  valueLabel: string;
  ikLabel: string;
  ikGoodLabel: string;
  promotionLabel: string;
  promotionClassName: "ok" | "warn" | "muted";
  promotionReasons: string[];
  promotionRecordId?: string;
  canPromote: boolean;
  nextActionLabel: string;
  nextActionDetail: string;
  nextActionClassName: "ok" | "warn" | "muted";
  fieldConfidences: ScanResultFieldConfidence[];
  sourceLabel: string;
  sequenceLabel: string;
  timestamp: string;
  imagePath?: string;
  runDir?: string;
  scanResultId?: string;
  notes: string[];
  error?: string;
}

export interface InventoryCatalogSummary {
  ok: boolean;
  title: string;
  detail: string;
  className: "ok" | "warn";
  counts: Array<{ label: string; value: string; sample?: string }>;
}

export interface InventoryPipelineCard {
  id: "scope" | "capture" | "processing" | "review" | "promotion" | "evidence";
  title: string;
  value: string;
  detail: string;
  className: "ok" | "warn" | "muted";
}

export interface InventoryPipelineSummary {
  cards: InventoryPipelineCard[];
  caption: string;
}

interface BuildInventoryRowsInput {
  nativeRunDir?: string;
  nativeResults?: readonly StoredScanResultEntry[];
  storedArtifacts?: readonly StoredArtifactRecord[];
  snapshotArtifacts?: readonly Artifact[];
}

export function buildInventoryRows({
  nativeRunDir = "",
  nativeResults = [],
  storedArtifacts = [],
  snapshotArtifacts = [],
}: BuildInventoryRowsInput): InventoryBrowserRow[] {
  const promotionSummary = buildScanResultPromotionSummary(nativeResults, storedArtifacts);
  const promotionByResultId = new Map(promotionSummary.decisions.map((decision) => [decision.resultId, decision]));
  const nativeRows = nativeResults.map((entry) => nativeResultToInventoryRow(entry, nativeRunDir, promotionByResultId.get(entry.id)));
  const storedRows = storedArtifacts.map(storedArtifactToInventoryRow);
  const seenStored = new Set(storedRows.map((row) => row.id));
  const snapshotRows = snapshotArtifacts
    .filter((artifact) => !seenStored.has(`store:${artifact.id}`))
    .map(snapshotArtifactToInventoryRow);
  return [...nativeRows, ...storedRows, ...snapshotRows];
}

export function filterInventoryRows(rows: readonly InventoryBrowserRow[], filter: InventoryBrowserFilter) {
  if (filter === "review") return rows.filter((row) => row.needsReview);
  if (filter === "parsed") return rows.filter((row) => !row.needsReview);
  if (filter === "stored") return rows.filter((row) => row.source === "store");
  if (filter === "native") return rows.filter((row) => row.source === "native");
  if (filter === "promotable") return rows.filter((row) => row.canPromote);
  return [...rows];
}

export function sortInventoryRows(rows: readonly InventoryBrowserRow[], sort: InventoryBrowserSort) {
  const sorted = [...rows];
  if (sort === "name") {
    return sorted.sort((left, right) => left.title.localeCompare(right.title) || right.timestamp.localeCompare(left.timestamp));
  }
  if (sort === "status") {
    return sorted.sort((left, right) => left.statusLabel.localeCompare(right.statusLabel) || right.timestamp.localeCompare(left.timestamp));
  }
  return sorted.sort((left, right) => right.timestamp.localeCompare(left.timestamp));
}

export function buildInventoryCatalogSummary(
  data: NativeScannerDataStatus | null | undefined,
  catalog?: NativeScannerCatalogStatus | null,
): InventoryCatalogSummary {
  if (!data) {
    return {
      ok: false,
      title: "IK inventorylists",
      detail: "Katalogstatus noch nicht geladen.",
      className: "warn",
      counts: [],
    };
  }

  if (!data.valid) {
    return {
      ok: false,
      title: data.version ? `IK ${data.version}` : "IK inventorylists",
      detail: `Unvollstaendig: ${data.missing.join(", ") || "unbekannte Dateien fehlen"}.`,
      className: "warn",
      counts: [],
    };
  }

  return {
    ok: true,
    title: `IK ${data.version || "?"}`,
    detail: `Artifact-Scanner nutzt IK-Artifactdaten aus ${data.source || "InventoryKamera inventorylists"}. Andere IK-Listen sind vorerst nicht im aktiven UI-Scope.`,
    className: "ok",
    counts: [
      catalogCount("Artifact-Sets", String(data.artifactSets), catalog?.artifacts.slice(0, 3).map((entry) => entry.good).filter(Boolean)),
      catalogCount("Artifact-Pieces", String(data.artifactPieces)),
      catalogCount("Aktiver Scope", "Artifacts", ["Waffen, Materialien und Charakterdetails ausgeblendet"]),
    ],
  };
}

export function buildArtifactPipelineSummary(
  data: NativeScannerDataStatus | null | undefined,
  nativeResults: readonly StoredScanResultEntry[] = [],
  promotionSummary: ScanResultPromotionSummary,
): InventoryPipelineSummary {
  const artifactResults = nativeResults.filter((entry) => entry.category === "artifact");
  const parsed = artifactResults.filter((entry) => entry.extractionStatus === "parsed" && !entry.needsReview).length;
  const review = artifactResults.filter((entry) => entry.needsReview).length;
  const errors = artifactResults.filter((entry) => entry.extractionStatus === "error" || entry.extractionStatus === "missing_crop" || entry.extractionStatus === "parse_error").length;
  const deferredValue = artifactResults.filter((entry) => entry.valueStatus === "deferred").length;
  const evidenceClassName = artifactResults.length === 0 ? "muted" : artifactResults.length < 20 ? "warn" : "ok";
  const processingClassName = artifactResults.length === 0 ? "muted" : parsed === artifactResults.length ? "ok" : "warn";
  const reviewClassName = artifactResults.length === 0 ? "muted" : review + errors === 0 ? "ok" : "warn";

  return {
    cards: [
      {
        id: "scope",
        title: "Scope",
        value: "Artifacts only",
        detail: data?.valid
          ? `${data.artifactSets}/${data.artifactPieces} IK Artifactdaten aktiv.`
          : "IK Artifactdaten noch nicht geladen.",
        className: data?.valid ? "ok" : "warn",
      },
      {
        id: "capture",
        title: "Native Capture",
        value: artifactResults.length > 0 ? `${artifactResults.length} Crops` : "Kein Run",
        detail: "C# Helper erfasst Karten-Crops; 16:9 Artifact-Inventar mit Detailkarte bleibt Pflicht.",
        className: artifactResults.length > 0 ? "ok" : "muted",
      },
      {
        id: "processing",
        title: "OCR Queue",
        value: artifactResults.length > 0 ? `${parsed}/${artifactResults.length} geparst` : "Wartet",
        detail: "OCR und Parser laufen nach der schnellen Erfassung als separater Nachgang.",
        className: processingClassName,
      },
      {
        id: "review",
        title: "Review Gate",
        value: `${review} Review`,
        detail: errors > 0 ? `${errors} blockierte oder fehlende Ergebnisse.` : "Unsichere OCR bleibt sichtbar statt als Wertung zu erscheinen.",
        className: reviewClassName,
      },
      {
        id: "promotion",
        title: "Promotion",
        value: `${promotionSummary.ready} speicherbar`,
        detail: `Store-Schreibzugriff bleibt explizit; ${promotionSummary.alreadyStored + promotionSummary.persisted} bereits im Store.`,
        className: promotionSummary.ready > 0 ? "ok" : promotionSummary.review + promotionSummary.blocked > 0 ? "warn" : "muted",
      },
      {
        id: "evidence",
        title: "Evidenz",
        value: artifactResults.length >= 20 ? "Run brauchbar" : artifactResults.length > 0 ? "Kleiner Run" : "Keine Daten",
        detail: artifactResults.length >= 20
          ? "Fuer Speed-Claims trotzdem 50/100-Item-Runs verwenden."
          : "Native-Paritaet erst nach groesseren 20/50/100-Artifact-Runs behaupten.",
        className: evidenceClassName,
      },
    ],
    caption: `${deferredValue} native Artifact-Ergebnisse haben noch keine Value-Auswertung; Extraktion und Artifact-Wert bleiben getrennt.`,
  };
}

function catalogCount(label: string, value: string, sampleValues: string[] = []) {
  const sample = sampleValues.filter(Boolean).join(", ");
  return sample ? { label, value, sample } : { label, value };
}

function nativeResultToInventoryRow(
  entry: StoredScanResultEntry,
  runDir: string,
  promotion?: ScanResultPromotionDecision,
): InventoryBrowserRow {
  const artifact = entry.artifact;
  const categoryLabel = scanResultCategoryLabel(entry.category);
  const clean = entry.extractionStatus === "parsed" && !entry.needsReview;
  const statusLabel = clean ? (entry.persistedArtifact ? "Stored" : "Geparst") : entry.extractionStatus === "missing_crop" ? "Crop fehlt" : "Review";
  const nextAction = nativeNextAction(entry, promotion);
  return {
    id: `native:${entry.id}`,
    source: "native",
    title: artifact?.name || `${categoryLabel} #${entry.sequence}`,
    setName: artifact?.setName || (entry.category === "artifact" ? "Unknown set" : "IK catalog-only"),
    slot: artifact?.slot || (entry.category === "artifact" ? "Unknown slot" : categoryLabel),
    mainStat: artifact?.mainStat || (entry.category === "artifact" ? "Unknown main stat" : "Not parsed"),
    mainValue: artifact?.mainValue || "?",
    levelLabel: typeof artifact?.level === "number" ? `+${artifact.level}` : "+?",
    substats: artifact?.substats ?? [],
    equipped: artifact?.equipped || "Not detected",
    locked: artifact?.locked,
    reviewDecision: entry.review?.status,
    confidence: entry.extractionConfidence,
    needsReview: entry.needsReview,
    statusLabel,
    statusClassName: clean ? entry.persistedArtifact ? "stored" : "parsed" : "review",
    valueLabel: entry.valueStatus === "deferred" ? "Wert offen" : "Review",
    ikLabel: entry.ikMatch ? (entry.ikMatch.matched ? "Matched" : "Review") : "Not checked",
    ikGoodLabel: entry.ikMatch?.setGood && entry.ikMatch?.artifactGood
      ? `${entry.ikMatch.setGood} / ${entry.ikMatch.artifactGood}`
      : "-",
    promotionLabel: promotion?.label ?? "Ungeprueft",
    promotionClassName: promotion?.className ?? "muted",
    promotionReasons: promotion?.reasons ?? [],
    promotionRecordId: promotion?.artifactRecordId,
    canPromote: Boolean(promotion?.canPersist),
    nextActionLabel: nextAction.label,
    nextActionDetail: nextAction.detail,
    nextActionClassName: nextAction.className,
    fieldConfidences: entry.fieldConfidences ?? [],
    sourceLabel: entry.category === "artifact" ? "Native Artifact" : `Native ${categoryLabel}`,
    sequenceLabel: `#${entry.sequence}`,
    timestamp: entry.capturedAt,
    imagePath: entry.imagePath,
    runDir,
    scanResultId: entry.id,
    notes: entry.notes,
    error: entry.error,
  };
}

function scanResultCategoryLabel(category: StoredScanResultEntry["category"]) {
  if (category === "weapon") return "Weapon";
  if (category === "character") return "Character";
  if (category === "material") return "Material";
  if (category === "unknown") return "Unknown";
  return "Artifact";
}

function storedArtifactToInventoryRow(record: StoredArtifactRecord): InventoryBrowserRow {
  const timestamp = record.lastSeenAt ?? record.firstSeenAt ?? "";
  return {
    id: `store:${record.id}`,
    source: "store",
    title: record.name,
    setName: record.setName,
    slot: record.slot,
    mainStat: record.mainStat,
    mainValue: record.mainValue,
    levelLabel: typeof record.level === "number" ? `+${record.level}` : "+?",
    substats: record.substats,
    equipped: record.equipped || "Not detected",
    locked: record.locked,
    confidence: record.confidence,
    needsReview: record.needsReview,
    statusLabel: record.needsReview ? "Review" : (record.timesSeen ?? 1) > 1 ? "Duplicate" : "Stored",
    statusClassName: record.needsReview ? "review" : (record.timesSeen ?? 1) > 1 ? "duplicate" : "stored",
    valueLabel: record.needsReview ? "Review" : "Wert offen",
    ikLabel: "Not checked",
    ikGoodLabel: "-",
    promotionLabel: "Im Store",
    promotionClassName: "muted",
    promotionReasons: ["Artifact is already in the local store."],
    promotionRecordId: record.id,
    canPromote: false,
    nextActionLabel: record.needsReview ? "Review pruefen" : "Bereits im Store",
    nextActionDetail: record.needsReview
      ? "Dieser Store-Eintrag bleibt unsicher, bis die OCR-Felder bestaetigt sind."
      : "Keine Promotion noetig; Value-Auswertung kann spaeter auf Store-Daten laufen.",
    nextActionClassName: record.needsReview ? "warn" : "muted",
    fieldConfidences: [],
    sourceLabel: "Store",
    sequenceLabel: record.timesSeen ? `${record.timesSeen}x` : "1x",
    timestamp,
    notes: [],
  };
}

function snapshotArtifactToInventoryRow(artifact: Artifact): InventoryBrowserRow {
  return {
    id: `snapshot:${artifact.id}`,
    source: "snapshot",
    title: artifact.setName,
    setName: artifact.setName,
    slot: artifact.slot,
    mainStat: artifact.mainStat,
    mainValue: "",
    levelLabel: `+${artifact.level}`,
    substats: artifact.substats.map((substat) => `${substat.key}+${substat.value}${substat.unit === "%" ? "%" : ""}`),
    equipped: artifact.equipped || "Not detected",
    confidence: artifact.confidence,
    needsReview: artifact.confidence < 80,
    statusLabel: artifact.confidence < 80 ? "Review" : "Snapshot",
    statusClassName: artifact.confidence < 80 ? "review" : "stored",
    valueLabel: "Wert offen",
    ikLabel: "Not checked",
    ikGoodLabel: "-",
    promotionLabel: "Snapshot",
    promotionClassName: "muted",
    promotionReasons: ["Snapshot rows are display-only until imported into the store."],
    canPromote: false,
    nextActionLabel: "Nur Anzeige",
    nextActionDetail: "Snapshot-Daten sind Fallback-Anzeige und kein nativer Scan-Resultat-Nachgang.",
    nextActionClassName: "muted",
    fieldConfidences: [],
    sourceLabel: "Snapshot",
    sequenceLabel: "local",
    timestamp: artifact.lastSeenAt,
    notes: [],
  };
}

function nativeNextAction(
  entry: StoredScanResultEntry,
  promotion?: ScanResultPromotionDecision,
): { label: string; detail: string; className: "ok" | "warn" | "muted" } {
  if (promotion?.canPersist) {
    return {
      label: "Bereit zur Promotion",
      detail: "Parsed, IK-gematcht und vollstaendig genug. Store-Schreibzugriff bleibt ein expliziter naechster Schritt.",
      className: "ok",
    };
  }

  if (promotion?.status === "already_stored" || promotion?.status === "persisted" || entry.persistedArtifact) {
    return {
      label: "Bereits im Store",
      detail: "Dieses Ergebnis ist Store-Evidenz und braucht keine erneute Promotion.",
      className: "muted",
    };
  }

  if (entry.needsReview || promotion?.status === "review") {
    return {
      label: "Review zuerst",
      detail: "OCR, IK-Match oder Feldconfidence ist nicht sauber genug fuer Store-Promotion.",
      className: "warn",
    };
  }

  if (promotion?.status === "blocked") {
    return {
      label: "Blockiert",
      detail: "Es fehlt eine geparste Artifact-Identitaet oder ein sicherer IK-Abgleich.",
      className: "warn",
    };
  }

  return {
    label: entry.valueStatus === "deferred" ? "Wert spaeter auswerten" : "Pruefen",
    detail: "Die Extraktion ist vom Artifact-Wert getrennt; Bewertung wird nachgelagert ergaenzt.",
    className: "muted",
  };
}
