import { automationBlockReason, requiresAdminForAutomation } from "../../../lib/automationPlanner";
import { validateAutoScanEntryPreflight, type ScanEntryMode } from "../../../lib/autoScanEntry";
import { captureRejectionReason } from "../../../lib/scannerCaptureQuality";
import { runAutoScanLoop } from "../../../lib/autoScanLoop";
import { addCaptureTiming, clampScanLimit, emptyAutoScanStats, resolveScanTargetCount, updateScanTiming, type AutoScanStats, type ScanSummary } from "../../../lib/scannerSession";
import { getAutoReviewReason, wait } from "../../../lib/scanReviewUtils";
import { type createScanDiagnosticEvent } from "../../../lib/scanDiagnosticsLog";
import type { AutomationRepositoryPort, RuntimeRepositoryPort } from "../../../infrastructure/repositories/rendererBridgeRepositoryTypes";
import type {
  AutomationGuard,
  BooleanResult,
  CaptureOptions,
  CaptureResult,
  ClickResult,
  FocusGenshinResult,
  RuntimeInfo,
  ScrollResult,
} from "../../../types/global";
import type { ParsedArtifactCandidate } from "../../../lib/artifactOcrParser";
import type { Dispatch, MutableRefObject, SetStateAction } from "react";
import { prepareAutoScanEntry } from "./scanViewEntryActions";

export interface ScanActionContext {
  autoScanRunning: boolean;
  setAutoScanRunning: Dispatch<SetStateAction<boolean>>;
  isScanning: boolean;
  stopVisibleScanRef: MutableRefObject<boolean>;
  selectedSourceId: string;
  bridgeReady: boolean;
  automationRepo?: AutomationRepositoryPort;
  runtimeRepo?: RuntimeRepositoryPort;
  runtimeInfo?: RuntimeInfo | null;
  scanLimit: number;
  skipRows: number;
  detectedInventoryCount: number;
  setScanSummary: Dispatch<SetStateAction<ScanSummary | null>>;
  setAutoScanStats: Dispatch<SetStateAction<AutoScanStats>>;
  setReviewStatus: Dispatch<SetStateAction<string>>;
  appendAutomationLog: (line: string) => void;
  appendClickDiagnostics: (result: ClickResult, prefix?: string) => void;
  appendDiagnosticEvent: (event: Omit<Parameters<typeof createScanDiagnosticEvent>[0], "includeFullScreenshot">) => void;
  captureSelectedSource: (delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult | null>;
  captureFastSelectedSource: (delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult | null>;
  parseArtifact: (capture: CaptureResult | null) => ParsedArtifactCandidate | null;
  persistParsedArtifact: (capture: CaptureResult | null, parsed: ParsedArtifactCandidate, source: string, needsReview: boolean) => Promise<boolean>;
  persistParsedArtifactsBatch?: (items: Array<{ capture: CaptureResult | null; parsed: ParsedArtifactCandidate; source: string; needsReview: boolean }>) => Promise<number>;
  saveReviewSample: (capture: CaptureResult | null, parsed: ParsedArtifactCandidate | null, reason?: string) => Promise<BooleanResult | null>;
  shouldFlagArtifactForReview: (parsed: ParsedArtifactCandidate | null) => boolean;
  focusDashboard: () => Promise<void>;
}

export interface VisibleGridScanOptions {
  scanLimit?: number;
  scanEntryMode?: ScanEntryMode;
  processInitialSelection?: boolean;
}

function buildScanSignature(parsed: ParsedArtifactCandidate) {
  return `${parsed.name}-${parsed.slot}-${parsed.mainStat}-${parsed.mainValue}-${parsed.level ?? 0}`;
}

export async function runAutoReviewScan(context: ScanActionContext): Promise<void> {
  const {
    autoScanRunning,
    selectedSourceId,
    stopVisibleScanRef,
    setAutoScanRunning,
    setReviewStatus,
    setScanSummary,
    setAutoScanStats,
    appendAutomationLog,
    scanLimit,
    detectedInventoryCount,
    captureSelectedSource,
    parseArtifact,
    persistParsedArtifact,
    persistParsedArtifactsBatch,
    saveReviewSample,
    shouldFlagArtifactForReview,
    focusDashboard,
  } = context;

  if (autoScanRunning || !selectedSourceId) return;

  setAutoScanRunning(true);
  stopVisibleScanRef.current = false;
  setScanSummary(null);
  setAutoScanStats(emptyAutoScanStats);
  setReviewStatus("Manueller Scan laeuft. Klicke in Genshin auf ein anderes Artifact; nur neue Artifacts werden verarbeitet.");

  const seen = new Set<string>();
  const stats: AutoScanStats = { ...emptyAutoScanStats, pages: 1 };
  const startedAt = Date.now();
  const updateManualStats = () => {
    updateScanTiming(stats, startedAt);
    setAutoScanStats({ ...stats });
  };
  let idleTicks = 0;
  const maxArtifacts = resolveScanTargetCount(scanLimit, detectedInventoryCount);
  const maxIdleTicks = 90;

  while (!stopVisibleScanRef.current && stats.parsed < maxArtifacts && idleTicks < maxIdleTicks) {
    const capture = await captureSelectedSource(0, true);
    const rejection = captureRejectionReason(capture, parseArtifact(capture));
    const parsed = parseArtifact(capture);

    if (!capture || !parsed || rejection) {
      if (capture && rejection) {
        await saveReviewSample(capture, parsed, `manual:capture-rejected`);
        stats.review++;
        updateManualStats();
      }
      idleTicks++;
      setReviewStatus(`Manueller Scan wartet auf ein lesbares Artifact... (${stats.parsed}/${maxArtifacts})${rejection ? ` ${rejection}` : ""}`);
      await wait(700);
      continue;
    }

    const signature = buildScanSignature(parsed);
    if (seen.has(signature)) {
      idleTicks++;
      setReviewStatus(`Manueller Scan wartet auf ein neues Artifact... (${stats.parsed}/${maxArtifacts})`);
      await wait(700);
      continue;
    }

    seen.add(signature);
    idleTicks = 0;
    stats.attempted++;
    stats.verified++;
    stats.parsed++;
    addCaptureTiming(stats, capture.timings, capture.elapsedMs);

    const reason = getAutoReviewReason(capture, parsed);
    const needsReview = shouldFlagArtifactForReview(parsed);
    if (reason) {
      await saveReviewSample(capture, parsed, `manual:${reason}`);
      stats.review++;
    }
    if (await persistParsedArtifact(capture, parsed, "manual-scan", needsReview)) {
      stats.stored++;
    }
    updateManualStats();
    setReviewStatus(`Manueller Scan: neues Artifact erkannt (${stats.parsed}/${maxArtifacts}). Klicke das naechste Artifact an oder druecke Stop.`);
    await wait(700);
  }

  setAutoScanRunning(false);
  updateScanTiming(stats, startedAt);
  const status: ScanSummary["status"] = stopVisibleScanRef.current ? "stopped" : "done";
  const idleSuffix = idleTicks >= maxIdleTicks ? " Keine neuen Artifacts erkannt; manueller Scan beendet." : "";
  await focusDashboard();
  setReviewStatus(
    `Manueller Scan ${status}. ${stats.verified} neue Ansichten verifiziert, ${stats.parsed} Artifacts gelesen, ${stats.stored} in der Datenbank gespeichert, ${stats.review} Review-Samples.${idleSuffix}`,
  );
  setScanSummary({
    mode: "Manueller Scan",
    status,
    ...stats,
    targetCount: maxArtifacts,
    gridLabel: "Nur neue, vom User angeklickte Artifacts wurden verarbeitet.",
  });

  appendAutomationLog(`manual scan finished: ${stats.parsed} parsed, ${stats.stored} stored, ${stats.review} review`);
}

export async function runVisibleGridScan(context: ScanActionContext, options: VisibleGridScanOptions = {}): Promise<void> {
  const {
    autoScanRunning,
    bridgeReady,
    selectedSourceId,
    runtimeInfo,
    automationRepo,
    runtimeRepo,
    stopVisibleScanRef,
    setAutoScanRunning,
    setScanSummary,
    setAutoScanStats,
    setReviewStatus,
    appendAutomationLog,
    appendClickDiagnostics,
    appendDiagnosticEvent,
    captureSelectedSource,
    captureFastSelectedSource,
    parseArtifact,
    persistParsedArtifact,
    persistParsedArtifactsBatch,
    saveReviewSample,
    shouldFlagArtifactForReview,
    scanLimit: configuredScanLimit,
    skipRows,
    detectedInventoryCount,
    focusDashboard,
  } = context;
  const scanLimit = typeof options.scanLimit === "number" ? clampScanLimit(options.scanLimit) : configuredScanLimit;
  const scanEntryMode = options.scanEntryMode ?? "visible-inventory";

  if (autoScanRunning || !bridgeReady || !selectedSourceId || !automationRepo?.focusGenshinForScanStart || !automationRepo?.focusGenshin || !automationRepo?.clickScreen || !automationRepo?.scrollScreen) return;
  if (scanEntryMode !== "visible-inventory" && !automationRepo.keyPress) {
    setReviewStatus("Auto-Scan-Einstieg ist nicht verfuegbar: Keypress-Bridge fehlt.");
    return;
  }

  const requiresAdminForAutoScan = requiresAdminForAutomation(runtimeInfo);
  if (requiresAdminForAutoScan) {
    setReviewStatus(
      "App laeuft nicht als Administrator. Bitte die App schliessen und als Administrator neu starten - Auto-Scan braucht Administrator-Rechte, damit Windows die simulierten Eingaben an Genshin nicht blockiert.",
    );
    setScanSummary({
      mode: "Automatischer Scan",
      status: "blocked",
      ...emptyAutoScanStats,
      targetCount: resolveScanTargetCount(scanLimit, detectedInventoryCount),
      gridLabel: "App laeuft nicht als Administrator. Neustart als Administrator noetig.",
    });
    return;
  }

  setAutoScanRunning(true);
  stopVisibleScanRef.current = false;
  setScanSummary(null);
  setAutoScanStats(emptyAutoScanStats);
  setReviewStatus("Automatischer Scan startet. ESC gedrueckt halten oder Stop klicken beendet den Scan sofort.");
  appendDiagnosticEvent({
    phase: "scan-start",
    severity: "info",
    message: `Auto-scan start requested (${scanEntryMode})`,
    details: { scanLimit, skipRows, detectedInventoryCount },
  });

  const freshRuntime = await runtimeRepo?.getRuntimeInfo().catch(() => null);
  const adminBlockReason = automationBlockReason(freshRuntime);
  if (adminBlockReason) {
    setAutoScanRunning(false);
    setReviewStatus(adminBlockReason);
    appendAutomationLog("blocked: App laeuft nicht als Administrator, keine In-Game-Klicks ausgefuehrt");
    appendDiagnosticEvent({
      phase: "preflight",
      severity: "error",
      message: adminBlockReason,
      details: { elevated: freshRuntime?.isElevated, genshinFound: freshRuntime?.genshinFound },
    });
    setScanSummary({
      mode: "Automatischer Scan",
      status: "blocked",
      ...emptyAutoScanStats,
      targetCount: resolveScanTargetCount(scanLimit, detectedInventoryCount),
      gridLabel: adminBlockReason,
    });
    return;
  }

  if (freshRuntime) {
    const required = freshRuntime.genshinFound ? `found:${freshRuntime.targetProcess || "genshin"}` : "not-found";
    appendAutomationLog(`runtime ping: elevated=${freshRuntime.isElevated} ${required}`);
    appendDiagnosticEvent({
      phase: "runtime",
      severity: freshRuntime.genshinFound ? "ok" : "warn",
      message: `Runtime ping: ${required}`,
      details: {
        elevated: freshRuntime.isElevated,
        foreground: freshRuntime.foregroundProcess,
        target: freshRuntime.targetProcess,
        helperPid: freshRuntime.helperPid,
      },
    });
  }

  const focusGenshinForScanStart = automationRepo.focusGenshinForScanStart ?? automationRepo.focusGenshin;
  setReviewStatus("Genshin wird in den Vordergrund geholt...");
  let focusResult: FocusGenshinResult | null = null;
  for (let attempt = 1; attempt <= 3; attempt += 1) {
    const current = await focusGenshinForScanStart().catch(() => null);
    if (!current) {
      appendAutomationLog(`focus attempt ${attempt}/3: exception`);
    } else {
      focusResult = current;
      appendAutomationLog(
        `focus attempt ${attempt}/3: ${current.focused ? "ok" : "failed"} found:${current.genshinFound ? "yes" : "no"} setForeground:${current.setForegroundResult ?? "n/a"} target:${current.targetProcess || "?"} fg:${current.foregroundProcess || "?"}`,
      );
      appendDiagnosticEvent({
        phase: "focus",
        severity: current.focused ? "ok" : "warn",
        message: `Focus attempt ${attempt}/3 ${current.focused ? "succeeded" : "failed"}`,
        details: {
          found: current.genshinFound,
          setForeground: current.setForegroundResult,
          target: current.targetProcess,
          foreground: current.foregroundProcess,
          alreadyForeground: current.alreadyForeground,
        },
      });
      if (current.focused) break;
      if (!current.genshinFound) break;
    }
    if (attempt < 3) await wait(300);
  }

  if (!focusResult?.focused) {
    setAutoScanRunning(false);
    const reason = !focusResult?.genshinFound
      ? "Genshin-Prozess wurde nicht gefunden. Bitte pruefen, ob Genshin laeuft, und Auto-Scan erneut starten."
      : "Genshin konnte nicht in den Vordergrund geholt werden. Bitte Genshin manuell anklicken/fokussieren und Auto-Scan erneut starten.";
    setReviewStatus(reason);
    appendDiagnosticEvent({
      phase: "focus",
      severity: "error",
      message: reason,
      details: {
        found: focusResult?.genshinFound,
        target: focusResult?.targetProcess,
        foreground: focusResult?.foregroundProcess,
      },
    });
    setScanSummary({
      mode: "Automatischer Scan",
      status: "blocked",
      ...emptyAutoScanStats,
      targetCount: resolveScanTargetCount(scanLimit, detectedInventoryCount),
      gridLabel: reason,
    });
    return;
  }

  setReviewStatus(scanEntryMode !== "visible-inventory"
    ? "Genshin ist im Vordergrund. Oeffne Artifact-Inventar und warte auf Detailkarte..."
    : "Genshin ist im Vordergrund. Automatischer Scan startet. ESC gedrueckt halten oder Stop klicken beendet den Scan sofort.");

  const entryCapture = await prepareAutoScanEntry({
    mode: scanEntryMode,
    automationRepo,
    captureFastSelectedSource,
    appendAutomationLog,
    appendDiagnosticEvent,
  });
  const entryPreflight = validateAutoScanEntryPreflight(entryCapture);
  if (!entryPreflight.ok) {
    setAutoScanRunning(false);
    setReviewStatus(entryPreflight.reason);
    appendAutomationLog(`entry blocked: ${entryPreflight.reason}`);
    appendDiagnosticEvent({
      phase: "entry-preflight",
      severity: "error",
      message: entryPreflight.reason,
      capture: entryCapture,
    });
    setScanSummary({
      mode: scanEntryMode === "visible-inventory" ? "Automatischer Scan" : `Automatischer Scan (${scanEntryMode})`,
      status: "blocked",
      ...emptyAutoScanStats,
      targetCount: resolveScanTargetCount(scanLimit, detectedInventoryCount),
      gridLabel: entryPreflight.reason,
    });
    await focusDashboard();
    return;
  }

  setReviewStatus("Artifact-Inventar ist bereit. Automatischer Scan startet. ESC gedrueckt halten oder Stop klicken beendet den Scan sofort.");
  appendDiagnosticEvent({
    phase: "entry-preflight",
    severity: "ok",
    message: "Artifact inventory preflight passed.",
    capture: entryCapture,
  });

  const result = await runAutoScanLoop(
    {
        api: {
          clickScreen: (x: number, y: number) => {
          if (!automationRepo?.clickScreen) {
            return Promise.resolve<ClickResult>({
              ok: false,
              x: 0,
              y: 0,
              clicked: false,
              moved: false,
              focused: false,
              inputBlocked: false,
            });
          }
          return automationRepo.clickScreen(x, y);
        },
        scrollScreen: (notches: number, anchorX?: number, anchorY?: number) => {
          if (!automationRepo?.scrollScreen) {
            return Promise.resolve<ScrollResult>({ ok: false, notchesSent: 0, inputBlocked: false });
          }
          return automationRepo.scrollScreen(notches, anchorX, anchorY);
        },
        getAutomationGuard: () =>
          automationRepo?.getAutomationGuard?.() ??
          Promise.resolve<AutomationGuard>({ ok: false, escapePressed: false, enterPressed: false, f9Pressed: false }),
      },
      captureSelectedSource: (delayMs = 0, focusGenshin = false, options) => captureSelectedSource(delayMs, focusGenshin, options),
      captureFastSelectedSource,
      parseArtifact,
      persistParsedArtifact,
      persistParsedArtifactsBatch,
      saveReviewSample,
      getAutoReviewReason,
      shouldFlagArtifactForReview,
      appendAutomationLog,
      appendClickDiagnostics,
      setReviewStatus,
      setAutoScanStats,
      shouldStop: () => stopVisibleScanRef.current,
    },
    {
      scanLimit,
      skipRows,
      detectedInventoryCount,
      processInitialSelection: options.processInitialSelection ?? scanEntryMode !== "visible-inventory",
      skipInitialGridTarget: scanEntryMode !== "visible-inventory",
    },
  );

  setAutoScanRunning(false);
  if (result.blockedReason) appendAutomationLog(`stop: ${result.blockedReason}`);
  await focusDashboard();
  setReviewStatus(`Automatischer Scan ${result.status === "stopped" ? "gestoppt" : result.status === "blocked" ? "blockiert" : "fertig"}. ${result.stats.clicked} Klicks, ${result.stats.attempted} Positionen bearbeitet, ${result.stats.verified} Ansichten verifiziert, ${result.stats.parsed} gelesen, ${result.stats.stored} in der Datenbank, ${result.stats.review} Review-Samples, ${result.stats.duplicates} Duplikate, ${result.stats.misses} Misses.${result.blockedReason ? ` ${result.blockedReason}` : ""}`);
  setScanSummary({
    mode: scanEntryMode === "visible-inventory" ? "Automatischer Scan" : `Automatischer Scan (${scanEntryMode})`,
    status: result.status,
    ...result.stats,
    targetCount: result.targetCount,
    gridLabel: result.gridLabel,
  });
}
