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

const logger = createLogger()

test.group('Request size', () => {
  test('has the request size value', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/v1/headers`,
      method: 'GET',
      headers: 'x-test: true\naccept: application/json',
    }
    const opts: HttpEngineOptions = {
      logger,
    }
    const engine = new CoreEngine(request, opts)
    const log = await engine.send()
    const size = log.size as IRequestsSize
    assert.equal(size.request, 91)
  }).tags(['@http-engine', '@response'])

  test('has the response size value', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/v1/headers`,
      method: 'GET',
      headers: 'x-test: true\naccept: application/json',
    }
    const opts: HttpEngineOptions = {
      logger,
    }
    const engine = new CoreEngine(request, opts)
    const log = await engine.send()
    const size = log.size as IRequestsSize
    assert.equal(size.response, 82)
  }).tags(['@http-engine', '@response'])
})

test.group('Responses test', () => {
  ;[
    ['Image - jpeg', `http://localhost:PORT/v1/image/jpeg`, 'image/jpeg'],
    ['Image - png', `http://localhost:PORT/v1/image/png`, 'image/png'],
    ['Image - svg', `http://localhost:PORT/v1/image/svg`, 'image/svg+xml'],
    ['Image - webp', `http://localhost:PORT/v1/image/webp`, 'image/webp'],
    ['html', `http://localhost:PORT/v1/response/html`, 'text/html; charset=UTF-8'],
    ['json', `http://localhost:PORT/v1/response/json`, 'application/json'],
    ['xml', `http://localhost:PORT/v1/response/xml`, 'application/xml'],
    ['Bytes', `http://localhost:PORT/v1/response/bytes/120`, 'application/octet-stream'],
  ].forEach((item) => {
    const [name, sourceUrl, mime] = item
    test(`reads the response: ${name}`, async ({ assert, httpConfig }) => {
      const url = sourceUrl.replace('PORT', String(httpConfig.httpPort))
      const engine = new CoreEngine(
        {
          url,
          method: 'GET',
        },
        { logger }
      )
      const info = await engine.send()
      const response = new Response(info.response as IResponse)

      const headers = new Headers(response.headers)
      assert.equal(headers.get('content-type'), mime, 'has the content type')
      const payload = (await response.readPayload('buffer')) as Buffer

      const { length } = payload
      assert.equal(headers.get('content-length'), String(length))
    }).tags(['@http-engine', '@response'])
  })
})

test.group('Chunked responses', (group) => {
  let request: IHttpRequest
  let opts: HttpEngineOptions

  group.each.setup((ctx) => {
    request = {
      url: `http://localhost:${ctx.context.httpConfig.httpPort}/v1/stream`,
      method: 'GET',
      headers: 'Host: test.com\nContent-Length: 0',
      payload: 'abc',
    }
    opts = {
      timeout: 50000,
      followRedirects: false,
      logger,
    }
  })

  test('receives chunked response.', async ({ assert }) => {
    const engine = new CoreEngine(request, opts)
    const info = await engine.send()
    const response = new Response(info.response as IResponse)
    const payload = (await response.readPayload('buffer')) as Buffer

    const parts = payload.toString().split('\n')
    assert.lengthOf(parts, 6)
    for (let i = 0; i < 5; i++) {
      assert.equal(parts[i].length, 128)
    }
  }).tags(['@http-engine', '@response', '@chunked'])
})

// test.group('readChunkSize()', (group) => {
//   let engine: CoreEngine
//   let request: IHttpRequest
//   let opts: HttpEngineOptions

//   group.each.setup((ctx) => {
//     request = {
//       url: `http://localhost:${ctx.context.httpConfig.httpPort}/v1/stream`,
//       method: 'GET',
//       headers: 'Host: test.com\nContent-Length: 0',
//       payload: 'abc',
//     }
//     opts = {
//       timeout: 50000,
//       followRedirects: false,
//       logger,
//     }
//     engine = new CoreEngine(request, opts)
//   })

//   test('returns the the same array when new line is not found', ({ assert }) => {
//     const b = Buffer.from('test')
//     const result = engine.readChunkSize(b)
//     assert.isTrue(b === result)
//   }).tags(['@http-engine', '@response', '@chunked'])

//   test('does not set chunkSize property', ({ assert }) => {
//     const b = Buffer.from('test')
//     engine.readChunkSize(b)
//     assert.isUndefined(engine.responseInfo.chunkSize)
//   }).tags(['@http-engine', '@response', '@chunked'])

//   test('parses chunk size', ({ assert }) => {
//     let chunk = Number(128).toString(16)
//     chunk += '\r\ntest'
//     const b = Buffer.from(chunk)
//     engine.readChunkSize(b)
//     assert.equal(engine.responseInfo.chunkSize, 128)
//   }).tags(['@http-engine', '@response', '@chunked'])

//   test('truncates the buffer', ({ assert }) => {
//     let chunk = Number(128).toString(16)
//     chunk += '\r\ntest'
//     const b = Buffer.from(chunk)
//     const result = engine.readChunkSize(b) as Buffer
//     assert.equal(result.toString(), 'test')
//   }).tags(['@http-engine', '@response', '@chunked'])
// })

// test.group('_processBodyChunked()', (group) => {
//   let engine: CoreEngine
//   let request: IHttpRequest
//   let opts: HttpEngineOptions

//   group.each.setup((ctx) => {
//     request = {
//       url: `http://localhost:${ctx.context.httpConfig.httpPort}/v1/stream`,
//       method: 'GET',
//       headers: 'Host: test.com\nContent-Length: 0',
//       payload: 'abc',
//     }
//     opts = {
//       timeout: 50000,
//       followRedirects: false,
//       logger,
//     }
//     engine = new CoreEngine(request, opts)
//     engine._reportResponse = (): void => {}
//   })

//   test('reads body chunk', ({ assert }) => {
//     let chunk = Number(4).toString(16)
//     chunk += '\r\ntest\r\n0\r\n'
//     const b = Buffer.from(chunk)
//     engine._processBodyChunked(b)
//     assert.equal(engine.responseInfo.chunkSize, 0)
//     assert.equal((engine._rawBody as Buffer).toString(), 'test')
//   }).tags(['@http-engine', '@response', '@chunked'])

//   test('Reads multi chunks', ({ assert }) => {
//     let chunk = Number(6).toString(16)
//     chunk += '\r\ntest\r\n\r\n'
//     chunk += Number(8).toString(16)
//     chunk += '\r\ntest1234\r\n'
//     chunk += '0\r\n'
//     const b = Buffer.from(chunk)
//     engine._processBodyChunked(b)
//     assert.equal(engine.responseInfo.chunkSize, 0)
//     assert.equal((engine._rawBody as Buffer).toString(), 'test\r\ntest1234')
//   }).tags(['@http-engine', '@response', '@chunked'])

//   test('Reads multi chunks with partial buffer', ({ assert }) => {
//     let chunk = Number(6).toString(16)
//     chunk += '\r\nte'
//     engine._processBodyChunked(Buffer.from(chunk))
//     chunk = 'st\r\n\r\n'
//     engine._processBodyChunked(Buffer.from(chunk))
//     chunk = Number(8).toString(16)
//     chunk += '\r\ntest'
//     engine._processBodyChunked(Buffer.from(chunk))
//     chunk = '1234\r\n0\r\n'
//     engine._processBodyChunked(Buffer.from(chunk))
//     assert.equal(engine.responseInfo.chunkSize, 0)
//     assert.equal((engine._rawBody as Buffer).toString(), 'test\r\ntest1234')
//   }).tags(['@http-engine', '@response', '@chunked'])
// })

// test.group('_processBody()', (group) => {
//   const testData = Buffer.from('abcdefghijklmn')
//   const testLength = testData.length
//   let engine: CoreEngine
//   let request: IHttpRequest
//   let opts: HttpEngineOptions

//   group.each.setup((ctx) => {
//     request = {
//       url: `http://localhost:${ctx.context.httpConfig.httpPort}/v1/stream`,
//       method: 'GET',
//       headers: 'Host: test.com\nContent-Length: 0',
//       payload: 'abc',
//     }
//     opts = {
//       timeout: 50000,
//       followRedirects: false,
//       logger,
//     }
//     engine = new CoreEngine(request, opts)
//     engine._reportResponse = (): void => {}
//   })

//   test('sets the responseInfo.body property', ({ assert }) => {
//     engine.responseInfo.contentLength = testLength + 1
//     engine.responseInfo.chunked = false
//     engine._processBody(testData)
//     assert.equal((engine.responseInfo.body as Buffer).toString(), testData.toString())
//   }).tags(['@http-engine', '@response'])

//   test('does not call _reportResponse when length is higher than data', ({ assert }) => {
//     engine.responseInfo.contentLength = testLength + 1
//     let called = false
//     engine._reportResponse = (): void => {
//       called = true
//     }
//     engine._processBody(testData)
//     assert.isFalse(called)
//   }).tags(['@http-engine', '@response'])

//   test('reports response when the data is read as whole on one socket buffer', ({ assert }) => {
//     engine.responseInfo.contentLength = testLength
//     let called = false
//     engine._reportResponse = (): void => {
//       called = true
//     }
//     engine._processBody(testData)
//     assert.isTrue(called, '_reportResponse was called')
//   }).tags(['@http-engine', '@response'])

//   test('reports response after more calls', ({ assert }) => {
//     engine.responseInfo.contentLength = testLength
//     let called = false
//     engine._reportResponse = (): void => {
//       called = true
//     }
//     engine._processBody(Buffer.from('abcdef'))
//     engine._processBody(Buffer.from('ghijklmn'))
//     assert.isTrue(called, '_reportResponse was called')
//   }).tags(['@http-engine', '@response'])
// })
