import type { VextApp } from "./app.js";
/**
 * ParsedFile — 已解析的上传文件
 *
 * 由 multipart 解析插件解析后填充到 req.files。
 * 框架本身不包含 multipart 解析逻辑，仅提供类型定义。
 * 具体解析由用户通过插件引入 busboy / formidable 等库实现。
 *
 * @example
 * // 在 multipart 解析插件中：
 * req.files = parsedFiles  // ParsedFile[]
 * // 在路由 handler 中：
 * const avatar = req.files?.[0]
 * console.log(avatar.filename, avatar.size)
 */
export interface ParsedFile {
    /** 表单字段名（<input name="avatar"> 中的 "avatar"） */
    fieldname: string;
    /** 原始文件名（客户端文件系统中的名称，可能含路径，需自行处理安全） */
    filename: string;
    /** MIME 类型（如 'image/jpeg'、'application/pdf'） */
    mimetype: string;
    /** 文件二进制数据 */
    buffer: Buffer;
    /** 文件大小（字节数） */
    size: number;
}
/**
 * VextRequest — 框架统一请求对象接口
 *
 * 由各 Adapter 负责将底层框架的请求对象转换为此接口。
 * 路由 handler 和中间件通过此接口访问请求数据，
 * 与底层框架解耦，确保切换 Adapter 时用户代码无需改动。
 */
export interface VextRequest {
    /** URL 查询参数（已解析为键值对） */
    query: Record<string, string>;
    /**
     * 请求体（由 body-parser 中间件负责填充）
     * 中间件执行前为 undefined，执行后为解析后的对象
     */
    body: unknown;
    /** 路径动态参数（如 /users/:id 中的 { id: '123' }） */
    params: Record<string, string>;
    /** 请求头（全部小写 key） */
    headers: Record<string, string | undefined>;
    /** HTTP 方法（大写，如 'GET'、'POST'） */
    method: string;
    /** 完整请求 URL */
    url: string;
    /** 路径部分（不含 query string） */
    path: string;
    /**
     * 当前请求匹配的路由模板（如 `/users/:id`）
     *
     * 由各 Adapter 在路由匹配完成后注入，值为路由注册时使用的模板字符串。
     * 与 `path`（实际请求路径，如 `/users/abc-123`）不同，`route` 是低基数字段，
     * 适合作为 Prometheus / OTEL 指标的维度标签，可避免高基数问题。
     *
     * 未匹配到路由时（如 404 场景），值为空字符串 `''`。
     *
     * @example
     * // 注册：app.get('/users/:id', ...)
     * // 请求：GET /users/abc-123
     * req.path   // '/users/abc-123'（实际路径，高基数）
     * req.route  // '/users/:id'（路由模板，低基数）✅
     *
     * @example
     * // 404 场景（无匹配路由）
     * req.route  // ''
     */
    route: string;
    /**
     * 当前请求所属的 app 实例
     *
     * 设计说明：路由 handler 通过 defineRoutes(app => ...) 闭包访问 app，
     * 不需要 req.app。但路由级中间件没有闭包，必须通过 req.app 访问
     * throw/logger 等能力。选择挂在 req.app 而非 req.throw/req.logger，
     * 是为了保持 VextRequest 接口职责清晰——业务扩展字段（req.user 等）
     * 和框架核心能力（app）通过命名空间隔离。
     */
    app: VextApp;
    /**
     * 请求唯一标识
     *
     * - 默认：从 x-request-id 请求头透传，不存在则框架自动生成 UUID v4
     * - 可选：插件通过覆盖 config.requestId.generate 替换生成算法
     *
     * 由 requestId 中间件负责填充，创建时为空字符串。
     */
    requestId: string;
    /**
     * 客户端 IP
     *
     * 当 config.trustProxy = true 时从 X-Forwarded-For 请求头读取第一个 IP，
     * 否则从底层 socket 的 remoteAddress 读取。
     */
    ip: string;
    /**
     * 请求协议
     *
     * 当 config.trustProxy = true 时从 X-Forwarded-Proto 请求头读取，
     * 否则默认为 'http'。
     */
    protocol: "http" | "https";
    /**
     * 注册请求关闭钩子（连接断开时触发）
     *
     * 主要用于 SSE / WebSocket：客户端断开时清理资源。
     * 内存安全：框架在 hooks 执行完毕后自动清空 hooks 数组，
     * 无需手动移除，不会因闭包引用造成内存泄漏。
     *
     * @param handler 关闭时执行的回调
     * @example req.onClose(() => sseStream.close())
     */
    onClose(handler: () => void): void;
    /**
     * 获取经过 validate 校验并类型转换后的数据
     *
     * 必须在 options.validate 配置了对应位置后调用，
     * 未配置对应位置时返回 undefined。
     * schema-dsl 会自动做类型转换（如 query 中的数字字符串 → number）。
     *
     * location 与数据源映射：
     *   'query'  → req.query   （URL 查询参数）
     *   'body'   → req.body    （请求体）
     *   'param'  → req.params  （路径动态参数，如 /:id）
     *   'header' → req.headers （请求头）
     *
     * 注意：location 使用单数 'param'（与 validate 配置的 key 一致），
     * 但底层数据源是复数 req.params。框架内部已正确映射。
     *
     * @param location 校验数据位置
     * @returns 校验后的数据对象；默认返回 Record<string, any>
     *
     * @example
     * // 直接使用（推荐，validator 已保证数据正确性）
     * const { page, limit } = req.valid('query')
     *
     * @example
     * // 加泛型获取更精确的 IDE 提示（可选）
     * const param = req.valid<{ id: string }>('param')
     * param.id  // IDE 知道是 string
     */
    valid<T = Record<string, any>>(location: "query" | "body" | "param" | "header"): T;
    /**
     * 翻译函数（i18n 插件注入）
     * @example req.t('user.not_found')        → '用户不存在'
     * @example req.t('welcome', { name: 'Alice' })  → '欢迎, Alice'
     */
    t?: (key: string, params?: Record<string, unknown>) => string;
    /**
     * 已解析的上传文件列表（由 multipart 解析插件填充）
     *
     * 需在路由或全局注册 multipart 解析插件后方可访问。
     * 插件解析 multipart/form-data 请求体，将文件写入此字段。
     * 非文件上传请求此字段为 undefined。
     *
     * @example
     * app.post('/upload', {}, async (req, res) => {
     *   const file = req.files?.[0]
     *   if (!file) return res.json({ error: '未收到文件' })
     *   // 处理 file.buffer ...
     * })
     */
    files?: ParsedFile[];
    /**
     * 获取原始请求体字符串（框架内部使用）
     *
     * 供 body-parser 中间件读取，不建议用户代码直接调用。
     * 多次调用返回相同字符串（带缓存，流只消费一次）。
     * GET / HEAD / OPTIONS 等无 body 方法返回空字符串。
     *
     * @internal 由各 adapter 注入实现
     */
    _getRawBody(maxBytes?: number): Promise<string>;
    /**
     * 获取原始请求体 Buffer（插件使用）
     *
     * 返回未经任何编码转换的原始字节，是实现 multipart 解析的前提。
     * 多次调用返回相同 Buffer 实例（带缓存，流只消费一次）。
     * GET / HEAD / OPTIONS 等无 body 方法返回空 Buffer（length = 0）。
     *
     * @example
     * // 在 multipart 解析插件中：
     * const buffer = await req._getRawBodyBuffer()
     * // 将 buffer 传递给 busboy / formidable 解析
     *
     * @internal 由各 adapter 注入实现
     */
    _getRawBodyBuffer(maxBytes?: number): Promise<Buffer>;
    /**
     * 允许中间件或插件在 req 上挂载自定义字段。
     *
     * 通过 declare module 'vextjs' 扩展 VextRequest 接口可获得类型提示：
     * @example
     * declare module 'vextjs' {
     *   interface VextRequest {
     *     user?: { id: string; role: string }
     *   }
     * }
     */
    [key: string]: unknown;
}
