import { join } from "node:path";

import type { RuntimeMode } from "../shared/api";
import type { ResolvedLoggingConfig } from "./checker/types";
import type { Logger } from "./logger";
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 { createConsoleFallback, createRuntimeLogger } from "./logger";
import { startServer } from "./server";

export interface BootstrapDependencies {
  createEngine?: (
    store: ProbeStore,
    targets: ResolvedConfig["targets"],
    maxConcurrentChecks: number,
    retentionMs: number,
    logger: Logger,
  ) => BootstrapEngine;
  createLogger?: (config: ResolvedLoggingConfig, mode: string, version: string) => Promise<Logger>;
  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;
  version: string;
}

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,
      logger: Logger,
    ) => new ProbeEngine(store, targets, maxConcurrentChecks, retentionMs, logger));
  const buildLogger = dependencies.createLogger ?? createRuntimeLogger;
  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 ??
    ((...data: unknown[]) => {
      createConsoleFallback().fatal(
        data.map((item) => (item instanceof Error ? item.message : String(item))).join(" "),
      );
    });

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

  try {
    const config = await load(options.configPath);

    try {
      logger = await buildLogger(config.logging, options.mode, options.version);
    } catch (logInitError) {
      logError("日志初始化失败:", logInitError instanceof Error ? logInitError.message : logInitError);
      exit(1);
    }

    logger!.info({ configPath: options.configPath, mode: options.mode, version: options.version }, "配置加载成功");

    store = createStore(join(config.dataDir, "probe.db"));
    store.syncTargets(config.targets);
    logger!.info({ dataDir: config.dataDir }, "数据库初始化成功");

    engine = createEngine(
      store,
      config.targets,
      config.maxConcurrentChecks,
      config.retentionMs,
      logger!.child({ component: "engine" }),
    );
    engine.start();
    logger!.info(
      { maxConcurrentChecks: config.maxConcurrentChecks, targetCount: config.targets.length },
      "调度引擎启动",
    );

    const shutdown = () => {
      logger?.info("收到退出信号，开始优雅关机");
      engine?.stop();
      store?.close();
      logger?.flush();
      exit(0);
    };
    onSignal("SIGINT", shutdown);
    onSignal("SIGTERM", shutdown);

    serve({
      config: { host: config.host, port: config.port },
      logger: logger!.child({ component: "server" }),
      mode: options.mode,
      staticAssets: options.staticAssets,
      store,
      version: options.version,
    });
  } catch (error) {
    engine?.stop();
    store?.close();
    if (logger) {
      logger.fatal({ error: error instanceof Error ? error.message : String(error) }, "启动失败");
      logger.flush();
    } else {
      logError("启动失败:", error instanceof Error ? error.message : error);
    }
    exit(1);
  }
}
