import { CliArgs } from "../../cli/args";
import type { LogLevelSource } from "../../logger";
import { ConfigValidationWarning, ResolvedConfig, SecondarySubMode } from "../../types";

export interface StartupBootstrapRuntimeState {
  initialArgs: CliArgs;
  mpvSocketPath: string;
  texthookerPort: number;
  backendOverride: string | null;
  autoStartOverlay: boolean;
  texthookerOnlyMode: boolean;
}

interface RuntimeAutoUpdateOptionManagerLike {
  getOptionValue: (id: "anki.autoUpdateNewCards") => unknown;
}

export interface RuntimeConfigLike {
  auto_start_overlay?: boolean;
  bind_visible_overlay_to_mpv_sub_visibility: boolean;
  invisibleOverlay: {
    startupVisibility: "visible" | "hidden" | "platform-default";
  };
  ankiConnect?: {
    behavior?: {
      autoUpdateNewCards?: boolean;
    };
  };
}

export interface StartupBootstrapRuntimeDeps {
  argv: string[];
  parseArgs: (argv: string[]) => CliArgs;
  setLogLevel: (level: string, source: LogLevelSource) => void;
  forceX11Backend: (args: CliArgs) => void;
  enforceUnsupportedWaylandMode: (args: CliArgs) => void;
  getDefaultSocketPath: () => string;
  defaultTexthookerPort: number;
  runGenerateConfigFlow: (args: CliArgs) => boolean;
  startAppLifecycle: (args: CliArgs) => void;
}

export function runStartupBootstrapRuntimeService(
  deps: StartupBootstrapRuntimeDeps,
): StartupBootstrapRuntimeState {
  const initialArgs = deps.parseArgs(deps.argv);

  if (initialArgs.logLevel) {
    deps.setLogLevel(initialArgs.logLevel, "cli");
  } else if (initialArgs.verbose) {
    deps.setLogLevel("debug", "cli");
  }

  deps.forceX11Backend(initialArgs);
  deps.enforceUnsupportedWaylandMode(initialArgs);

  const state: StartupBootstrapRuntimeState = {
    initialArgs,
    mpvSocketPath: initialArgs.socketPath ?? deps.getDefaultSocketPath(),
    texthookerPort: initialArgs.texthookerPort ?? deps.defaultTexthookerPort,
    backendOverride: initialArgs.backend ?? null,
    autoStartOverlay: initialArgs.autoStartOverlay,
    texthookerOnlyMode: initialArgs.texthooker,
  };

  if (!deps.runGenerateConfigFlow(initialArgs)) {
    deps.startAppLifecycle(initialArgs);
  }

  return state;
}

interface AppReadyConfigLike {
  secondarySub?: {
    defaultMode?: SecondarySubMode;
  };
  websocket?: {
    enabled?: boolean | "auto";
    port?: number;
  };
  logging?: {
    level?: "debug" | "info" | "warn" | "error";
  };
}

export interface AppReadyRuntimeDeps {
  loadSubtitlePosition: () => void;
  resolveKeybindings: () => void;
  createMpvClient: () => void;
  reloadConfig: () => void;
  getResolvedConfig: () => AppReadyConfigLike;
  getConfigWarnings: () => ConfigValidationWarning[];
  logConfigWarning: (warning: ConfigValidationWarning) => void;
  setLogLevel: (level: string, source: LogLevelSource) => void;
  initRuntimeOptionsManager: () => void;
  setSecondarySubMode: (mode: SecondarySubMode) => void;
  defaultSecondarySubMode: SecondarySubMode;
  defaultWebsocketPort: number;
  hasMpvWebsocketPlugin: () => boolean;
  startSubtitleWebsocket: (port: number) => void;
  log: (message: string) => void;
  createMecabTokenizerAndCheck: () => Promise<void>;
  createSubtitleTimingTracker: () => void;
  loadYomitanExtension: () => Promise<void>;
  texthookerOnlyMode: boolean;
  shouldAutoInitializeOverlayRuntimeFromConfig: () => boolean;
  initializeOverlayRuntime: () => void;
  handleInitialArgs: () => void;
}

export function getInitialInvisibleOverlayVisibilityService(
  config: RuntimeConfigLike,
  platform: NodeJS.Platform,
): boolean {
  const visibility = config.invisibleOverlay.startupVisibility;
  if (visibility === "visible") return true;
  if (visibility === "hidden") return false;
  if (platform === "linux") return false;
  return true;
}

export function shouldAutoInitializeOverlayRuntimeFromConfigService(
  config: RuntimeConfigLike,
): boolean {
  if (config.auto_start_overlay === true) return true;
  if (config.invisibleOverlay.startupVisibility === "visible") return true;
  return false;
}

export function shouldBindVisibleOverlayToMpvSubVisibilityService(
  config: RuntimeConfigLike,
): boolean {
  return config.bind_visible_overlay_to_mpv_sub_visibility;
}

export function isAutoUpdateEnabledRuntimeService(
  config: ResolvedConfig | RuntimeConfigLike,
  runtimeOptionsManager: RuntimeAutoUpdateOptionManagerLike | null,
): boolean {
  const value = runtimeOptionsManager?.getOptionValue("anki.autoUpdateNewCards");
  if (typeof value === "boolean") return value;
  return (config as ResolvedConfig).ankiConnect?.behavior?.autoUpdateNewCards !== false;
}

export async function runAppReadyRuntimeService(
  deps: AppReadyRuntimeDeps,
): Promise<void> {
  deps.loadSubtitlePosition();
  deps.resolveKeybindings();
  await deps.createMecabTokenizerAndCheck();
  deps.createMpvClient();

  deps.reloadConfig();
  const config = deps.getResolvedConfig();
  deps.setLogLevel(config.logging?.level ?? "info", "config");
  for (const warning of deps.getConfigWarnings()) {
    deps.logConfigWarning(warning);
  }
  deps.initRuntimeOptionsManager();
  deps.setSecondarySubMode(
    config.secondarySub?.defaultMode ?? deps.defaultSecondarySubMode,
  );

  const wsConfig = config.websocket || {};
  const wsEnabled = wsConfig.enabled ?? "auto";
  const wsPort = wsConfig.port || deps.defaultWebsocketPort;

  if (
    wsEnabled === true ||
    (wsEnabled === "auto" && !deps.hasMpvWebsocketPlugin())
  ) {
    deps.startSubtitleWebsocket(wsPort);
  } else if (wsEnabled === "auto") {
    deps.log("mpv_websocket detected, skipping built-in WebSocket server");
  }

  deps.createSubtitleTimingTracker();
  await deps.loadYomitanExtension();

  if (deps.texthookerOnlyMode) {
    deps.log("Texthooker-only mode enabled; skipping overlay window.");
  } else if (deps.shouldAutoInitializeOverlayRuntimeFromConfig()) {
    deps.initializeOverlayRuntime();
  } else {
    deps.log("Overlay runtime deferred: waiting for explicit overlay command.");
  }

  deps.handleInitialArgs();
}
