import path from "node:path";
import { existsSync } from "node:fs";
import { RouteMetadataCollector } from "../openapi/collector.js";
import { OpenAPIGenerator } from "../openapi/generator.js";
import { registerDocEndpoints } from "../openapi/doc-endpoints.js";
// ── 主函数 ──────────────────────────────────────────────────
/**
 * reloadRoutes — 重载路由并构建新的 requestHandler
 *
 * 核心策略：创建全新的 adapter 实例
 *   - Hono 等框架不支持清除路由 → 新建实例解决
 *   - 新 adapter 走与生产环境完全一致的注册 + 构建流程
 *   - 旧 adapter 实例由 GC 自动回收
 *
 * 流程：
 *   1. 创建全新的 adapter 实例（不是复用旧的！）
 *   2. 注册内置中间件（如果提供了 builtinMiddlewares）
 *   3. 注册插件全局中间件
 *   4. 注册错误处理 + 404 兜底
 *   5. 加载路由文件并注册到新 adapter
 *   6. adapter.buildHandler() → 返回新 handler
 *
 * @param options 重载选项
 * @returns 路由重载结果（新 handler + 新 adapter）
 * @throws 任何步骤失败时向上抛出（调用方不应调用 swap）
 */
export async function reloadRoutes(options) {
    const { app, outDir, middlewareDefs, globalMiddlewares, resolveAdapter: resolveAdapterFn, loadRoutes: loadRoutesFn, createErrorHandler: createErrorHandlerFn, createNotFoundHandler: createNotFoundHandlerFn, builtinMiddlewares, } = options;
    const routesDir = path.join(outDir, "routes");
    // ── 1. 创建全新的 adapter 实例 ────────────────────────
    //
    // 这样路由注册表是干净的，且与生产环境走同一条代码路径。
    // resolveAdapter 根据 config.adapter 配置（默认 'native'）
    // 创建对应的 adapter 实例（异步动态 import 按需加载）。
    //
    const freshAdapter = await resolveAdapterFn(app.config, app);
    app.logger.debug(`[hot-reload] creating fresh adapter: ${freshAdapter.name}`);
    // ── 2. 注册内置中间件（如果提供）─────────────────────
    //
    // 与 dev-bootstrap.ts 中的中间件注册顺序和条件守卫保持一致：
    //   requestId → cors → body-parser → rate-limit → response-wrapper → access-log
    //
    // 注意：builtinMwCreators 中对应 creator 为 undefined 时表示该中间件被禁用，
    // route-reloader 通过 if 检查自动跳过，行为与 dev-bootstrap 条件守卫完全一致。
    //
    if (builtinMiddlewares) {
        if (builtinMiddlewares.createRequestIdMiddleware) {
            freshAdapter.registerMiddleware(builtinMiddlewares.createRequestIdMiddleware(app.config));
        }
        if (builtinMiddlewares.createCorsMiddleware) {
            freshAdapter.registerMiddleware(builtinMiddlewares.createCorsMiddleware(app.config));
        }
        if (builtinMiddlewares.createBodyParserMiddleware) {
            freshAdapter.registerMiddleware(builtinMiddlewares.createBodyParserMiddleware(app.config));
        }
        if (builtinMiddlewares.createRateLimitMiddleware) {
            freshAdapter.registerMiddleware(builtinMiddlewares.createRateLimitMiddleware(app.config));
        }
        if (builtinMiddlewares.responseWrapper) {
            freshAdapter.registerMiddleware(builtinMiddlewares.responseWrapper);
        }
        if (builtinMiddlewares.createAccessLogMiddleware) {
            freshAdapter.registerMiddleware(builtinMiddlewares.createAccessLogMiddleware(app.config));
        }
    }
    // ── 3. 注册插件全局中间件 ─────────────────────────────
    //
    // 来自 plugins 的全局中间件在 soft reload 时不变
    // （plugins 属于不可重载阶段，每次 cold restart 才会重新加载）。
    //
    for (const mw of globalMiddlewares) {
        freshAdapter.registerMiddleware(mw);
    }
    // ── 4. 注册错误处理 + 404 兜底 ────────────────────────
    //
    // v2.2: 统一使用 registerErrorHandler（与 08-adapter.md 接口一致）
    //
    const responseConfig = app.config.response ?? {};
    freshAdapter.registerErrorHandler(createErrorHandlerFn(responseConfig));
    freshAdapter.registerNotFound(createNotFoundHandlerFn());
    // ── 5. 临时替换 app.adapter 以便 loadRoutes 使用 ──────
    //
    // loadRoutes 内部通过 app.adapter.registerRoute() 注册路由。
    // 我们需要将 app.adapter 临时替换为新的 freshAdapter，
    // 让 loadRoutes 将路由注册到新实例上。
    //
    // 重载完成后恢复原始 adapter 引用（失败时也恢复）。
    //
    const originalAdapter = app.adapter;
    try {
        app.adapter = freshAdapter;
        // ── 6. 重新加载路由文件并注册到新 adapter ────────────
        //
        // require.cache 已被清除（cache-invalidator），
        // loadRoutes 内部的 dynamic import 或 require 会从
        // .vext/dev/routes/ 读取新编译的 .js。
        //
        // loadRoutes 走与 bootstrap 完全一致的路径：
        //   scanRouteFiles → filePathToPrefix → loadRouteFile →
        //   registerRouteDefinition（含中间件解析 + validate 构建）
        //
        // 🆕 BUG-022 修复：如果 openapiConfig 存在，创建 collector
        // 传入 loadRoutes，在路由加载完成后重新生成 OpenAPI spec
        // 并注册文档端点到新 adapter。
        //
        const openapiCfg = options.openapiConfig;
        const openapiEnabled = openapiCfg != null &&
            openapiCfg.enabled !== false;
        const collector = openapiEnabled ? new RouteMetadataCollector() : null;
        if (existsSync(routesDir)) {
            await loadRoutesFn(app, routesDir, {
                middlewareDefs,
                globalMiddlewares,
            }, collector);
        }
        else {
            app.logger.warn("[hot-reload] routes directory not found, no routes registered");
        }
        // ── 6b. 🆕 重新生成 OpenAPI spec + 注册文档端点 ─────
        //
        // BUG-022 修复：热重载创建全新 adapter 实例后，
        // 原有的 /docs 和 /openapi.json 端点不在新 adapter 上，
        // 必须重新生成 spec 并注册端点。
        //
        if (openapiEnabled && collector) {
            const generator = new OpenAPIGenerator({
                title: openapiCfg?.title,
                description: openapiCfg?.description,
                version: openapiCfg?.version,
                servers: openapiCfg?.servers,
                tags: openapiCfg?.tags,
                securitySchemes: openapiCfg
                    ?.securitySchemes,
                guardSecurityMap: openapiCfg
                    ?.guardSecurityMap,
                contact: openapiCfg?.contact,
                license: openapiCfg?.license,
                tagGroups: openapiCfg?.tagGroups,
            });
            const spec = generator.generate(collector.getRoutes());
            registerDocEndpoints(app, spec, {
                specPath: openapiCfg?.jsonPath ??
                    "/openapi.json",
                specPublicPath: openapiCfg
                    ?.jsonPublicPath,
                docsPath: openapiCfg?.docsPath ??
                    "/docs",
                title: openapiCfg?.title,
                scalar: openapiCfg?.scalar,
            });
            app.logger.info(`[hot-reload] [openapi] ${collector.getCount()} route(s) documented`);
        }
        // ── 7. 从 adapter 构建完整的 requestHandler ─────────
        //
        // adapter 内部的所有逻辑（VextRequest/VextResponse 转换、
        // Hono trie router、trustProxy 处理等）全部保持一致。
        //
        const handler = freshAdapter.buildHandler();
        // ── 7.1 清空路由缓存 ────────────────────────────────
        // 路由定义已变化，旧缓存可能无效，安全起见全部清空。
        if (app.cache) {
            await app.cache.clear();
        }
        app.logger.info(`[hot-reload] routes reloaded via fresh adapter (${freshAdapter.name})`);
        return {
            handler,
            adapter: freshAdapter,
        };
    }
    finally {
        // ── 8. 恢复原始 adapter 引用 ────────────────────────
        //
        // 无论成功还是失败，都将 app.adapter 恢复为原始引用。
        //
        // 注意：soft reload 成功后，app.adapter 仍然指向原始 adapter。
        // 新的 freshAdapter 通过 buildHandler() 返回的 handler
        // 由 HotSwappableHandler.swap() 绑定到 server socket。
        //
        // 这是有意为之：app.adapter 在 dev 模式下不应被
        // 用户代码直接访问（标记为 @internal）。
        // Server socket 的请求处理通过 HotSwappableHandler 间接引用 handler，
        // 而非通过 app.adapter。
        //
        // 如果需要让后续操作使用新 adapter，调用方可以通过
        // RouteReloadResult.adapter 获取并手动更新。
        //
        app.adapter = originalAdapter;
    }
}
// ── 简化版重载函数 ──────────────────────────────────────────
/**
 * createSimpleRouteReloader — 创建一个预配置的路由重载函数
 *
 * 将所有依赖注入点绑定后，返回一个只需 outDir 和 invalidationSet 的
 * 简化重载函数。适合在 soft reload 主流程中使用。
 *
 * @param deps 依赖注入
 * @returns 简化的路由重载函数
 *
 * @example
 * ```ts
 * // 初始化时
 * const reloadRoutesFn = createSimpleRouteReloader({
 *   app,
 *   resolveAdapter,
 *   loadRoutes,
 *   createErrorHandler,
 *   createNotFoundHandler,
 *   middlewareDefs,
 *   globalMiddlewares,
 * })
 *
 * // soft reload 时
 * const { handler } = await reloadRoutesFn(outDir)
 * hotHandler.swap(handler)
 * ```
 */
export function createSimpleRouteReloader(deps) {
    return (outDir) => reloadRoutes({
        app: deps.app,
        outDir,
        middlewareDefs: deps.middlewareDefs,
        globalMiddlewares: deps.globalMiddlewares,
        resolveAdapter: deps.resolveAdapter,
        loadRoutes: deps.loadRoutes,
        createErrorHandler: deps.createErrorHandler,
        createNotFoundHandler: deps.createNotFoundHandler,
        builtinMiddlewares: deps.builtinMiddlewares,
        openapiConfig: deps.openapiConfig,
    });
}
