import { automationBlockReason, requiresAdminForAutomation } from "../../../lib/automationPlanner";
import { captureRejectionReason } from "../../../lib/scannerCaptureQuality";
import { runAutoScanLoop } from "../../../lib/autoScanLoop";
import { clampScanLimit, emptyAutoScanStats, resolveScanTargetCount, type AutoScanStats, type ScanSummary } from "../../../lib/scannerSession";
import { getAutoReviewReason, wait } from "../../../lib/scanReviewUtils";
import type { AutomationRepositoryPort, RuntimeRepositoryPort } from "../../../infrastructure/repositories/rendererBridgeRepositories";
import type { AutomationGuard, BooleanResult, CaptureOptions, CaptureResult, ClickResult, RuntimeInfo, ScrollResult } from "../../../types/global";
import type { ParsedArtifactCandidate } from "../../../lib/artifactOcrParser";
import type { MutableRefObject } from "react";
import type { Dispatch, SetStateAction } from "react";

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;
  captureSelectedSource: (delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult | null>;
  captureFastSelectedSource: (delayMs?: number, focusGenshin?: boolean) => Promise<CaptureResult | null>;
  parseArtifact: (capture: CaptureResult | null) => ParsedArtifactCandidate | null;
  persistParsedArtifact: (capture: CaptureResult | null, parsed: ParsedArtifactCandidate, source: string, needsReview: boolean) => Promise<boolean>;
  saveReviewSample: (capture: CaptureResult | null, parsed: ParsedArtifactCandidate | null, reason?: string) => Promise<BooleanResult | null>;
  shouldFlagArtifactForReview: (parsed: ParsedArtifactCandidate | null) => boolean;
  focusDashboard: () => Promise<void>;
}

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,
    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 };
  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++;
        setAutoScanStats({ ...stats });
      }
      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++;

    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++;
    }
    setAutoScanStats({ ...stats });
    setReviewStatus(`Manueller Scan: neues Artifact erkannt (${stats.parsed}/${maxArtifacts}). Klicke das naechste Artifact an oder druecke Stop.`);
    await wait(700);
  }

  setAutoScanRunning(false);
  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): Promise<void> {
  const {
    autoScanRunning,
    bridgeReady,
    selectedSourceId,
    runtimeInfo,
    automationRepo,
    runtimeRepo,
    stopVisibleScanRef,
    setAutoScanRunning,
    setScanSummary,
    setAutoScanStats,
    setReviewStatus,
    appendAutomationLog,
    appendClickDiagnostics,
    captureSelectedSource,
    captureFastSelectedSource,
    parseArtifact,
    persistParsedArtifact,
    saveReviewSample,
    shouldFlagArtifactForReview,
    scanLimit,
    skipRows,
    detectedInventoryCount,
    focusDashboard,
  } = context;

  if (autoScanRunning || !bridgeReady || !selectedSourceId || !automationRepo?.clickScreen || !automationRepo?.scrollScreen) 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.");

  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");
    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}`);
  }

  setReviewStatus("Genshin wird in den Vordergrund geholt...");
  const focusResult = await automationRepo?.focusGenshin().catch(() => null);
  if (focusResult) {
    appendAutomationLog(
      `focus: ${focusResult.focused ? "ok" : "fehlgeschlagen"} found:${focusResult.genshinFound ? "yes" : "no"} setForeground:${focusResult.setForegroundResult ?? "n/a"} target:${focusResult.targetProcess || "?"} fg:${focusResult.foregroundProcess || "?"}`,
    );
  }
  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);
    setScanSummary({
      mode: "Automatischer Scan",
      status: "blocked",
      ...emptyAutoScanStats,
      targetCount: resolveScanTargetCount(scanLimit, detectedInventoryCount),
      gridLabel: reason,
    });
    return;
  }

  setReviewStatus("Genshin ist im Vordergrund. Automatischer Scan startet. ESC gedrueckt halten oder Stop klicken beendet den Scan sofort.");

  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) => captureSelectedSource(delayMs, focusGenshin),
      captureFastSelectedSource,
      parseArtifact,
      persistParsedArtifact,
      saveReviewSample,
      getAutoReviewReason,
      shouldFlagArtifactForReview,
      appendAutomationLog,
      appendClickDiagnostics,
      setReviewStatus,
      setAutoScanStats,
      shouldStop: () => stopVisibleScanRef.current,
    },
    {
      scanLimit,
      skipRows,
      detectedInventoryCount,
    },
  );

  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: "Automatischer Scan",
    status: result.status,
    ...result.stats,
    targetCount: result.targetCount,
    gridLabel: result.gridLabel,
  });
}
