import { assertBodySize, createPayloadTooLargeError } from "../../lib/middlewares/body-parser.js";
/**
 * HonoContext → VextRequest 转换
 *
 * 将 Hono 的 Context 对象转换为 vext 框架的统一请求接口。
 * 所有底层框架特有的 API 在此处适配，后续代码只与 VextRequest 交互。
 *
 * 转换要点：
 *   - query: 从 URL searchParams 解析（🆕 懒解析，首次访问时才解析）
 *   - body: 由 body-parser 中间件后续填充（初始 undefined）
 *   - params: 从 Hono 路由参数提取（🆕 懒解析，首次访问时才提取）
 *   - headers: 从原始请求头提取（key 全小写）（🆕 懒解析，首次访问时才遍历）
 *   - requestId: 由 requestId 中间件后续填充（初始空字符串）
 *   - ip: 根据 trustProxy 配置决定从 X-Forwarded-For 或 socket 读取
 *   - protocol: 根据 trustProxy 配置决定从 X-Forwarded-Proto 或默认值读取
 *   - onClose: 注册请求关闭钩子，连接断开时触发
 *   - valid: 获取 validate 中间件校验后的数据
 *
 * 🆕 性能优化（懒解析）：
 *   query / headers / params 使用 getter + 缓存模式。
 *   大部分中间件和 handler 只访问其中 1-2 个字段，
 *   懒解析避免了每请求都遍历所有 headers 和构造 URL 对象的开销。
 *   首次访问时解析并缓存，后续访问直接返回缓存值。
 *
 * @param c           Hono Context 对象
 * @param app         VextApp 实例
 * @returns VextRequest 实例
 */
export function createVextRequest(c, app) {
    const trustProxy = app.config.trustProxy ?? false;
    const closeHandlers = [];
    // ── 懒解析缓存 ──────────────────────────────────────────
    let _queryCache;
    let _headersCache;
    let _paramsCache;
    // ── 缓存原始请求体（body-parser 用）───────────────────────
    // 使用 Buffer 作为主缓存（arrayBuffer() 读取一次），字符串从中惰性派生。
    // 这样 _getRawBodyBuffer() 和 _getRawBody() 都不会重复消费 ReadableStream。
    let _rawBufferCache;
    let _rawStringCache;
    async function getRawBodyBuffer(maxBytes) {
        if (_rawBufferCache !== undefined) {
            assertBodySize(_rawBufferCache.byteLength, maxBytes);
            return _rawBufferCache;
        }
        const body = c.req.raw.body;
        if (body) {
            const reader = body.getReader();
            const chunks = [];
            let total = 0;
            while (true) {
                const { value, done } = await reader.read();
                if (done)
                    break;
                const chunk = Buffer.from(value);
                total += chunk.byteLength;
                if (maxBytes !== undefined && total > maxBytes) {
                    throw createPayloadTooLargeError(maxBytes);
                }
                chunks.push(chunk);
            }
            _rawBufferCache = Buffer.concat(chunks);
            return _rawBufferCache;
        }
        const ab = await c.req.arrayBuffer();
        _rawBufferCache = Buffer.from(ab);
        assertBodySize(_rawBufferCache.byteLength, maxBytes);
        return _rawBufferCache;
    }
    async function getRawBody(maxBytes) {
        if (_rawStringCache !== undefined) {
            assertBodySize(Buffer.byteLength(_rawStringCache, "utf-8"), maxBytes);
            return _rawStringCache;
        }
        const buf = await getRawBodyBuffer(maxBytes);
        _rawStringCache = buf.toString("utf-8");
        return _rawStringCache;
    }
    // ── 懒解析函数 ──────────────────────────────────────────
    /**
     * 懒解析 query 参数
     *
     * 仅在首次访问 req.query 时执行 new URL() + Object.fromEntries()。
     * GET /json 等不需要 query 的场景完全跳过此开销。
     */
    function parseQuery() {
        if (_queryCache !== undefined)
            return _queryCache;
        try {
            const url = new URL(c.req.url);
            _queryCache = Object.fromEntries(url.searchParams);
        }
        catch {
            // URL 解析失败时降级为空对象（防御性处理）
            _queryCache = {};
        }
        return _queryCache;
    }
    /**
     * 懒解析 headers
     *
     * 仅在首次访问 req.headers 时遍历原始 Headers 对象。
     * 大部分中间件通过 c.req.header() 直接读取单个 header，
     * 但某些场景（如 validate header / 用户代码遍历）需要完整对象。
     *
     * 🆕 优化：中间件内部直接读取单个 header 时（如 requestId / cors / bodyParser），
     * 通过 req.headers['key'] 触发此懒解析，首次调用后缓存。
     */
    function parseHeaders() {
        if (_headersCache !== undefined)
            return _headersCache;
        const record = {};
        c.req.raw.headers.forEach((value, key) => {
            record[key] = value;
        });
        _headersCache = record;
        return _headersCache;
    }
    /**
     * 懒解析路由参数
     *
     * c.req.param() 在某些 Hono 版本中，当路由无动态段时可能抛出异常。
     * 使用 try-catch 防御，降级为空对象。
     * 仅在首次访问 req.params 时执行。
     */
    function parseParams() {
        if (_paramsCache !== undefined)
            return _paramsCache;
        try {
            _paramsCache = (c.req.param() ?? {});
        }
        catch {
            _paramsCache = {};
        }
        return _paramsCache;
    }
    // ── 构造 VextRequest 对象 ────────────────────────────────
    //
    // 🆕 性能优化：query / headers / params 使用 Object.defineProperty
    // 定义为 getter + 缓存（lazy evaluation）。首次访问时解析，后续直接返回缓存值。
    // 这样 GET /json 这类简单场景不会触发 new URL() / headers 遍历等重操作。
    //
    // 实现方式：先在对象字面量中放占位符值（满足 TypeScript 类型检查），
    // 然后立即用 Object.defineProperty 覆盖为 getter。
    // 首次访问 getter 时执行解析并用 value descriptor 替换 getter（后续零开销）。
    //
    const req = {
        // ── 占位符（立即被 defineProperty 覆盖）─────────────
        query: null,
        headers: null,
        params: null,
        // ── 立即可用字段 ────────────────────────────────────
        body: undefined, // body-parser 中间件负责填充
        method: c.req.method.toUpperCase(),
        url: c.req.url,
        path: c.req.path,
        route: "", // registerRoute handler 覆写为真实路由模板（F-01）
        // ── 元信息 ──────────────────────────────────────────
        app,
        requestId: "", // requestId 中间件负责填充
        ip: resolveIp(c, trustProxy),
        protocol: resolveProtocol(c, trustProxy),
        // ── 生命周期 ────────────────────────────────────────
        onClose(handler) {
            closeHandlers.push(handler);
        },
        // ── 校验数据 ────────────────────────────────────────
        valid(location) {
            // validate 中间件将校验后的数据存储在 req._validated_<location> 上
            return req[`_validated_${location}`];
        },
        // ── 内部方法（body-parser 中间件使用）───────────────────
        // 从 Hono Context 读取原始请求体，带缓存（流只能消费一次）。
        _getRawBody: getRawBody,
        _getRawBodyBuffer: getRawBodyBuffer,
        // ── 内部方法（body-parser multipart 解析用）────────────
        // 返回 Hono 原生 Web Request 对象，供 body-parser 直接调用
        // c.req.raw.formData()，跳过 Buffer 中转，性能更优。
        // 仅供框架内部使用，不建议用户代码直接调用。
        _getHonoRawRequest() {
            return c.req.raw;
        },
    };
    // ── 定义懒解析 getter（query / headers / params）────────
    //
    // 使用 defineProperty 覆盖占位符值，实现：
    //   1. 首次访问时调用解析函数
    //   2. 解析后用 value descriptor 覆盖 getter（后续访问零开销）
    //   3. 仍然支持写入（如测试场景下直接赋值）
    //
    // 注意：configurable: true 是必须的，否则首次访问后无法覆盖为 value descriptor。
    //
    Object.defineProperty(req, "query", {
        get() {
            const value = parseQuery();
            // 用 value descriptor 覆盖 getter，后续访问直接返回值（零开销）
            Object.defineProperty(req, "query", {
                value,
                writable: true,
                configurable: true,
                enumerable: true,
            });
            return value;
        },
        set(v) {
            // 支持直接赋值（如测试场景）
            _queryCache = v;
            Object.defineProperty(req, "query", {
                value: v,
                writable: true,
                configurable: true,
                enumerable: true,
            });
        },
        configurable: true,
        enumerable: true,
    });
    Object.defineProperty(req, "headers", {
        get() {
            const value = parseHeaders();
            Object.defineProperty(req, "headers", {
                value,
                writable: true,
                configurable: true,
                enumerable: true,
            });
            return value;
        },
        set(v) {
            _headersCache = v;
            Object.defineProperty(req, "headers", {
                value: v,
                writable: true,
                configurable: true,
                enumerable: true,
            });
        },
        configurable: true,
        enumerable: true,
    });
    Object.defineProperty(req, "params", {
        get() {
            const value = parseParams();
            Object.defineProperty(req, "params", {
                value,
                writable: true,
                configurable: true,
                enumerable: true,
            });
            return value;
        },
        set(v) {
            _paramsCache = v;
            Object.defineProperty(req, "params", {
                value: v,
                writable: true,
                configurable: true,
                enumerable: true,
            });
        },
        configurable: true,
        enumerable: true,
    });
    // ── 请求结束时执行 onClose hooks ─────────────────────────
    // 通过 AbortSignal 监听请求中断（客户端断开连接）
    // 内存安全：执行后清空 handlers 数组，防止闭包泄漏
    try {
        const signal = c.req.raw.signal;
        if (signal) {
            const onAbort = () => {
                for (const h of closeHandlers) {
                    try {
                        h();
                    }
                    catch {
                        // onClose handler 异常不应影响其他 handler
                    }
                }
                closeHandlers.length = 0;
            };
            if (signal.aborted) {
                // 信号已触发，立即执行
                onAbort();
            }
            else {
                signal.addEventListener("abort", onAbort, { once: true });
            }
        }
    }
    catch {
        // 某些环境下 signal 可能不可用，静默忽略
    }
    return req;
}
/**
 * 解析客户端 IP 地址
 *
 * trustProxy = true 时，从 X-Forwarded-For 请求头读取第一个 IP（代理链的原始客户端 IP）。
 * trustProxy = false 时，从底层 socket 的 remoteAddress 读取。
 *
 * @param c          Hono Context
 * @param trustProxy 是否信任代理
 * @returns 客户端 IP 地址
 */
function resolveIp(c, trustProxy) {
    if (trustProxy) {
        const xff = c.req.header("x-forwarded-for");
        if (xff) {
            const firstIp = xff.split(",")[0];
            if (firstIp)
                return firstIp.trim();
        }
    }
    // Hono node-server 通过 c.env.incoming 暴露原始 IncomingMessage
    // 从其 socket.remoteAddress 获取客户端 IP
    try {
        const env = c.env;
        const incoming = env?.incoming;
        if (incoming?.socket?.remoteAddress) {
            return incoming.socket.remoteAddress;
        }
    }
    catch {
        // 环境不支持时降级
    }
    return "127.0.0.1";
}
/**
 * 解析请求协议
 *
 * trustProxy = true 时，从 X-Forwarded-Proto 请求头读取。
 * trustProxy = false 时，默认为 'http'。
 *
 * @param c          Hono Context
 * @param trustProxy 是否信任代理
 * @returns 请求协议 'http' | 'https'
 */
function resolveProtocol(c, trustProxy) {
    if (trustProxy) {
        const proto = c.req.header("x-forwarded-proto");
        if (proto === "https")
            return "https";
    }
    return "http";
}
