import debug from 'debug'

import { BaseHandler } from './BaseHandler'

import { validateHeader } from '../validators/HeaderValidator'

import type http from 'node:http'
import type { ServerOptions, WithRequired } from '../types'
import { DataStore, Uid, CancellationContext, ERRORS, Metadata, Upload, EVENTS } from '../utils'

const log = debug('tus-node-server:handlers:post')

/**
 * PostHandler 类用于处理 HTTP POST 请求，主要用于在 DataStore 中创建文件。
 * 该类继承自 BaseHandler，并重写了部分方法以实现特定的功能。
 */
export class PostHandler extends BaseHandler {
  // 重写 BaseHandler 中的 options 类型，确保在构造函数中设置了 namingFunction
  declare options: WithRequired<ServerOptions, 'namingFunction'>

  /**
   * 构造函数，初始化 PostHandler 实例。
   * @param store - DataStore 实例，用于存储上传的文件。
   * @param options - 服务器配置选项，包含 namingFunction 等。
   * @throws 如果 namingFunction 不是函数，则抛出错误。
   */
  constructor(store: DataStore, options: ServerOptions) {
    if (options.namingFunction && typeof options.namingFunction !== 'function') {
      throw new Error("'namingFunction' must be a function")
    }

    if (!options.namingFunction) {
      options.namingFunction = Uid.rand
    }

    super(store, options)
  }

  /**
   * 在 DataStore 中创建文件。
   * @param req - HTTP 请求对象。
   * @param res - HTTP 响应对象。
   * @param context - 取消操作的上下文。
   * @returns 返回处理后的 HTTP 响应对象。
   * @throws 如果请求头中包含 'upload-concat' 但 DataStore 不支持 'concatentation' 扩展，则抛出错误。
   * @throws 如果请求头中 'upload-length' 和 'upload-defer-length' 同时存在或同时不存在，则抛出错误。
   * @throws 如果 'upload-metadata' 解析失败，则抛出错误。
   * @throws 如果文件大小超过配置的最大值，则抛出错误。
   */
  async send(
    req: http.IncomingMessage,
    res: http.ServerResponse,
    context: CancellationContext
  ) {
    if ('upload-concat' in req.headers && !this.store.hasExtension('concatentation')) {
      throw ERRORS.UNSUPPORTED_CONCATENATION_EXTENSION
    }

    const upload_length = req.headers['upload-length'] as string | undefined
    const upload_defer_length = req.headers['upload-defer-length'] as string | undefined
    const upload_metadata = req.headers['upload-metadata'] as string | undefined

    if (
      upload_defer_length !== undefined && // 如果扩展不支持，则抛出错误
      !this.store.hasExtension('creation-defer-length')
    ) {
      throw ERRORS.UNSUPPORTED_CREATION_DEFER_LENGTH_EXTENSION
    }

    if ((upload_length === undefined) === (upload_defer_length === undefined)) {
      throw ERRORS.INVALID_LENGTH
    }
   
    let metadata: ReturnType<(typeof Metadata)['parse']> | undefined
    if ('upload-metadata' in req.headers) {
      try {
        metadata = Metadata.parse(upload_metadata)
      } catch {
        throw ERRORS.INVALID_METADATA
      }
    }

    let id: string
    try {
      id = await this.options.namingFunction(req, metadata)
    } catch (error) {
      log('create: check your `namingFunction`. Error', error)
      throw error
    }

    const maxFileSize = await this.getConfiguredMaxSize(req, id)

    if (
      upload_length &&
      maxFileSize > 0 &&
      Number.parseInt(upload_length, 10) > maxFileSize
    ) {
      throw ERRORS.ERR_MAX_SIZE_EXCEEDED
    }

    if (this.options.onIncomingRequest) {
      await this.options.onIncomingRequest(req, res, id)
    }

    const upload = new Upload({
      id,
      size: upload_length ? Number.parseInt(upload_length, 10) : undefined,
      offset: 0,
      metadata,
    })

    if (this.options.onUploadCreate) {
      try {
        const resOrObject = await this.options.onUploadCreate(req, res, upload)
        // 向后兼容，将在下一个主要版本中移除
        // 由于在测试中模拟了实例，因此无法使用 `instanceof` 进行检查
        if (
          typeof (resOrObject as http.ServerResponse).write === 'function' &&
          typeof (resOrObject as http.ServerResponse).writeHead === 'function'
        ) {
          res = resOrObject as http.ServerResponse
        } else {
          // 由于 TS 只理解 instanceof，因此类型定义较为丑陋
          type ExcludeServerResponse<T> = T extends http.ServerResponse ? never : T
          const obj = resOrObject as ExcludeServerResponse<typeof resOrObject>
          res = obj.res
          if (obj.metadata) {
            upload.metadata = obj.metadata
          }
        }
      } catch (error: any) {
        log(`onUploadCreate error: ${error.body}`)
        throw error
      }
    }

    const lock = await this.acquireLock(req, id, context)

    let isFinal: boolean
    let url: string

    // 推荐的响应默认值
    const responseData = {
      status: 201,
      headers: {} as Record<string, string | number>,
      body: '',
    }

    try {
      await this.store.create(upload)
      url = this.generateUrl(req, upload.id)

      this.emit(EVENTS.POST_CREATE, req, res, upload, url)

      isFinal = upload.size === 0 && !upload.sizeIsDeferred

      // 如果请求中包含 Content-Type 头，并且使用了 creation-with-upload 扩展
      if (validateHeader('content-type', req.headers['content-type'])) {
        const bodyMaxSize = await this.calculateMaxBodySize(req, upload, maxFileSize)
        const newOffset = await this.writeToStore(req, upload, bodyMaxSize, context)

        responseData.headers['Upload-Offset'] = newOffset.toString()
        isFinal = newOffset === Number.parseInt(upload_length as string, 10)
        upload.offset = newOffset
      }
    } catch (e) {
      context.abort()
      throw e
    } finally {
      await lock.unlock()
    }
    // 上传完成后的处理逻辑
    if (isFinal && this.options.onUploadFinish) {
      try {
        // 调用自定义的上传完成回调函数，传入请求、响应和上传对象
        // 允许用户自定义上传完成后的处理逻辑
        const resOrObject = await this.options.onUploadFinish(req, res, upload)

        // 兼容性处理：检查返回值是否为 HTTP 响应对象
        // 通过检查对象是否具有 write 和 writeHead 方法来判断
        if (
          typeof (resOrObject as http.ServerResponse).write === 'function' &&
          typeof (resOrObject as http.ServerResponse).writeHead === 'function'
        ) {
          // 如果直接返回 HTTP 响应对象，直接覆盖原响应对象
          res = resOrObject as http.ServerResponse
        } else {
          // 处理自定义返回对象的情况
          // 使用复杂的类型定义排除 ServerResponse 类型
          type ExcludeServerResponse<T> = T extends http.ServerResponse ? never : T

          // 将返回对象转换为非 ServerResponse 类型
          const obj = resOrObject as ExcludeServerResponse<typeof resOrObject>

          // 更新响应对象
          res = obj.res

          // 根据返回对象更新响应状态码
          if (obj.status_code) responseData.status = obj.status_code

          // 更新响应体
          if (obj.body) responseData.body = obj.body

          // 合并响应头，允许覆盖默认头
          if (obj.headers)
            responseData.headers = Object.assign(obj.headers, responseData.headers)
        }
      } catch (error: any) {
        // 记录上传完成回调中的错误
        log(`onUploadFinish: ${error.body}`)

        // 抛出错误，中断上传流程
        throw error
      }
    }


    // Upload-Expires 响应头指示未完成的上传何时过期。
    // 如果在创建时已知过期时间，则必须在响应中包含 Upload-Expires 头
    if (
      this.store.hasExtension('expiration') &&
      this.store.getExpiration() > 0 &&
      upload.creation_date
    ) {
      const created = await this.store.getUpload(upload.id)

      if (created.offset !== Number.parseInt(upload_length as string, 10)) {
        const creation = new Date(upload.creation_date)
        // 值必须为 RFC 7231 日期时间格式
        responseData.headers['Upload-Expires'] = new Date(
          creation.getTime() + this.store.getExpiration()
        ).toUTCString()
      }
    }

    // 仅在最终的 HTTP 状态码为 201 或 3xx 时附加 Location 头
    if (
      responseData.status === 201 ||
      (responseData.status >= 300 && responseData.status < 400)
    ) {
      responseData.headers.Location = url
    }

    const writtenRes = this.write(
      res,
      responseData.status,
      responseData.headers,
      responseData.body
    )

    if (isFinal) {
      this.emit(EVENTS.POST_FINISH, req, writtenRes, upload)
    }

    return writtenRes
  }
}