import { test } from '@japa/runner'
import zlib from 'zlib'
import { IResponse, Response, createLogger, CoreEngine, IHttpRequest, Headers } from '../../../../src/index.js'

const logger = createLogger()

test.group('Compression test', () => {
  test('{i} reads the compressed response')
    .with([
      ['brotli', `http://localhost:PORT/v1/compression/brotli`, 'br'],
      ['deflate', `http://localhost:PORT/v1/compression/deflate`, 'deflate'],
      ['gzip', `http://localhost:PORT/v1/compression/gzip`, 'gzip'],
    ])
    .run(async ({ assert, httpConfig }, [, sourceUrl, enc]) => {
      const url = sourceUrl.replace('PORT', String(httpConfig.httpPort))
      const engine = new CoreEngine(
        {
          url,
          method: 'GET',
          headers: `accept-encoding: ${enc}`,
        },
        { logger }
      )
      const info = await engine.send()
      const response = new Response(info.response as IResponse)

      assert.ok(response.payload, 'has the payload')

      const headers = new Headers(response.headers)
      assert.equal(headers.get('content-encoding'), enc, 'has the content-encoding in the response')

      const payload = (await response.readPayload('buffer')) as Buffer
      const body = payload.toString()
      const data = JSON.parse(body)
      assert.typeOf(data, 'array', 'has the response body')
    })
})

function createDeflate(str?: string): Buffer {
  return zlib.deflateSync(Buffer.from(str || 'deflate-string'))
}

function createGzip(str?: string): Buffer {
  return zlib.gzipSync(Buffer.from(str || 'gzip-string'))
}

function createBrotli(str?: string): Buffer {
  return zlib.brotliCompressSync(Buffer.from(str || 'brotli-string'))
}

test.group('decompress()', (group) => {
  let request: IHttpRequest

  group.each.setup(() => {
    request = {
      url: 'https://domain.com',
      method: 'GET',
    }
  })

  test('returns undefined when no data', async ({ assert }) => {
    const engine = new CoreEngine(request)
    const result = await engine.decompress(undefined)
    assert.isUndefined(result)
  }).tags(['@http-engine', '@compression'])

  test('returns undefined when aborted', async ({ assert }) => {
    const engine = new CoreEngine(request)
    engine.aborted = true
    const result = await engine.decompress(Buffer.from('test'))
    assert.isUndefined(result)
  }).tags(['@http-engine', '@compression'])

  test('returns the same buffer when no content-encoding header', async ({ assert }) => {
    const b = Buffer.from('test')
    const engine = new CoreEngine(request)
    engine.currentHeaders = new Headers()
    engine.currentResponse = Response.fromValues(200)
    engine.currentResponse.loadingTime = 1
    const result = (await engine.decompress(b)) as Buffer
    assert.equal(result.compare(b), 0)
  }).tags(['@http-engine', '@compression'])

  test('decompresses deflate', async ({ assert }) => {
    const b = createDeflate()
    const engine = new CoreEngine(request)
    engine.currentHeaders = new Headers('content-encoding: deflate')
    engine.currentResponse = Response.fromValues(200)
    engine.currentResponse.loadingTime = 1
    const result = (await engine.decompress(b)) as Buffer
    assert.equal(result.toString(), 'deflate-string')
  }).tags(['@http-engine', '@compression'])

  test('decompresses gzip', async ({ assert }) => {
    const b = createGzip()
    const engine = new CoreEngine(request)
    engine.currentHeaders = new Headers('content-encoding: gzip')
    engine.currentResponse = Response.fromValues(200)
    engine.currentResponse.loadingTime = 1
    const result = (await engine.decompress(b)) as Buffer
    assert.equal(result.toString(), 'gzip-string')
  }).tags(['@http-engine', '@compression'])

  test('decompresses brotli', async ({ assert }) => {
    const b = createBrotli()
    const engine = new CoreEngine(request)
    engine.currentHeaders = new Headers('content-encoding: br')
    engine.currentResponse = Response.fromValues(200)
    engine.currentResponse.loadingTime = 1
    const result = (await engine.decompress(b)) as Buffer
    assert.equal(result.toString(), 'brotli-string')
  }).tags(['@http-engine', '@compression'])
})
