import {
  artifactTabClickTarget,
  keyPressBlocked,
  validateAutoScanEntryPreflight,
  type ScanEntryMode,
} from "../../../lib/autoScanEntry";
import { wait } from "../../../lib/scanReviewUtils";
import {
  summarizeClickResult,
  summarizeKeyPressResult,
  type createScanDiagnosticEvent,
} from "../../../lib/scanDiagnosticsLog";
import type { AutomationRepositoryPort } from "../../../infrastructure/repositories/rendererBridgeRepositories";
import type { CaptureOptions, CaptureResult } from "../../../types/global";

export interface PrepareAutoScanEntryInput {
  mode: ScanEntryMode;
  automationRepo: AutomationRepositoryPort;
  captureFastSelectedSource: (delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult | null>;
  appendAutomationLog: (line: string) => void;
  appendDiagnosticEvent: (event: Omit<Parameters<typeof createScanDiagnosticEvent>[0], "includeFullScreenshot">) => void;
}

export async function prepareAutoScanEntry({
  mode,
  automationRepo,
  captureFastSelectedSource,
  appendAutomationLog,
  appendDiagnosticEvent,
}: PrepareAutoScanEntryInput) {
  if (mode === "visible-inventory") {
    const capture = await waitForEntryCapture({
      captureFastSelectedSource,
      timeoutMs: 900,
      predicate: (candidate) => validateAutoScanEntryPreflight(candidate).ok,
    });
    appendDiagnosticEvent({
      phase: "entry-visible",
      severity: capture ? "ok" : "error",
      message: capture && validateAutoScanEntryPreflight(capture).ok
        ? "Visible inventory preflight capture ready."
        : "Visible inventory preflight capture failed.",
      capture,
    });
    return capture;
  }

  if (mode === "direct-inventory") {
    return tryInventoryEntrySequence({
      label: "direct",
      sendEscapeFirst: false,
      automationRepo,
      captureFastSelectedSource,
      appendAutomationLog,
      appendDiagnosticEvent,
    });
  }

  if (mode === "auto-entry") {
    const directCapture = await tryInventoryEntrySequence({
      label: "auto-direct",
      sendEscapeFirst: false,
      automationRepo,
      captureFastSelectedSource,
      appendAutomationLog,
      appendDiagnosticEvent,
    });
    const directPreflight = validateAutoScanEntryPreflight(directCapture);
    if (directPreflight.ok) return directCapture;
    appendAutomationLog(`auto-entry direct path failed: ${directPreflight.reason}`);
    appendDiagnosticEvent({
      phase: "entry-fallback",
      severity: "info",
      message: `Direct inventory entry did not reach an artifact detail card. Trying ESC/B fallback. ${directPreflight.reason}`,
      capture: directCapture,
    });
  }

  return tryInventoryEntrySequence({
    label: "paimon",
    sendEscapeFirst: true,
    automationRepo,
    captureFastSelectedSource,
    appendAutomationLog,
    appendDiagnosticEvent,
  });
}

async function tryInventoryEntrySequence({
  label,
  sendEscapeFirst,
  automationRepo,
  captureFastSelectedSource,
  appendAutomationLog,
  appendDiagnosticEvent,
}: {
  label: string;
  sendEscapeFirst: boolean;
  automationRepo: AutomationRepositoryPort;
  captureFastSelectedSource: (delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult | null>;
  appendAutomationLog: (line: string) => void;
  appendDiagnosticEvent: (event: Omit<Parameters<typeof createScanDiagnosticEvent>[0], "includeFullScreenshot">) => void;
}) {
  if (sendEscapeFirst) {
    const escapeResult = await automationRepo.keyPress?.("ESC");
    appendAutomationLog(`${label} entry key ESC: ${escapeResult?.ok ? "ok" : "blocked"}`);
    appendDiagnosticEvent({
      phase: "entry-key",
      severity: keyPressBlocked(escapeResult) ? "error" : "ok",
      message: `${label} entry key ESC`,
      details: summarizeKeyPressResult(escapeResult),
    });
    if (keyPressBlocked(escapeResult)) return null;
    const menuProbe = await waitForEntryCapture({
      captureFastSelectedSource,
      timeoutMs: 750,
      predicate: (capture) => Boolean(capture),
    });
    appendDiagnosticEvent({
      phase: "entry-capture",
      severity: menuProbe ? "info" : "warn",
      message: `${label} capture after ESC step.`,
      capture: menuProbe,
    });
    if (menuProbe?.paimonMenu?.present) {
      const closeMenuResult = await automationRepo.keyPress?.("ESC");
      appendAutomationLog(`${label} entry key ESC close menu: ${closeMenuResult?.ok ? "ok" : "blocked"}`);
      appendDiagnosticEvent({
        phase: "entry-key",
        severity: keyPressBlocked(closeMenuResult) ? "error" : "ok",
        message: `${label} entry key ESC close menu`,
        details: summarizeKeyPressResult(closeMenuResult),
      });
      if (keyPressBlocked(closeMenuResult)) return menuProbe;
      const worldProbe = await waitForEntryCapture({
        captureFastSelectedSource,
        timeoutMs: 750,
        predicate: (capture) => Boolean(capture && !capture.paimonMenu?.present),
      });
      appendDiagnosticEvent({
        phase: "entry-capture",
        severity: worldProbe ? "info" : "warn",
        message: `${label} capture after closing Paimon menu.`,
        capture: worldProbe,
      });
      if (worldProbe?.paimonMenu?.present) {
        appendAutomationLog(`${label} entry stopped: Paimon menu still visible after second ESC`);
        return worldProbe;
      }
    }
  }

  const inventoryResult = await automationRepo.keyPress?.("B");
  appendAutomationLog(`${label} entry key B: ${inventoryResult?.ok ? "ok" : "blocked"}`);
  appendDiagnosticEvent({
    phase: "entry-key",
    severity: keyPressBlocked(inventoryResult) ? "error" : "ok",
    message: `${label} entry key B`,
    details: summarizeKeyPressResult(inventoryResult),
  });
  if (keyPressBlocked(inventoryResult)) return null;
  const tabProbe = await waitForEntryCapture({
    captureFastSelectedSource,
    timeoutMs: 1200,
    predicate: (capture) => Boolean(capture && !capture.paimonMenu?.present && capture.inventoryGrid && capture.inventoryGrid.source !== "missing"),
  });
  appendDiagnosticEvent({
    phase: "entry-capture",
    severity: tabProbe ? "info" : "warn",
    message: `${label} capture after Inventory-key step.`,
    capture: tabProbe,
  });
  if (tabProbe?.paimonMenu?.present) {
    appendAutomationLog(`${label} entry stopped: Paimon menu still visible after Inventory key`);
    appendDiagnosticEvent({
      phase: "entry-capture",
      severity: "info",
      message: `${label} entry stopped before tab click because Paimon menu is still visible.`,
      capture: tabProbe,
    });
    return tabProbe;
  }
  if (!tabProbe?.inventoryGrid || tabProbe.inventoryGrid.source === "missing") return tabProbe;

  const target = artifactTabClickTarget(tabProbe);
  appendAutomationLog(`${label} entry artifact tab -> ${target.x},${target.y}`);
  const click = await automationRepo.clickScreen(target.x, target.y);
  appendDiagnosticEvent({
    phase: "entry-click",
    severity: click.inputBlocked || click.clicked === false || click.moved === false ? "warn" : "ok",
    message: `${label} artifact tab click at ${target.x},${target.y}`,
    details: summarizeClickResult(click),
    capture: tabProbe,
  });
  if (click.inputBlocked || click.clicked === false) return null;
  const gridProbe = await waitForEntryCapture({
    captureFastSelectedSource,
    timeoutMs: 900,
    predicate: (capture) => Boolean(capture?.inventoryGrid && capture.inventoryGrid.source !== "missing"),
  });
  appendDiagnosticEvent({
    phase: "entry-capture",
    severity: gridProbe ? "info" : "warn",
    message: `${label} capture after artifact-tab click before first tile selection.`,
    capture: gridProbe,
  });
  const firstTarget = gridProbe?.inventoryGrid?.centers?.[0];
  if (!firstTarget) return gridProbe;
  appendAutomationLog(`${label} entry first artifact tile -> ${firstTarget.x},${firstTarget.y}`);
  const firstTileClick = await automationRepo.clickScreen(firstTarget.x, firstTarget.y);
  appendDiagnosticEvent({
    phase: "entry-click",
    severity: firstTileClick.inputBlocked || firstTileClick.clicked === false || firstTileClick.moved === false ? "warn" : "ok",
    message: `${label} first artifact tile click at ${firstTarget.x},${firstTarget.y}`,
    details: summarizeClickResult(firstTileClick),
    capture: gridProbe,
  });
  if (firstTileClick.inputBlocked || firstTileClick.clicked === false) return null;
  const finalCapture = await waitForEntryCapture({
    captureFastSelectedSource,
    timeoutMs: 900,
    predicate: (capture) => validateAutoScanEntryPreflight(capture).ok,
  });
  appendDiagnosticEvent({
    phase: "entry-capture",
    severity: finalCapture ? "info" : "warn",
    message: `${label} final capture after first artifact selection.`,
    capture: finalCapture,
  });
  return finalCapture;
}

async function waitForEntryCapture({
  captureFastSelectedSource,
  timeoutMs,
  pollMs = 150,
  predicate,
}: {
  captureFastSelectedSource: (delayMs?: number, focusGenshin?: boolean, options?: CaptureOptions) => Promise<CaptureResult | null>;
  timeoutMs: number;
  pollMs?: number;
  predicate: (capture: CaptureResult | null) => boolean;
}) {
  const startedAt = Date.now();
  let latest: CaptureResult | null = null;
  while (Date.now() - startedAt <= timeoutMs) {
    latest = await captureFastSelectedSource(0, true);
    if (predicate(latest)) return latest;
    const remaining = timeoutMs - (Date.now() - startedAt);
    if (remaining <= 0) break;
    await wait(Math.min(pollMs, remaining));
  }
  return latest;
}
