/**
 * @file tus协议服务端类型定义文件
 * @description 定义了tus文件上传服务器所需的各种类型接口
 * @version 1.0.0
 */

import type http from 'node:http'
import { Locker, Upload } from './utils'


/**
 * tus服务器配置选项接口
 * @interface ServerOptions
 * @description 包含了配置tus服务器所需的所有选项
 */
export type ServerOptions = {
  /**
   * 服务器接收上传请求的路由路径
   * @example '/files'
   */
  path: string

  /**
   * 允许上传的最大文件大小限制
   * @param req HTTP请求对象
   * @param uploadId 上传ID
   * @returns 文件大小限制(字节)
   */
  maxSize?:
  | number
  | ((req: http.IncomingMessage, uploadId: string | null) => Promise<number> | number)

  /**
   * 是否返回相对URL作为Location响应头
   * @description 默认返回绝对URL,设为true则返回相对URL
   */
  relativeLocation?: boolean

  /**
   * 是否支持代理转发头
   * @description 允许使用Forwarded、X-Forwarded-Proto和X-Forwarded-Host头
   * 来覆盖服务器返回的Location头
   */
  respectForwardedHeaders?: boolean

  /**
   * CORS允许的自定义请求头
   * @description 这些头会被添加到Access-Control-Allow-Headers响应头中
   */
  allowedHeaders?: string[]

  /**
   * 是否允许跨域请求携带凭证
   * @description 设置Access-Control-Allow-Credentials响应头
   */
  allowedCredentials?: boolean

  /**
   * CORS允许的来源域名列表
   * @description 这些域名会被添加到Access-Control-Allow-Origin响应头中
   */
  allowedOrigins?: string[]

  /**
   * 上传进度事件发送间隔(毫秒)
   * @description 通过EVENTS.POST_RECEIVE_V2事件发送上传进度的时间间隔
   */
  postReceiveInterval?: number

  /**
   * 自定义上传URL生成逻辑
   * @param req HTTP请求对象
   * @param options URL生成选项
   * @returns 生成的上传URL
   */
  generateUrl?: (
    req: http.IncomingMessage,
    options: { proto: string; host: string; path: string; id: string }
  ) => string

  /**
   * 自定义从请求中提取上传ID的逻辑
   * @param req HTTP请求对象
   * @param lastPath URL最后一段路径
   * @returns 提取的上传ID
   */
  getFileIdFromRequest?: (
    req: http.IncomingMessage,
    lastPath?: string
  ) => string | undefined

  /**
   * 自定义文件命名函数
   * @description 默认使用crypto.randomBytes(16).toString('hex')生成
   * @param req HTTP请求对象
   * @param metadata 上传文件的元数据
   * @returns 生成的文件名
   */
  namingFunction?: (
    req: http.IncomingMessage,
    metadata?: Record<string, string | null>
  ) => string | Promise<string>

  /**
   * 文件锁接口实现
   * @description 用于确保对上传文件和元数据的独占访问
   */
  locker:
  | Locker
  | Promise<Locker>
  | ((req: http.IncomingMessage) => Locker | Promise<Locker>)

  /**
   * 锁清理超时时间(毫秒)
   * @description 服务器等待已取消的锁进行清理的最长时间
   */
  lockDrainTimeout?: number

  /**
   * 是否禁止终止已完成的上传
   * @description 设为true时无法删除已上传完成的文件
   */
  disableTerminationForFinishedUploads?: boolean

  /**
   * 上传创建前的钩子函数
   * @description 在新上传创建前调用,可用于验证上传元数据或添加响应头
   * @param req HTTP请求对象
   * @param res HTTP响应对象
   * @param upload 上传对象
   * @throws 抛出错误时会中止请求
   */
  onUploadCreate?: (
    req: http.IncomingMessage,
    res: http.ServerResponse,
    upload: Upload
  ) => Promise<
    http.ServerResponse | { res: http.ServerResponse; metadata?: Upload['metadata'] }
  >

  /**
   * 上传完成后的钩子函数
   * @description 在上传完成后、返回响应前调用,可用于后处理验证
   * @param req HTTP请求对象
   * @param res HTTP响应对象
   * @param upload 上传对象
   * @throws 抛出错误时会中止请求
   */
  onUploadFinish?: (
    req: http.IncomingMessage,
    res: http.ServerResponse,
    upload: Upload
  ) => Promise<
    | http.ServerResponse
    | {
      res: http.ServerResponse
      status_code?: number
      headers?: Record<string, string | number>
      body?: string
    }
  >

  /**
   * 请求接收时的钩子函数
   * @description 在收到新请求时调用
   * @param req HTTP请求对象
   * @param res HTTP响应对象
   * @param uploadId 上传ID
   */
  onIncomingRequest?: (
    req: http.IncomingMessage,
    res: http.ServerResponse,
    uploadId: string
  ) => Promise<void>

  /**
   * 错误响应处理钩子
   * @description 在服务器即将发送错误响应时调用,用于自定义错误处理
   * @param req HTTP请求对象
   * @param res HTTP响应对象
   * @param err 错误对象
   */
  onResponseError?: (
    req: http.IncomingMessage,
    res: http.ServerResponse,
    err: Error | { status_code: number; body: string }
  ) =>
    | Promise<{ status_code: number; body: string } | undefined>
    | { status_code: number; body: string }
    | undefined
}

/**
 * 路由处理器类型
 * @description HTTP请求处理函数类型定义
 */
export type RouteHandler = (req: http.IncomingMessage, res: http.ServerResponse) => void

/**
 * 工具类型:使指定属性变为可选
 * @template T 原始类型
 * @template K 需要变为可选的属性键
 */
export type WithOptional<T, K extends keyof T> = Omit<T, K> & { [P in K]+?: T[P] }

/**
 * 工具类型:使指定属性变为必需
 * @template T 原始类型
 * @template K 需要变为必需的属性键
 */
export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }