/**
 * MonSQLize 内置插件核心实现
 *
 * 负责整个 MonSQLize 生命周期编排：
 *   1. 读取并校验 database 配置
 *   2. 构建 MonSQLize 构造函数配置（buildMonSQLizeConfig）
 *   3. 动态 import monsqlize 包，创建实例
 *   4. 注册 onClose 钩子（安全模式：先注册再执行 I/O）
 *   5. 连接数据库（Fail Fast）
 *   6. 加载 Model 定义（本地 + shared 包）
 *   7. 挂载 app.db（MonSQLizeConnection）+ app.monsqlize（原始实例）
 *
 * 设计原则：
 *   - 先注册 onClose 再执行 I/O（确保连接异常时也能清理）
 *   - Fail Fast：配置缺失或连接失败直接抛出，终止启动
 *   - 动态 import monsqlize（避免未安装时框架启动失败）
 *   - 日志桥接：将 MonSQLize 日志桥接到 app.logger
 *
 * @module lib/plugins/monsqlize/plugin
 * @see 13-monsqlize-plugin.md §2.3（插件核心实现）
 */
import { createConnection } from "./connection.js";
import { loadModels } from "./model-loader.js";
/**
 * setupMonSQLize — 插件 setup 入口
 *
 * 由内置插件的 setup() 调用，完成 MonSQLize 的完整初始化流程。
 *
 * @param app     插件上下文（config.database 已可用）
 * @param srcDir  src/ 目录的绝对路径（用于定位 models/ 目录）
 * @throws 配置缺失或连接失败时抛出错误（Fail Fast）
 */
export async function setupMonSQLize(app, srcDir) {
    let config = app.config
        .database;
    // ── 配置校验 ──────────────────────────────────────────────
    if (!config) {
        throw new Error('[monsqlize] Missing "database" configuration.\n' +
            "  Add database config to src/config/default.ts:\n" +
            "  export default {\n" +
            "    database: {\n" +
            '      config: { url: "mongodb://localhost:27017/mydb" }\n' +
            "    }\n" +
            "  }");
    }
    if (!config.config) {
        throw new Error('[monsqlize] Missing "database.config" — at minimum provide { url: "..." }.');
    }
    // ── 0. 多连接池 useMemoryServer 预处理（仅测试场景）────────
    // monSQLize 的 PoolConfig 校验器要求 pool 必须有真实 mongodb:// uri，
    // 不接受 useMemoryServer 标志。此处在 vext 层为标记了 useMemoryServer
    // 的 pool 条目预先启动 MongoMemoryServer，并把生成的 URI 写回 config.uri，
    // 同时在 onClose 中停止这些 memory server。
    const memoryServersToStop = [];
    if (config.pools && config.pools.length > 0) {
        // 深拷贝 pools（vext 配置可能被冻结，无法原地写入）
        const clonedPools = config.pools.map((p) => ({
            ...p,
            config: { ...(p.config || {}) },
        }));
        for (const pool of clonedPools) {
            const innerCfg = pool.config;
            const hasUri = typeof innerCfg.uri === "string" || typeof innerCfg.url === "string";
            if (innerCfg.useMemoryServer === true && !hasUri) {
                try {
                    const { MongoMemoryServer } = await import("mongodb-memory-server");
                    const server = await MongoMemoryServer.create(innerCfg.memoryServerOptions);
                    const uri = server.getUri();
                    innerCfg.uri = uri;
                    delete innerCfg.useMemoryServer;
                    delete innerCfg.memoryServerOptions;
                    memoryServersToStop.push({
                        stop: async () => {
                            await server.stop();
                        },
                    });
                    app.logger.info(`[monsqlize] pool '${pool.name}' using in-memory MongoDB`, { uri });
                }
                catch (err) {
                    throw new Error(`[monsqlize] Failed to start MongoMemoryServer for pool '${pool.name}': ${err.message}\n` +
                        "  Make sure mongodb-memory-server is installed.");
                }
            }
        }
        config = { ...config, pools: clonedPools };
    }
    // ── 1. 构建 MonSQLize 配置 ────────────────────────────────
    const monsqlizeConfig = buildMonSQLizeConfig(config, app);
    // ── 2. 动态 import + 创建 MonSQLize 实例 ──────────────────
    let MonSQLizeClass;
    try {
        const mod = await import("monsqlize");
        // 兼容 CJS default export 和 ESM named export
        MonSQLizeClass = mod.default ?? mod.MonSQLize ?? mod;
    }
    catch (err) {
        throw new Error("[monsqlize] Failed to import 'monsqlize' package.\n" +
            `  ${err.message}\n` +
            "  Make sure monsqlize is installed: npm install monsqlize");
    }
    const monsqlize = new MonSQLizeClass(monsqlizeConfig);
    // ── 3. 先注册 onClose，再执行 I/O（安全模式）──────────────
    //
    // 即使 connect() 失败，onClose 也能正确清理半初始化的资源。
    // monsqlize.close() 内部会检查连接状态，未连接时是 no-op。
    //
    app.onClose(async () => {
        try {
            await monsqlize.close();
            app.logger.info("[monsqlize] connection closed");
        }
        catch (err) {
            app.logger.error("[monsqlize] error closing connection", {
                error: err.message,
            });
        }
        // 停止本插件为 pool 启动的 in-memory MongoDB 实例
        for (const ms of memoryServersToStop) {
            try {
                await ms.stop();
            }
            catch (err) {
                app.logger.warn("[monsqlize] failed to stop pool memory server", {
                    error: err.message,
                });
            }
        }
    });
    // ── 4. 连接数据库（Fail Fast）─────────────────────────────
    const connection = await createConnection(monsqlize, app);
    app.logger.info("[monsqlize] connected successfully");
    // ── 5. 加载 Model 定义 ────────────────────────────────────
    await loadModels(monsqlize, config.models, app, srcDir);
    // ── 6. 挂载到 app ─────────────────────────────────────────
    app.extend("db", connection);
    app.extend("monsqlize", monsqlize);
    app.logger.info("[monsqlize] plugin ready");
}
/**
 * 将 vext database 配置转换为 MonSQLize 构造函数配置
 *
 * 映射关系：
 *   - VextConfig.database.config        → MonSQLize({ config })
 *   - VextConfig.database.cache         → MonSQLize({ cache })
 *   - VextConfig.database.pools         → MonSQLize({ pools })
 *   - VextConfig.database.slowQueryLog  → MonSQLize({ slowQueryLog })
 *   - VextConfig.database.logger='app'  → 桥接到 app.logger
 *
 * @param config  用户的 database 配置
 * @param app     插件上下文（提供 logger 桥接）
 * @returns MonSQLize 构造函数配置对象
 */
function buildMonSQLizeConfig(config, app) {
    // ── 映射 config.config（vext 用户配置 → MonSQLize 配置）────
    // vext 类型定义使用 `url` 字段，MonSQLize 期望 `uri` 字段。
    // N1: 同时支持 uri（主要）和 url（已废弃），做字段名映射。
    const mongoConfig = { ...config.config };
    if ("url" in mongoConfig &&
        mongoConfig.url != null &&
        !("uri" in mongoConfig)) {
        mongoConfig.uri = mongoConfig.url;
        delete mongoConfig.url;
    }
    // N2: 提取 databaseName 传给 MonSQLize 实例
    // MonSQLize 从顶层 databaseName 字段读取并设置 this.databaseName，
    // 用于 _resolveModelCollection 中无 connection.database 时的默认数据库回退。
    const resolvedDatabaseName = config.databaseName ??
        (() => {
            const uri = (mongoConfig.uri ?? mongoConfig.url);
            if (!uri)
                return undefined;
            try {
                const pathname = new URL(uri).pathname;
                const dbName = pathname.replace(/^\//, "").split("?")[0];
                return dbName || undefined;
            }
            catch {
                return undefined;
            }
        })();
    const result = {
        // MonSQLize 的 type 字段是数据库类型（目前仅支持 "mongodb"），
        // 不同于 vext 的 database.type（连接模式：url / replica / srv）。
        // vext 的 config.type 已包含在 config.config 的结构中（url vs hosts vs host），
        // MonSQLize 内部通过 config 结构自动判断连接方式。
        type: "mongodb",
        config: mongoConfig,
        ...(resolvedDatabaseName != null && { databaseName: resolvedDatabaseName }),
        maxTimeMS: config.maxTimeMS ?? 2000,
        findLimit: config.findLimit ?? 10,
        findPageMaxLimit: config.findPageMaxLimit ?? 500,
        slowQueryMs: config.slowQueryMs ?? 500,
        autoConvertObjectId: config.autoConvertObjectId,
        namespace: config.namespace ?? { scope: "database" },
        cursorSecret: config.cursorSecret,
    };
    // ── 缓存配置 ──────────────────────────────────────────────
    if (config.cache) {
        const cacheConfig = {};
        if (config.cache.memory?.enabled !== false) {
            cacheConfig.memory = {
                maxSize: config.cache.memory?.maxSize ?? 1000,
                ttl: config.cache.memory?.ttl ?? 300,
            };
        }
        if (config.cache.redis?.enabled) {
            cacheConfig.redis = {
                url: config.cache.redis.url,
                prefix: config.cache.redis.prefix,
                ttl: config.cache.redis.ttl,
            };
        }
        result.cache = cacheConfig;
    }
    // ── 多连接池 ──────────────────────────────────────────────
    // monSQLize 的 PoolConfig 校验器要求扁平结构 { name, uri, options? }，
    // 而非嵌套的 { name, config: { uri } }。此处把用户在 vext 配置中写的
    // { name, config: { url|uri } } 平铺为 { name, uri, options? }。
    if (config.pools && config.pools.length > 0) {
        result.pools = config.pools.map((pool) => {
            const inner = { ...(pool.config || {}) };
            let uri = inner.uri;
            if (uri == null && inner.url != null)
                uri = inner.url;
            const flat = { name: pool.name, uri };
            if (pool.options !== undefined)
                flat.options = pool.options;
            // 透传可选字段（role/weight/tags/healthCheck 等）
            for (const k of ["role", "weight", "tags", "healthCheck"]) {
                if (pool[k] !== undefined)
                    flat[k] = pool[k];
            }
            return flat;
        });
        result.poolStrategy = config.poolStrategy ?? "auto";
    }
    // ── 慢查询日志 ────────────────────────────────────────────
    if (config.slowQueryLog?.enabled) {
        result.slowQueryLog = {
            collection: config.slowQueryLog.collection ?? "_slow_queries",
        };
    }
    // ── 日志器（桥接 app.logger）──────────────────────────────
    if (config.logger !== false) {
        result.logger = {
            info: (msg, meta) => app.logger.info(`[monsqlize] ${msg}`, meta),
            warn: (msg, meta) => app.logger.warn(`[monsqlize] ${msg}`, meta),
            error: (msg, meta) => app.logger.error(`[monsqlize] ${msg}`, meta),
            debug: (msg, meta) => app.logger.debug(`[monsqlize] ${msg}`, meta),
        };
    }
    // ── 内存数据库（测试用）───────────────────────────────────
    // useMemoryServer 必须放入 mongoConfig（即 result.config），
    // 因为 connectMongo 从 config 参数内部读取该字段：
    //   let { uri, useMemoryServer } = config || {}
    // 而非从顶层 monsqlize 配置读取。
    if (config.useMemoryServer) {
        mongoConfig.useMemoryServer = true;
    }
    return result;
}
