import { detailFingerprint } from "../../../../../lib/autoScanLoop";
import { dataGeneratedAt, sourceVersion } from "../../../../../lib/genshinData";
import { dataPackageStatus } from "../../../../../lib/dataPackageStatus";
import { validateLookupPackage } from "../../../../../lib/genshinLookup";
import { useCallback, useMemo, type MouseEvent } from "react";
import type { ScanDiagnosticsModalProps } from "../types";
import type { ScanDiagnosticEvent } from "../../../../../lib/scanDiagnosticsLog";

export interface ScanDiagnosticsModelProgress {
  width: number;
  show: boolean;
}

export interface ScanDiagnosticsModalModel {
  closeDiagnostics: () => void;
  openDetails: () => void;
  handleSaveReviewSample: () => void;
  stopPropagation: (event: MouseEvent<HTMLDivElement>) => void;
  canOpenDetails: boolean;
  statusTitle: string;
  rightsClassName: string;
  rightsValue: string;
  genshinClassName: string;
  genshinValue: string;
  shouldShowAdminBanner: boolean;
  gridSourceClass: string;
  gridMainValue: string;
  gridMetaValue: string;
  learningRulesText: string;
  learningRulesSubtext: string;
  autoScanModeLabel: string;
  fingerprintText: string;
  runtimeRows: string[];
  scanLimitText: string;
  scanTipText: string;
  autoScanStatsLines: Array<{ label: string; value: number }>;
  playerProgress: ScanDiagnosticsModelProgress;
  autoScanRunning: boolean;
  showDevRows: boolean;
  reviewStatus: string;
  automationLogLines: string[];
  diagnosticEvents: ScanDiagnosticEvent[];
  canSaveReviewSample: boolean;
}

type ScanDiagnosticsController = ScanDiagnosticsModalProps["controller"];

interface UseScanDiagnosticsModalModelInput extends Pick<
  ScanDiagnosticsModalProps,
  | "setDetailsOpen"
  | "setDiagnosticsOpen"
  | "latestCapture"
  | "controller"
> {
  captureStatus: string;
  // saveReviewSample / canSaveReviewSample live on the controller, not the modal
  // props; the component wires them through from controller.* .
  saveReviewSample: ScanDiagnosticsController["saveReviewSample"];
  canSaveReviewSample: ScanDiagnosticsController["canSaveReviewSample"];
}

export function useScanDiagnosticsModalModel({
  setDetailsOpen,
  setDiagnosticsOpen,
  saveReviewSample,
  canSaveReviewSample,
  latestCapture,
  controller,
  captureStatus,
}: UseScanDiagnosticsModalModelInput): ScanDiagnosticsModalModel {
  const closeDiagnostics = useCallback(() => setDiagnosticsOpen(false), [setDiagnosticsOpen]);
  const openDetails = useCallback(() => setDetailsOpen(true), [setDetailsOpen]);
  const handleSaveReviewSample = useCallback(() => {
    if (canSaveReviewSample && controller.parsedArtifact) {
      void saveReviewSample();
    }
  }, [canSaveReviewSample, controller.parsedArtifact, saveReviewSample]);
  const stopPropagation = useCallback((event: MouseEvent<HTMLDivElement>) => {
    event.stopPropagation();
  }, []);

  const canOpenDetails = Boolean(latestCapture?.crops?.length || latestCapture?.ocr?.length);
  const shouldShowAdminBanner = !controller.runtimeInfo?.isElevated;
  const rightsClassName = controller.runtimeInfo?.isElevated ? "ok" : "blocked";
  const rightsValue = controller.runtimeInfo?.isElevated ? "Admin" : "Standard";
  const genshinClassName = controller.runtimeInfo?.genshinFound ? "ok" : "neutral";
  const genshinValue = controller.runtimeInfo?.genshinFound ? "Gefunden" : "Nicht gefunden";
  const statusTitle = controller.devMode ? "Dev-Ausgabe kompakt" : "Dev-Ausgabe erweitern";
  const autoScanModeLabel = controller.autoScanRunning ? "Scan active" : "Last scan";

  const inventoryGrid = latestCapture?.inventoryGrid;
  const gridSourceClass = inventoryGrid?.source ?? "missing";
  const gridMainValue = inventoryGrid
    ? inventoryGrid?.source === "missing"
      ? "not detected"
      : `${inventoryGrid?.cols} x ${inventoryGrid?.rows}`
    : "waiting";
  const gridMetaValue = inventoryGrid
    ? `${inventoryGrid?.confidence}% confidence - ${inventoryGrid?.centers.length} click targets`
    : "Run a capture once while the artifact inventory is visible.";

  const learningRulesText = controller.learningRulesLoaded ? `${controller.learningRuleCount} local rules` : "loading";
  const dataStaleness = dataPackageStatus(dataGeneratedAt, sourceVersion);
  const lookupStatus = validateLookupPackage();
  const lookupText = lookupStatus.valid
    ? `Lookup OK: ${lookupStatus.summary.artifactSets} sets / ${lookupStatus.summary.artifactPieces} pieces`
    : `Lookup invalid: ${lookupStatus.errors[0] ?? "unknown error"}`;
  const learningRulesSubtext = `Review samples feed deterministic OCR fixes before each new parse. ${lookupText}. Data package: ${sourceVersion}${dataStaleness.warning ? ` - ${dataStaleness.warning}` : ""}`;

  const playerProgress = useMemo(() => {
    const width = Math.min(
      100,
      controller.autoScanStats.attempted > 0
        ? Math.round((controller.autoScanStats.verified / Math.max(1, controller.autoScanStats.attempted)) * 100)
        : 0,
    );
    const show = controller.autoScanRunning || controller.autoScanStats.clicked > 0 || controller.autoScanStats.parsed > 0;
    return { width, show };
  }, [
    controller.autoScanRunning,
    controller.autoScanStats.attempted,
    controller.autoScanStats.clicked,
    controller.autoScanStats.parsed,
    controller.autoScanStats.verified,
  ]);

  const autoScanStatsLines = useMemo(
    () => [
      { label: "clicked", value: controller.autoScanStats.clicked },
      { label: "attempted", value: controller.autoScanStats.attempted },
      { label: "verified", value: controller.autoScanStats.verified },
      { label: "parsed", value: controller.autoScanStats.parsed },
      { label: "stored", value: controller.autoScanStats.stored },
      { label: "review", value: controller.autoScanStats.review },
      { label: "duplicates", value: controller.autoScanStats.duplicates },
      { label: "misses", value: controller.autoScanStats.misses },
      { label: "pages", value: controller.autoScanStats.pages },
      { label: "elapsedMs", value: controller.autoScanStats.elapsedMs },
      { label: "activeScanMs", value: controller.autoScanStats.activeScanMs },
      { label: "writeFlushMs", value: controller.autoScanStats.writeFlushMs },
      { label: "avgMs", value: controller.autoScanStats.averageMsPerParsed },
      { label: "activeAvgMs", value: controller.autoScanStats.activeAverageMsPerParsed },
      { label: "avgCaptureMs", value: controller.autoScanStats.averageCaptureMs },
      { label: "avgCaptureRoundTripMs", value: controller.autoScanStats.averageCaptureRoundTripMs },
      { label: "avgCaptureRoundTripOverheadMs", value: controller.autoScanStats.averageCaptureRoundTripOverheadMs },
      { label: "captureP50Ms", value: controller.autoScanStats.captureP50Ms },
      { label: "captureP90Ms", value: controller.autoScanStats.captureP90Ms },
      { label: "avgOcrMs", value: controller.autoScanStats.averageOcrMs },
      { label: "ocrP50Ms", value: controller.autoScanStats.ocrP50Ms },
      { label: "ocrP90Ms", value: controller.autoScanStats.ocrP90Ms },
      { label: "cardReadyAvgMs", value: controller.autoScanStats.averageCardReadyMs },
      { label: "cardReadyCount", value: controller.autoScanStats.cardReadyCount },
      { label: "scrollReadyAvgMs", value: controller.autoScanStats.averageScrollReadyMs },
      { label: "scrollReadyCount", value: controller.autoScanStats.scrollReadyCount },
      { label: "perMin x10", value: Math.round(controller.autoScanStats.artifactsPerMinute * 10) },
      { label: "activePerMin x10", value: Math.round(controller.autoScanStats.activeArtifactsPerMinute * 10) },
      { label: "projected100Ms", value: controller.autoScanStats.projectedMsFor100 },
      { label: "activeProjected100Ms", value: controller.autoScanStats.activeProjectedMsFor100 },
    ],
    [
      controller.autoScanStats.clicked,
      controller.autoScanStats.attempted,
      controller.autoScanStats.verified,
      controller.autoScanStats.parsed,
      controller.autoScanStats.stored,
      controller.autoScanStats.review,
      controller.autoScanStats.duplicates,
      controller.autoScanStats.misses,
      controller.autoScanStats.pages,
      controller.autoScanStats.elapsedMs,
      controller.autoScanStats.activeScanMs,
      controller.autoScanStats.writeFlushMs,
      controller.autoScanStats.averageMsPerParsed,
      controller.autoScanStats.activeAverageMsPerParsed,
      controller.autoScanStats.averageCaptureMs,
      controller.autoScanStats.averageCaptureRoundTripMs,
      controller.autoScanStats.averageCaptureRoundTripOverheadMs,
      controller.autoScanStats.captureP50Ms,
      controller.autoScanStats.captureP90Ms,
      controller.autoScanStats.averageOcrMs,
      controller.autoScanStats.ocrP50Ms,
      controller.autoScanStats.ocrP90Ms,
      controller.autoScanStats.averageCardReadyMs,
      controller.autoScanStats.cardReadyCount,
      controller.autoScanStats.averageScrollReadyMs,
      controller.autoScanStats.scrollReadyCount,
      controller.autoScanStats.artifactsPerMinute,
      controller.autoScanStats.activeArtifactsPerMinute,
      controller.autoScanStats.projectedMsFor100,
      controller.autoScanStats.activeProjectedMsFor100,
    ],
  );

  const runtimeRows = useMemo(() => {
    const selectedSourceLabel = controller.selectedSource
      ? `Selected: ${controller.selectedSource.name}`
      : "No source selected";
    const storedTotalLabel = controller.storedTotal !== null ? `DB: ${controller.storedTotal} Artifacts` : "DB: -";
    const runtime = controller.runtimeInfo
      ? `${controller.runtimeInfo?.isElevated ? "admin" : "standard"} - target ${controller.runtimeInfo?.genshinFound ? controller.runtimeInfo?.targetProcess || "Genshin" : "missing"} - fg ${controller.runtimeInfo?.foregroundProcess || "unknown"}`
      : "unknown";

    return [selectedSourceLabel, storedTotalLabel, runtime, captureStatus, controller.reviewStatus].filter(Boolean);
  }, [controller.selectedSource, controller.storedTotal, controller.runtimeInfo, captureStatus, controller.reviewStatus]);

  const fingerprintText = latestCapture ? detailFingerprint(latestCapture) : "--";
  const scanLimitText = controller.scanLimit ? `Scan-Limit: ${controller.scanLimit}` : "Keine Limitinfo.";
  const scanTipText = "Tip: Die Auto-Scan Logik bleibt aktivierbar, aber Dev-Ansicht ist rein informativ.";

  return {
    closeDiagnostics,
    openDetails,
    handleSaveReviewSample,
    stopPropagation,
    canOpenDetails,
    statusTitle,
    rightsClassName,
    rightsValue,
    genshinClassName,
    genshinValue,
    shouldShowAdminBanner,
    gridSourceClass,
    gridMainValue,
    gridMetaValue,
    learningRulesText,
    learningRulesSubtext,
    autoScanModeLabel,
    fingerprintText,
    runtimeRows,
    scanLimitText,
    scanTipText,
    autoScanStatsLines,
    playerProgress,
    autoScanRunning: controller.autoScanRunning,
    showDevRows: controller.devMode,
    reviewStatus: controller.reviewStatus,
    automationLogLines: controller.automationLog,
    diagnosticEvents: controller.diagnosticEvents,
    canSaveReviewSample: canSaveReviewSample && Boolean(controller.parsedArtifact),
  };
}
