import path from "node:path";
import { invalidateAndEvict } from "./cache-invalidator.js";
import { reloadServices } from "./service-reloader.js";
import { reloadRoutes } from "./route-reloader.js";
import { reloadLocales, shouldReloadLocales } from "./i18n-reloader.js";
import { reportMemoryIfNeeded } from "./memory-monitor.js";
// ── SoftReloader 类 ─────────────────────────────────────────
/**
 * SoftReloader — Soft Reload 编排器
 *
 * 核心职责：
 *   1. 编排 Soft Reload 的完整流程（7 个步骤）
 *   2. 管理并发保护（reload 锁 + 待处理队列）
 *   3. 提供降级策略（失败回退 + 级联检测）
 *
 * 使用方式（dev-bootstrap.ts 中创建）：
 *
 * ```ts
 * const softReloader = new SoftReloader({
 *   compiler,
 *   hotHandler,
 *   app,
 *   config,
 *   logger: app.logger,
 *   resolveAdapter,
 *   loadRoutes,
 *   loadMiddlewares,
 *   createErrorHandler,
 *   createNotFoundHandler,
 *   builtinMiddlewares,
 *   configureI18n,
 *   getGlobalMiddlewares: () => internals.getGlobalMiddlewares(),
 * });
 *
 * // IPC 消息中调用
 * process.on('message', async (msg) => {
 *   if (msg.type === 'reload') {
 *     await softReloader.reload(msg.files);
 *   }
 * });
 * ```
 */
export class SoftReloader {
    compiler;
    hotHandler;
    app;
    config;
    logger;
    resolveAdapterFn;
    loadRoutesFn;
    loadMiddlewaresFn;
    createErrorHandlerFn;
    createNotFoundHandlerFn;
    builtinMiddlewares;
    configureI18n;
    getGlobalMiddlewares;
    openapiConfig;
    reloadModelsFn;
    /**
     * v2.2 并发锁 — 防止多次 softReload 并行执行
     *
     * 当 reload() 正在执行时，后续的 reload() 调用将变更文件
     * 暂存到待处理队列中，当前 reload 完成后自动处理队列。
     */
    reloadLock = false;
    /**
     * 待处理的变更文件队列
     *
     * 在 reloadLock 为 true 时，新的变更文件被暂存到此队列。
     * 当前 reload 完成后取出并执行。
     *
     * 合并策略：按 path 去重，保留最新的 type。
     */
    pendingReload = null;
    /**
     * 累计成功 reload 次数
     */
    successCount = 0;
    /**
     * 累计失败 reload 次数
     */
    failureCount = 0;
    get lifecycleLevel() {
        const logger = this.config.logger;
        return logger?.lifecycleLevel === "verbose" ? "verbose" : "concise";
    }
    constructor(options) {
        this.compiler = options.compiler;
        this.hotHandler = options.hotHandler;
        this.app = options.app;
        this.config = options.config;
        this.logger = options.logger;
        this.resolveAdapterFn = options.resolveAdapter;
        this.loadRoutesFn = options.loadRoutes;
        this.loadMiddlewaresFn = options.loadMiddlewares;
        this.createErrorHandlerFn = options.createErrorHandler;
        this.createNotFoundHandlerFn = options.createNotFoundHandler;
        this.builtinMiddlewares = options.builtinMiddlewares;
        this.configureI18n = options.configureI18n;
        this.getGlobalMiddlewares = options.getGlobalMiddlewares;
        this.openapiConfig = options.openapiConfig;
        this.reloadModelsFn = options.reloadModels;
    }
    // ── 公开方法 ──────────────────────────────────────────────
    /**
     * reload — Soft Reload 入口（含并发保护）
     *
     * 如果当前正在 reload，将变更暂存到队列中等待。
     * 当前 reload 完成后自动取出队列中的变更并执行。
     *
     * @param changedFiles 变更文件信息列表
     * @returns 最后一次 reload 的结果
     */
    async reload(changedFiles) {
        // ── 并发保护：合并到待处理队列 ──────────────────────
        if (this.reloadLock) {
            if (this.pendingReload) {
                // 按 path 去重，保留最新的 type
                const map = new Map(this.pendingReload.map((f) => [f.path, f]));
                for (const f of changedFiles) {
                    map.set(f.path, f);
                }
                this.pendingReload = [...map.values()];
            }
            else {
                this.pendingReload = [...changedFiles];
            }
            this.logger.debug(`[hot-reload] reload in progress, queued ${changedFiles.length} file(s)`);
            // 返回一个"已排队"的占位结果
            return {
                success: true,
                requestedColdRestart: false,
                elapsed: 0,
                compileTime: 0,
                cacheTime: 0,
                i18nTime: 0,
                middlewareTime: 0,
                serviceTime: 0,
                modelTime: 0,
                routeTime: 0,
                swapTime: 0,
                tier: "T1:code",
                evictedModules: 0,
            };
        }
        this.reloadLock = true;
        let lastResult;
        try {
            lastResult = await this.doSoftReload(changedFiles);
            // ── 处理待处理队列 ────────────────────────────────
            while (this.pendingReload !== null) {
                const next = this.pendingReload;
                this.pendingReload = null;
                this.logger.info(`[hot-reload] processing queued changes: ${next.length} file(s)`);
                lastResult = await this.doSoftReload(next);
            }
            return lastResult;
        }
        finally {
            this.reloadLock = false;
        }
    }
    /**
     * 获取累计成功 reload 次数
     */
    getSuccessCount() {
        return this.successCount;
    }
    /**
     * 获取累计失败 reload 次数
     */
    getFailureCount() {
        return this.failureCount;
    }
    /**
     * 检查当前是否正在 reload
     */
    isReloading() {
        return this.reloadLock;
    }
    /**
     * 检查是否有待处理的变更
     */
    hasPendingChanges() {
        return this.pendingReload !== null;
    }
    // ── 私有方法 ──────────────────────────────────────────────
    /**
     * doSoftReload — 实际执行 Soft Reload（完整 7 步流程）
     *
     * 流程：
     *   0. 分级编译：modify → compileSingle (Tier 1, O(1))
     *                add/delete → rebuildWithNewEntryPoints (Tier 2, O(N))
     *   1. 清除 require.cache（编译产物及其依赖者）
     *   2. 重载 i18n（如变更包含 locales/）
     *   3. 重载 middleware definitions
     *   4. 选择性重载 services（仅 invalidated 的实例）
     *   5. 创建新 adapter 实例 + 重载 routes + buildHandler
     *   6. 原子替换 handler
     *   7. 内存监控
     *
     * 保持不变的：app、config、plugins、DB 连接、server socket、未变更的 service 实例
     *
     * @param changedFiles 变更文件信息列表（FileChangeInfo[]）
     * @returns 单次 reload 结果
     */
    async doSoftReload(changedFiles) {
        const startTime = performance.now();
        let compileEnd = startTime;
        let cacheEnd = startTime;
        let i18nEnd = startTime;
        let mwEnd = startTime;
        let svcEnd = startTime;
        let modelEnd = startTime;
        let routeEnd = startTime;
        let swapEnd = startTime;
        const hasStructuralChange = changedFiles.some((f) => f.type === "add" || f.type === "delete");
        const tier = hasStructuralChange
            ? "T2:structural"
            : "T1:code";
        try {
            // ── Step 0: 分级编译 ────────────────────────────────
            if (hasStructuralChange) {
                // Tier 2: 结构变更 → 重建 esbuild context + 全量增量编译
                await this.compiler.rebuildWithNewEntryPoints();
            }
            else {
                // Tier 1: 代码变更 → 单文件编译（O(1)，与项目大小无关）
                const projectRoot = this.compiler.getProjectRoot();
                const srcFiles = changedFiles
                    .filter((f) => f.path.startsWith("src/"))
                    .map((f) => path.resolve(projectRoot, f.path));
                if (srcFiles.length > 0) {
                    await this.compiler.compileFiles(srcFiles);
                }
            }
            compileEnd = performance.now();
            // ── Step 1: 清除 require.cache ──────────────────────
            const outDir = this.compiler.getOutDir();
            const filePaths = changedFiles.map((f) => f.path);
            const compiledFiles = filePaths.map((f) => this.compiler.resolveCompiled(f));
            const cacheResult = invalidateAndEvict(compiledFiles, outDir);
            // 级联检测：失效集合 > 80% 缓存 → 降级 Cold Restart
            if (cacheResult.cascadeDetected) {
                this.logger.warn("[hot-reload] invalidation cascade too large " +
                    `(${cacheResult.invalidated.size} modules), requesting cold restart`);
                process.send?.({
                    type: "request-cold-restart",
                    reason: "cascade too large",
                });
                this.failureCount++;
                const elapsed = performance.now() - startTime;
                return {
                    success: false,
                    requestedColdRestart: true,
                    elapsed,
                    compileTime: compileEnd - startTime,
                    cacheTime: elapsed - (compileEnd - startTime),
                    i18nTime: 0,
                    middlewareTime: 0,
                    serviceTime: 0,
                    modelTime: 0,
                    routeTime: 0,
                    swapTime: 0,
                    tier,
                    evictedModules: 0,
                };
            }
            cacheEnd = performance.now();
            // ── Step 2: 重载 i18n ───────────────────────────────
            if (shouldReloadLocales(filePaths)) {
                await reloadLocales({
                    outDir,
                    logger: this.logger,
                    configureI18n: this.configureI18n,
                });
            }
            i18nEnd = performance.now();
            // ── Step 3: 重载 middleware definitions ──────────────
            const middlewareDefs = await this.loadMiddlewaresFn(path.join(outDir, "middlewares"), this.config.middlewares ?? [], this.logger, this.lifecycleLevel);
            mwEnd = performance.now();
            // ── Step 4: 选择性重载 services ─────────────────────
            const serviceResult = await reloadServices(this.app, outDir, cacheResult.invalidated);
            svcEnd = performance.now();
            // ── Step 4.5: 选择性重载 models ─────────────────────
            let modelResult;
            if (this.reloadModelsFn) {
                modelResult = await this.reloadModelsFn(cacheResult.invalidated);
            }
            modelEnd = performance.now();
            // ── Step 5: 创建新 adapter + 重载 routes ────────────
            const globalMiddlewares = this.getGlobalMiddlewares();
            const routeResult = await reloadRoutes({
                app: this.app,
                outDir,
                middlewareDefs,
                globalMiddlewares,
                resolveAdapter: this.resolveAdapterFn,
                loadRoutes: this.loadRoutesFn,
                createErrorHandler: this.createErrorHandlerFn,
                createNotFoundHandler: this.createNotFoundHandlerFn,
                builtinMiddlewares: this.builtinMiddlewares,
                openapiConfig: this.openapiConfig,
            });
            routeEnd = performance.now();
            // ── Step 6: 原子替换 ────────────────────────────────
            this.hotHandler.swap(routeResult.handler);
            swapEnd = performance.now();
            // ── Step 7: 性能报告 + 内存监控 ─────────────────────
            const elapsed = swapEnd - startTime;
            const reloadCount = this.hotHandler.getReloadCount();
            if (this.lifecycleLevel === "verbose") {
                this.logger.info(`[hot-reload] [OK] ${elapsed.toFixed(0)}ms [${tier}] ` +
                    `(compile:${(compileEnd - startTime).toFixed(0)}ms ` +
                    `cache:${(cacheEnd - compileEnd).toFixed(0)}ms ` +
                    `i18n:${(i18nEnd - cacheEnd).toFixed(0)}ms ` +
                    `mw:${(mwEnd - i18nEnd).toFixed(0)}ms ` +
                    `svc:${(svcEnd - mwEnd).toFixed(0)}ms ` +
                    `model:${(modelEnd - svcEnd).toFixed(0)}ms ` +
                    `route:${(routeEnd - modelEnd).toFixed(0)}ms ` +
                    `swap:${(swapEnd - routeEnd).toFixed(0)}ms) ` +
                    `[${cacheResult.evicted} modules evicted] ` +
                    `#${reloadCount}`);
            }
            else {
                this.logger.info(`[hot-reload] [OK] ${elapsed.toFixed(0)}ms [${tier}] #${reloadCount}`);
            }
            // 内存监控
            const memoryReport = reportMemoryIfNeeded(reloadCount);
            this.successCount++;
            return {
                success: true,
                requestedColdRestart: false,
                elapsed,
                compileTime: compileEnd - startTime,
                cacheTime: cacheEnd - compileEnd,
                i18nTime: i18nEnd - cacheEnd,
                middlewareTime: mwEnd - i18nEnd,
                serviceTime: svcEnd - mwEnd,
                modelTime: modelEnd - svcEnd,
                routeTime: routeEnd - modelEnd,
                swapTime: swapEnd - routeEnd,
                tier,
                evictedModules: cacheResult.evicted,
                serviceResult,
                modelResult,
                memoryReport,
            };
        }
        catch (err) {
            // ── 失败回退 ────────────────────────────────────────
            //
            // Soft Reload 的任何步骤失败时：
            //   1. 不调用 swap() — 旧 handler 通过闭包继续服务
            //   2. 打印错误信息
            //   3. 等待用户修复后再次触发
            //
            // 这是设计文档 11e §1 描述的"失败回退机制"：
            //   "如果 soft reload 的任何步骤失败，不调用 swap()，
            //    旧 handler 通过闭包继续服务。"
            //
            const elapsed = performance.now() - startTime;
            const error = err instanceof Error ? err : new Error(String(err));
            this.logger.error(`[hot-reload] [FAIL] failed after ${elapsed.toFixed(0)}ms: ${error.message}`);
            if (error.stack) {
                this.logger.error(error.stack);
            }
            this.logger.error("[hot-reload] keeping previous version active. Fix the error and save again.");
            this.failureCount++;
            return {
                success: false,
                requestedColdRestart: false,
                elapsed,
                compileTime: compileEnd - startTime,
                cacheTime: cacheEnd - compileEnd,
                i18nTime: i18nEnd - cacheEnd,
                middlewareTime: mwEnd - i18nEnd,
                serviceTime: svcEnd - mwEnd,
                modelTime: modelEnd - svcEnd,
                routeTime: routeEnd - modelEnd,
                swapTime: 0,
                tier,
                evictedModules: 0,
                error: error.message,
            };
        }
    }
}
