import { join } from "node:path";

import type { RuntimeMode } from "../shared/api";
import type { StartServerOptions } from "./server";
import type { StaticAssets } from "./static";

import { loadConfig, type ResolvedConfig } from "./checker/config-loader";
import { ProbeEngine } from "./checker/engine";
import { ProbeStore } from "./checker/store";
import { startServer } from "./server";

export interface BootstrapDependencies {
  createEngine?: (
    store: ProbeStore,
    targets: ResolvedConfig["targets"],
    maxConcurrentChecks: number,
    retentionMs: number,
  ) => BootstrapEngine;
  createStore?: (dbPath: string) => ProbeStore;
  exit?: (code: number) => never;
  loadConfig?: (configPath: string) => Promise<ResolvedConfig>;
  logError?: (...data: unknown[]) => void;
  onSignal?: (signal: ShutdownSignal, handler: () => void) => void;
  startServer?: (options: StartServerOptions) => unknown;
}

export interface BootstrapOptions {
  configPath: string;
  mode: RuntimeMode;
  staticAssets?: StaticAssets;
}

type BootstrapEngine = Pick<ProbeEngine, "start" | "stop">;
type ShutdownSignal = "SIGINT" | "SIGTERM";

export async function bootstrap(options: BootstrapOptions, dependencies: BootstrapDependencies = {}): Promise<void> {
  const load = dependencies.loadConfig ?? loadConfig;
  const createStore = dependencies.createStore ?? ((dbPath: string) => new ProbeStore(dbPath));
  const createEngine =
    dependencies.createEngine ??
    ((store: ProbeStore, targets: ResolvedConfig["targets"], maxConcurrentChecks: number, retentionMs: number) =>
      new ProbeEngine(store, targets, maxConcurrentChecks, retentionMs));
  const serve = dependencies.startServer ?? startServer;
  const onSignal =
    dependencies.onSignal ??
    ((signal: ShutdownSignal, handler: () => void) => {
      process.on(signal, handler);
    });
  const exit = dependencies.exit ?? ((code: number) => process.exit(code));
  const logError = dependencies.logError ?? console.error;

  let store: ProbeStore | undefined;
  let engine: BootstrapEngine | undefined;

  try {
    const config = await load(options.configPath);
    store = createStore(join(config.dataDir, "probe.db"));
    store.syncTargets(config.targets);

    engine = createEngine(store, config.targets, config.maxConcurrentChecks, config.retentionMs);
    engine.start();

    const shutdown = () => {
      engine?.stop();
      store?.close();
      exit(0);
    };
    onSignal("SIGINT", shutdown);
    onSignal("SIGTERM", shutdown);

    serve({
      config: { host: config.host, port: config.port },
      mode: options.mode,
      staticAssets: options.staticAssets,
      store,
    });
  } catch (error) {
    engine?.stop();
    store?.close();
    logError("启动失败:", error instanceof Error ? error.message : error);
    exit(1);
  }
}
