import { test } from '@japa/runner'
import net from 'net'
import {
  createLogger,
  CoreEngine,
  IHttpRequest,
  HttpEngineOptions,
  IErrorResponse,
  ISerializedError,
} from '../../../../src/index.js'

const logger = createLogger()

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

  group.each.setup((ctx) => {
    request = {
      url: `http://localhost:${ctx.context.httpConfig.httpPort}/v1/headers`,
      method: 'GET',
    }
    opts = {
      logger,
    }
  })

  function setupSocket(base: CoreEngine, httpPort: number): Promise<void> {
    return new Promise((resolve, reject) => {
      const socket = new net.Socket({
        writable: true,
      })
      socket.connect(httpPort, 'localhost', () => {
        base.socket = socket
        resolve()
      })
      socket.on('error', () => {
        reject(new Error('Unable to connect'))
      })
    })
  }

  test('sets aborted flag', ({ assert }) => {
    const base = new CoreEngine(request, opts)
    base.abort()
    assert.isTrue(base.aborted)
  }).tags(['@http-engine', '@abort'])

  test('destroys the socket', async ({ assert, httpConfig }) => {
    const base = new CoreEngine(request, opts)
    await setupSocket(base, httpConfig.httpPort)
    base.abort()
    assert.isUndefined(base.socket)
  }).tags(['@http-engine', '@abort'])

  test('removes destroyed socket', async ({ assert, httpConfig }) => {
    const base = new CoreEngine(request, opts)
    await setupSocket(base, httpConfig.httpPort)
    const soc = base.socket as net.Socket
    soc.pause()
    soc.destroy()
    base.abort()
    assert.isUndefined(base.socket)
  }).tags(['@http-engine', '@abort'])

  test('decompress() results to undefined', async ({ assert }) => {
    const base = new CoreEngine(request, opts)
    base.abort()
    const result = await base.decompress(Buffer.from('test'))
    assert.isUndefined(result)
  }).tags(['@http-engine', '@abort'])

  test('_createResponse() throws an error', async ({ assert }) => {
    const base = new CoreEngine(request, opts)
    base.abort()
    await assert.rejects(() => base._createResponse(), 'Request aborted.')
  }).tags(['@http-engine', '@abort'])

  test('aborts the request with a signal', async ({ assert }) => {
    const ctrl = new AbortController()
    const base = new CoreEngine(request, { ...opts, signal: ctrl.signal })
    const p = base.send()
    ctrl.abort()
    const result = await p
    const response = result.response as IErrorResponse
    assert.equal(response.status, 0)
    assert.equal((response.error as ISerializedError).message, 'Request aborted')
  }).tags(['@http-engine', '@abort'])
})
