import { useCallback, useEffect, useMemo, useRef } from "react";
import type { Dispatch, MutableRefObject, SetStateAction } from "react";
import { runAutoReviewScan as runAutoReviewScanAction, runVisibleGridScan as runVisibleGridScanAction, type VisibleGridScanOptions } from "./scanViewScanActions";
import {
  initializeLearningState,
  loadReviewQueue as loadReviewQueueFromRepo,
  persistParsedArtifact as persistParsedArtifactHelper,
  persistParsedArtifactsBatch as persistParsedArtifactsBatchHelper,
  saveReviewSample as saveReviewSampleHelper,
} from "./scanViewReviewHelpers";
import { createReviewContext, createScanActionContext } from "./scanViewControllerService";
import { useScanCommandListener } from "./useScanCommandListener";
import type { ParsedArtifactCandidate } from "../../../lib/artifactOcrParser";
import type { ScannerLearningRules } from "../../../lib/scannerLearning";
import type { BooleanResult, CaptureOptions, CaptureResult, ClickResult, NativeScannerPreflightStatus, NativeScannerRunStatus, ReviewSampleRecord } from "../../../types/global";
import type {
  ArtifactRepositoryPort,
  ReviewSampleRepositoryPort,
  LearningRepositoryPort,
  RuntimeRepositoryPort,
  AutomationRepositoryPort,
} from "../../../infrastructure/repositories/rendererBridgeRepositoryTypes";
import { clampScanLimit, emptyAutoScanStats, resolveScanTargetCount, type AutoScanStats, type ScanSummary } from "../../../lib/scannerSession";
import type { RuntimeInfo } from "../../../types/global";
import type { StoredScanResultEntry } from "../../../types/storage";
import type { createScanDiagnosticEvent } from "../../../lib/scanDiagnosticsLog";
import { validateAutoScanEntryPreflight } from "../../../lib/autoScanEntry";
import { wait } from "../../../lib/scanReviewUtils";

type BooleanSetter = Dispatch<SetStateAction<boolean>>;
type NumberSetter = Dispatch<SetStateAction<number>>;
type StringSetter = Dispatch<SetStateAction<string>>;
type NumberOrNullSetter = Dispatch<SetStateAction<number | null>>;
type ScannerRulesSetter = Dispatch<SetStateAction<ScannerLearningRules>>;
type ReviewSamplesSetter = Dispatch<SetStateAction<ReviewSampleRecord[]>>;
type ScanSummarySetter = Dispatch<SetStateAction<ScanSummary | null>>;
type AutoScanStatsSetter = Dispatch<SetStateAction<AutoScanStats>>;
type NativeScanResultsSetter = Dispatch<SetStateAction<StoredScanResultEntry[]>>;

function nativeScanSummaryStatus(status: NativeScannerRunStatus["status"]): ScanSummary["status"] {
  if (status === "stopped") return "stopped";
  if (status === "blocked") return "blocked";
  return "done";
}

function nativeScannerStats(status: NativeScannerRunStatus): AutoScanStats {
  const captured = Math.max(0, Math.round(status.captured ?? 0));
  const activeMs = Math.max(0, Math.round(status.activeMs ?? 0));
  const averageMsPerCaptured = captured > 0 ? Math.round(activeMs / captured) : 0;
  return {
    ...emptyAutoScanStats,
    clicked: Math.max(0, Math.round(status.clicked ?? captured)),
    attempted: captured,
    verified: captured,
    pages: Math.max(0, Math.round(status.pages ?? 0)),
    elapsedMs: activeMs,
    activeScanMs: activeMs,
    captureMs: activeMs,
    captureRoundTripMs: activeMs,
    averageCaptureMs: averageMsPerCaptured,
    averageCaptureRoundTripMs: averageMsPerCaptured,
    captureP50Ms: averageMsPerCaptured,
    captureP90Ms: averageMsPerCaptured,
    cardReadyMs: activeMs,
    cardReadyCount: captured,
    averageCardReadyMs: averageMsPerCaptured,
    artifactsPerMinute: activeMs > 0 && captured > 0 ? Math.round((captured * 60000 / activeMs) * 10) / 10 : 0,
    activeArtifactsPerMinute: activeMs > 0 && captured > 0 ? Math.round((captured * 60000 / activeMs) * 10) / 10 : 0,
    projectedMsFor100: averageMsPerCaptured > 0 ? averageMsPerCaptured * 100 : 0,
    activeProjectedMsFor100: averageMsPerCaptured > 0 ? averageMsPerCaptured * 100 : 0,
  };
}

function nativePreflightBlockReason(preflight: NativeScannerPreflightStatus) {
  if (preflight.blockReason) {
    return preflight.blockReason;
  }
  if (preflight.categoryReady === false) {
    return `Native IK-Erfassung fuer Kategorie '${preflight.category ?? "unbekannt"}' ist noch nicht implementiert.`;
  }
  if (!preflight.data.valid) {
    return `IK inventorylists fehlen oder sind unvollstaendig: ${preflight.data.missing.join(", ") || "unbekannt"}.`;
  }
  if (!preflight.genshinFound) {
    return "Genshin-Fenster wurde nicht gefunden.";
  }
  if (!preflight.isSixteenNine) {
    const size = preflight.bounds ? `${preflight.bounds.width}x${preflight.bounds.height}` : "unbekannt";
    return `Genshin-Layout ${size} ist nicht 16:9. Native IK-Erfassung ist aktuell auf 16:9 begrenzt.`;
  }
  if (preflight.grid.count < 32) {
    return `Native Grid-Erkennung ist unvollstaendig (${preflight.grid.count}/32 Ziele).`;
  }
  return "Native IK-Erfassung ist noch nicht bereit.";
}

interface ScanViewActionInput {
  autoScanRunning: boolean;
  setAutoScanRunning: BooleanSetter;
  isScanning: boolean;
  stopVisibleScanRef: MutableRefObject<boolean>;
  selectedSourceId: string;
  bridgeReady: boolean;
  automationRepo?: AutomationRepositoryPort;
  runtimeInfo?: RuntimeInfo | null;
  runtimeRepo?: RuntimeRepositoryPort;
  scanLimit: number;
  skipRows: number;
  detectedInventoryCount: number;
  setScanSummary: ScanSummarySetter;
  setAutoScanStats: AutoScanStatsSetter;
  setReviewStatus: StringSetter;
  appendAutomationLog: (line: string) => void;
  appendClickDiagnostics: (result: ClickResult, prefix?: string) => void;
  appendDiagnosticEvent: (event: Omit<Parameters<typeof createScanDiagnosticEvent>[0], "includeFullScreenshot">) => void;
  parseArtifact: (capture: CaptureResult | null) => ParsedArtifactCandidate | null;
  artifactRepo?: ArtifactRepositoryPort;
  reviewSamplesRepo?: ReviewSampleRepositoryPort;
  learningRepo?: LearningRepositoryPort;
  onStoredArtifactsChanged?: (() => Promise<void>) | (() => void);
  setReviewSampleTotal: NumberSetter;
  setReviewSamples: ReviewSamplesSetter;
  setLearningRulesLoaded: BooleanSetter;
  setScannerLearningRules: ScannerRulesSetter;
  setStoredTotal: NumberOrNullSetter;
  setNativeScanResults: NativeScanResultsSetter;
  scannerLearningRules: ScannerLearningRules;
  captureSelectedSource: (delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult | null>;
  latestCapture: CaptureResult | null;
  parsedArtifact: ParsedArtifactCandidate | null;
  canCaptureSource: boolean;
  setReviewQueueOpen: BooleanSetter;
}

export interface ScanViewActionResult {
  requestScanStop: (reason?: string) => void;
  saveReviewSample: (
    capture?: CaptureResult | null,
    parsed?: ParsedArtifactCandidate | null,
    reason?: string,
  ) => Promise<BooleanResult | null>;
  loadReviewQueue: () => Promise<void>;
  openReviewQueue: () => Promise<void>;
  runAutoReviewScan: () => Promise<void>;
  runGuidedAutoScan: (options?: { scanLimit?: number }) => Promise<void>;
  runVisibleGridScan: (options?: VisibleGridScanOptions) => Promise<void>;
}

export function useScanViewActions(input: ScanViewActionInput): ScanViewActionResult {
  const {
    autoScanRunning,
    setAutoScanRunning,
    isScanning,
    stopVisibleScanRef,
    selectedSourceId,
    bridgeReady,
    automationRepo,
    runtimeInfo,
    runtimeRepo,
    scanLimit,
    skipRows,
    detectedInventoryCount,
    setScanSummary,
    setAutoScanStats,
    setReviewStatus,
    appendAutomationLog,
    appendClickDiagnostics,
    appendDiagnosticEvent,
    parseArtifact,
    artifactRepo,
    reviewSamplesRepo,
    learningRepo,
    onStoredArtifactsChanged,
    setReviewSampleTotal,
    setReviewSamples,
    setLearningRulesLoaded,
    setScannerLearningRules,
    setStoredTotal,
    setNativeScanResults,
    scannerLearningRules,
    captureSelectedSource,
    latestCapture,
    parsedArtifact,
    canCaptureSource,
    setReviewQueueOpen,
  } = input;
  const learningInitializedRef = useRef(false);

  const requestScanStop = useCallback((reason = "Stop angefordert.") => {
    stopVisibleScanRef.current = true;
    void automationRepo?.nativeScannerStop?.()
      .then((status) => {
        if (status.message) appendAutomationLog(`native stop: ${status.message}`);
      })
      .catch(() => {
        // Best-effort stop; the existing renderer loop also watches the ref.
      });
    appendAutomationLog(`stop requested: ${reason}`);
    setReviewStatus(`${reason} Der aktuelle Klick/Capture-Schritt wird noch sauber beendet.`);
  }, [appendAutomationLog, automationRepo, setReviewStatus, stopVisibleScanRef]);

  const reviewContext = useMemo(
    () =>
      createReviewContext({
        artifactRepo,
        reviewSamplesRepo,
        learningRepo,
        onStoredArtifactsChanged,
        setReviewSampleTotal,
        setReviewSamples,
        setLearningRulesLoaded,
        setScannerLearningRules,
        setReviewStatus,
        setStoredTotal,
        appendAutomationLog,
      }),
    [
      artifactRepo,
      reviewSamplesRepo,
      learningRepo,
      onStoredArtifactsChanged,
      setReviewSampleTotal,
      setReviewSamples,
      setLearningRulesLoaded,
      setScannerLearningRules,
      setReviewStatus,
      setStoredTotal,
      appendAutomationLog,
    ],
  );

  useEffect(() => {
    if (learningInitializedRef.current) return;
    if (!learningRepo && !reviewSamplesRepo && !artifactRepo) return;
    learningInitializedRef.current = true;
    void initializeLearningState(reviewContext);
  }, [artifactRepo, learningRepo, reviewContext, reviewSamplesRepo]);

  const focusDashboard = useCallback(async () => {
    try {
      await automationRepo?.focusMainWindow?.();
    } catch {
      // Best-effort fallback; state remains in renderer.
    }
  }, [automationRepo?.focusMainWindow]);

  const parseArtifactAndPersist = useCallback(
    async function parseArtifactAndPersist(
      capture: CaptureResult | null,
      parsed: ParsedArtifactCandidate,
      source: string,
      needsReview: boolean,
    ) {
      return persistParsedArtifactHelper(capture, parsed, source, needsReview, reviewContext);
    },
    [reviewContext],
  );

  const parseArtifactsAndPersistBatch = useCallback(
    async function parseArtifactsAndPersistBatch(
      items: Array<{ capture: CaptureResult | null; parsed: ParsedArtifactCandidate; source: string; needsReview: boolean }>,
    ) {
      return persistParsedArtifactsBatchHelper(items, reviewContext);
    },
    [reviewContext],
  );

  const handleSaveReviewSample = useCallback(
    async function handleSaveReviewSample(
      capture: CaptureResult | null = latestCapture,
      parsed: ParsedArtifactCandidate | null = parsedArtifact,
      reason = "manual",
    ): Promise<BooleanResult | null> {
      const result = await saveReviewSampleHelper(
        capture,
        parsed,
        reason,
        scannerLearningRules,
        reviewContext,
      );
      return result.result ? { ok: result.result.ok } : null;
    },
    [latestCapture, parsedArtifact, reviewContext, scannerLearningRules],
  );

  const scanActionContext = useMemo(
    () =>
      createScanActionContext({
        autoScanRunning,
        setAutoScanRunning,
        isScanning,
        stopVisibleScanRef,
        selectedSourceId,
        bridgeReady,
        automationRepo,
        runtimeInfo,
        runtimeRepo,
        scanLimit,
        skipRows,
        detectedInventoryCount,
        setScanSummary,
        setAutoScanStats,
        setReviewStatus,
        appendAutomationLog,
        appendClickDiagnostics,
        appendDiagnosticEvent,
        parseArtifact,
        persistParsedArtifact: parseArtifactAndPersist,
        persistParsedArtifactsBatch: parseArtifactsAndPersistBatch,
        saveReviewSample: handleSaveReviewSample,
        focusDashboard,
        captureSelectedSource: (delayMs = 0, focusGenshin = false, options) => captureSelectedSource(delayMs, focusGenshin, {
          ocrMode: "artifact",
          omitFullFrame: true,
          omitInventoryPreview: true,
          ...options,
        }),
        captureFastSelectedSource: (delayMs = 0, focusGenshin = false, options) => captureSelectedSource(delayMs, focusGenshin, {
          skipOcr: true,
          omitFullFrame: true,
          omitCrops: true,
          omitCropImages: true,
          omitLockState: true,
          ...options,
        }),
      }),
    [
      autoScanRunning,
      setAutoScanRunning,
      isScanning,
      stopVisibleScanRef,
      selectedSourceId,
      bridgeReady,
      automationRepo,
      runtimeInfo,
      runtimeRepo,
      scanLimit,
      skipRows,
      detectedInventoryCount,
      setScanSummary,
      setAutoScanStats,
      setReviewStatus,
      appendAutomationLog,
      appendClickDiagnostics,
      appendDiagnosticEvent,
      parseArtifact,
      parseArtifactAndPersist,
      parseArtifactsAndPersistBatch,
      handleSaveReviewSample,
      focusDashboard,
      captureSelectedSource,
    ],
  );

  const loadReviewQueueAction = useCallback(async () => {
    await loadReviewQueueFromRepo(reviewContext);
  }, [reviewContext]);

  const openReviewQueueModal = useCallback(async () => {
    await loadReviewQueueAction();
    setReviewQueueOpen(true);
  }, [loadReviewQueueAction, setReviewQueueOpen]);

  const runAutoReviewScan = useCallback(async () => {
    if (autoScanRunning || !selectedSourceId || !canCaptureSource) return;
    await runAutoReviewScanAction(scanActionContext);
  }, [autoScanRunning, canCaptureSource, selectedSourceId, scanActionContext]);

  const runVisibleGridScan = useCallback(async (options: VisibleGridScanOptions = {}) => {
    if (autoScanRunning || !bridgeReady || !selectedSourceId || !automationRepo?.clickScreen || !automationRepo?.scrollScreen) {
      return;
    }
    await runVisibleGridScanAction(scanActionContext, options);
  }, [
    autoScanRunning,
    bridgeReady,
    selectedSourceId,
    automationRepo?.clickScreen,
    automationRepo?.scrollScreen,
    scanActionContext,
  ]);

  const runGuidedAutoScan = useCallback(async (options: { scanLimit?: number } = {}) => {
    if (autoScanRunning || !bridgeReady || !automationRepo) {
      return;
    }
    if (automationRepo.nativeScannerPreflight && automationRepo.nativeScannerStart && automationRepo.nativeScannerStatus && automationRepo.nativeScannerStop) {
      const targetCount = resolveScanTargetCount(clampScanLimit(options.scanLimit ?? scanLimit), detectedInventoryCount);
      setAutoScanRunning(true);
      stopVisibleScanRef.current = false;
      setScanSummary(null);
      setAutoScanStats(emptyAutoScanStats);
      setNativeScanResults([]);
      setReviewStatus("Native IK-Erfassung prueft Daten, Genshin-Fenster und 16:9-Grid...");

      const preflight = await automationRepo.nativeScannerPreflight({ category: "artifacts" });
      appendAutomationLog(
        `native preflight: category=${preflight.category ?? "artifacts"} data=${preflight.data.valid ? "ok" : "missing"} version=${preflight.data.version || "?"} genshin=${preflight.genshinFound ? "yes" : "no"} grid=${preflight.grid.count}/32 visual=${preflight.visual?.ready === false ? "blocked" : "ok"}`,
      );
      appendDiagnosticEvent({
        phase: "native-preflight",
        severity: preflight.ready ? "ok" : "error",
        message: preflight.ready ? "Native IK preflight passed." : nativePreflightBlockReason(preflight),
        details: {
          dataValid: preflight.data.valid,
          version: preflight.data.version,
          artifactSets: preflight.data.artifactSets,
          artifactPieces: preflight.data.artifactPieces,
          artifactEntries: preflight.data.artifactSets + preflight.data.artifactPieces,
          category: preflight.category,
          categoryReady: preflight.categoryReady,
          genshinFound: preflight.genshinFound,
          width: preflight.bounds?.width,
          height: preflight.bounds?.height,
          isSixteenNine: preflight.isSixteenNine,
          gridTargets: preflight.grid.count,
          visualReady: preflight.visual?.ready,
          visualWhitePct: preflight.visual?.whitePct,
          visualDarkPct: preflight.visual?.darkPct,
          visualColorPct: preflight.visual?.colorPct,
          visualLumaStdDev: preflight.visual?.lumaStdDev,
        },
      });

      if (!preflight.ready) {
        const reason = nativePreflightBlockReason(preflight);
        setAutoScanRunning(false);
        setReviewStatus(reason);
        setScanSummary({
          mode: "Native IK-Erfassung",
          status: "blocked",
          ...emptyAutoScanStats,
          targetCount,
          gridLabel: reason,
        });
        return;
      }

      let status = await automationRepo.nativeScannerStart({ limit: targetCount, category: "artifacts" });
      const applyNativeStatus = (nextStatus: NativeScannerRunStatus) => {
        const stats = nativeScannerStats(nextStatus);
        setAutoScanStats(stats);
        setReviewStatus(
          `Native IK-Erfassung: ${nextStatus.captured}/${nextStatus.target || targetCount} Karten-Crops erfasst, ${nextStatus.pages || 1} Seite(n). ${nextStatus.message}`,
        );
        return stats;
      };

      applyNativeStatus(status);
      while (status.running && !stopVisibleScanRef.current) {
        await wait(350);
        status = await automationRepo.nativeScannerStatus();
        applyNativeStatus(status);
      }
      if (status.running && stopVisibleScanRef.current) {
        status = await automationRepo.nativeScannerStop();
        applyNativeStatus(status);
      }

      setAutoScanRunning(false);
      await focusDashboard();
      const finalStats = nativeScannerStats(status);
      setAutoScanStats(finalStats);
      const processing = status.runDir
        ? await automationRepo.nativeScannerProcessRun?.({ runDir: status.runDir, persist: false }).catch(() => null)
        : null;
      const loadedResults = processing?.ok && status.runDir
        ? await automationRepo.nativeScannerLoadResults?.({ runDir: status.runDir, limit: 24 }).catch(() => null)
        : null;
      if (loadedResults?.ok) {
        setNativeScanResults(loadedResults.results);
      }
      setScanSummary({
        mode: "Native IK-Erfassung",
        status: nativeScanSummaryStatus(status.status),
        ...finalStats,
        targetCount: status.target || targetCount,
        gridLabel: processing?.ok
          ? `${status.message} Post-Processing: ${processing.parsed}/${processing.processed} geparst, ${processing.review} Review, Results: ${loadedResults?.results.length ?? 0}/${loadedResults?.total ?? 0}`
          : `${status.message} OCR, Parsing und Evaluierung sind fuer Geschwindigkeit in diesem Schritt bewusst ausgelassen. Output: ${status.runDir || status.outputRoot || "native-scans"}`,
      });
      setReviewStatus(
        processing?.ok
          ? `Native IK-Erfassung ${nativeScanSummaryStatus(status.status)}. ${status.captured}/${status.target || targetCount} Crops; Post-Processing ${processing.parsed}/${processing.processed} geparst, ${processing.review} Review.`
          : `Native IK-Erfassung ${nativeScanSummaryStatus(status.status)}. ${status.captured}/${status.target || targetCount} Karten-Crops erfasst; Queue liegt unter ${status.runDir || "native-scans"}.`,
      );
      appendAutomationLog(`native scan finished: status=${status.status} captured=${status.captured} activeMs=${status.activeMs}`);
      if (processing?.ok) {
        appendAutomationLog(`native process finished: parsed=${processing.parsed}/${processing.processed} review=${processing.review} report=${processing.reportPath}`);
      }
      if (loadedResults?.ok) {
        appendAutomationLog(`native results loaded: ${loadedResults.results.length}/${loadedResults.total} entries`);
      }
      return;
    }

    if (!selectedSourceId || !automationRepo.clickScreen || !automationRepo.scrollScreen) {
      return;
    }
    setReviewStatus("Auto-Scan prueft den Startzustand ohne OCR...");
    const preflightCapture = await captureSelectedSource(0, true, {
      skipOcr: true,
      omitFullFrame: true,
      omitCrops: true,
      omitCropImages: true,
      omitLockState: true,
    });
    const visibleInventoryReady = validateAutoScanEntryPreflight(preflightCapture).ok;
    appendDiagnosticEvent({
      phase: "guided-start",
      severity: visibleInventoryReady ? "ok" : "info",
      message: visibleInventoryReady
        ? "Artifact inventory detail view already visible; starting scan directly."
        : "Artifact detail view is not ready; guided scan waits for a visible artifact detail card instead of navigating.",
      capture: preflightCapture,
    });
    if (!visibleInventoryReady) {
      setReviewStatus("Auto-Scan wartet: Bitte Artifact-Inventar mit sichtbarer Detailkarte oeffnen und erneut starten.");
      return;
    }
    await runVisibleGridScanAction(scanActionContext, {
      scanLimit: options.scanLimit,
      scanEntryMode: "visible-inventory",
      processInitialSelection: true,
    });
  }, [
    autoScanRunning,
    bridgeReady,
    selectedSourceId,
    automationRepo?.clickScreen,
    automationRepo?.scrollScreen,
    automationRepo?.nativeScannerPreflight,
    automationRepo?.nativeScannerStart,
    automationRepo?.nativeScannerStatus,
    automationRepo?.nativeScannerStop,
    automationRepo?.nativeScannerProcessRun,
    automationRepo?.nativeScannerLoadResults,
    setAutoScanRunning,
    setAutoScanStats,
    setScanSummary,
    setReviewStatus,
    setNativeScanResults,
    scanLimit,
    detectedInventoryCount,
    stopVisibleScanRef,
    appendAutomationLog,
    captureSelectedSource,
    appendDiagnosticEvent,
    focusDashboard,
    scanActionContext,
  ]);

  useScanCommandListener({
    automationRepo,
    autoScanRunning,
    isScanning,
    selectedSourceId,
    requestScanStop,
    runGuidedAutoScan,
    runVisibleGridScan,
  });

  return {
    requestScanStop,
    saveReviewSample: handleSaveReviewSample,
    loadReviewQueue: loadReviewQueueAction,
    openReviewQueue: openReviewQueueModal,
    runAutoReviewScan,
    runGuidedAutoScan,
    runVisibleGridScan,
  };
}
