import { type Logger, type ILogObj } from '../../../lib/logging/index.js'

const nlNlBuffer = Buffer.from([13, 10, 13, 10])
const nlBuffer = Buffer.from([13, 10])

export interface HeadersParseResult {
  complete: boolean
  headers?: Buffer
  remaining?: Buffer
}

/**
 * Parser for HTTP headers
 */
export class HeadersParser {
  private logger: Logger<ILogObj>

  constructor(logger: Logger<ILogObj>) {
    this.logger = logger
  }

  /**
   * Parse HTTP headers from buffer
   * @param data Buffer containing header data
   * @param existingHeaders Previously accumulated headers
   * @returns Headers parse result
   */
  parse(data: Buffer, existingHeaders?: Buffer): HeadersParseResult {
    this.logger.info('Processing headers')

    // Looking for end of headers section
    let index = data.indexOf(nlNlBuffer)
    let padding = 4

    if (index === -1) {
      // It can also be 2x ASCII 10
      const _index = data.indexOf(Buffer.from([10, 10]))
      if (_index !== -1) {
        index = _index
        padding = 2
      }
    }

    // https://github.com/jarrodek/socket-fetch/issues/3
    const enterIndex = data.indexOf(nlBuffer)

    if (index === -1 && enterIndex !== 0) {
      // end in next chunk
      if (!existingHeaders) {
        return {
          complete: false,
          headers: data,
        }
      } else {
        const sum = existingHeaders.length + data.length
        const combined = Buffer.concat([existingHeaders, data], sum)
        return {
          complete: false,
          headers: combined,
        }
      }
    }

    if (enterIndex !== 0) {
      const headersArray = data.slice(0, index)
      if (!existingHeaders) {
        return {
          complete: true,
          headers: headersArray,
          remaining: this.extractRemaining(data, index, padding),
        }
      } else {
        const sum = existingHeaders.length + headersArray.length
        const combined = Buffer.concat([existingHeaders, headersArray], sum)
        return {
          complete: true,
          headers: combined,
          remaining: this.extractRemaining(data, index, padding),
        }
      }
    }

    return {
      complete: true,
      headers: existingHeaders,
      remaining: this.extractRemaining(data, index, padding),
    }
  }

  /**
   * Extract remaining data after headers
   * @param data Original data buffer
   * @param index Index where headers end
   * @param padding Padding to skip
   * @returns Remaining data
   */
  private extractRemaining(data: Buffer, index: number, padding: number): Buffer {
    const start = index === -1 ? 0 : index
    const enterIndex = data.indexOf(nlBuffer)
    const move = enterIndex === 0 ? 2 : padding
    return data.slice(start + move)
  }
}
