import { test } from '@japa/runner'
import { HttpResponse } from '../../../src/models/HttpResponse.js'
import { ErrorResponse, IErrorResponse } from '../../../src/models/ErrorResponse.js'
import { ErrorResponse as LegacyErrorResponse } from '../../../src/models/legacy/request/ArcResponse.js'
import { HttpResponseKind } from '../../../src/models/kinds.js'
import { ISerializedError } from '../../../src/models/SerializableError.js'

test.group('Models > ErrorResponse > Initialization > Default response initialization', () => {
  test('initializes a default project', ({ assert }) => {
    const result = new ErrorResponse()
    assert.equal(result.kind, HttpResponseKind, 'sets the kind property')
    assert.equal(result.status, 0, 'sets the status property')
    assert.equal(result.error.message, 'Unknown error', 'sets the error')
    assert.isUndefined(result.statusText, 'has no statusText property')
    assert.isUndefined(result.headers, 'has no headers property')
    assert.isUndefined(result.payload, 'has no payload property')
  })
})

test.group('Models > ErrorResponse > Initialization > From schema initialization', (group) => {
  let base: IErrorResponse
  group.each.setup(() => {
    base = {
      kind: HttpResponseKind,
      status: 0,
      error: 'test',
    }
  })

  test('sets the kind', ({ assert }) => {
    const init: IErrorResponse = { ...base }
    const response = new ErrorResponse(init)
    assert.equal(response.kind, HttpResponseKind)
  })

  test('sets the status', ({ assert }) => {
    const init: IErrorResponse = { ...base, ...{ status: 200 } }
    const response = new ErrorResponse(init)
    assert.equal(response.status, 200)
  })

  test('sets the error', ({ assert }) => {
    const init: IErrorResponse = { ...base }
    const response = new ErrorResponse(init)
    assert.equal(response.error.message, 'test')
  })

  test('sets the parent properties', ({ assert }) => {
    const init: IErrorResponse = {
      ...base,
      ...{
        status: 200,
        statusText: 'hello',
        headers: 'content-type: test',
        payload: 'test',
      },
    }
    const response = new ErrorResponse(JSON.stringify(init))
    assert.equal(response.status, 200, 'has the status')
    assert.equal(response.statusText, 'hello', 'has the statusText')
    assert.equal(response.headers, 'content-type: test', 'has the headers')
    assert.equal(response.payload, 'test', 'has the payload')
  })
})

test.group('toJSON()', () => {
  test('serializes the kind', ({ assert }) => {
    const response = new ErrorResponse()
    const result = response.toJSON()
    assert.equal(result.kind, HttpResponseKind)
  })

  test('serializes the error', ({ assert }) => {
    const response = ErrorResponse.fromError('test')
    const result = response.toJSON()
    assert.equal((result.error as ISerializedError).message, 'test')
  })

  test('serializes the parent values', ({ assert }) => {
    const init: IErrorResponse = {
      status: 200,
      statusText: 'hello',
      headers: 'content-type: test',
      payload: 'test',
      error: 'test',
    }
    const response = new ErrorResponse(init)
    const result = response.toJSON()
    assert.equal(result.status, 200)
    assert.equal(result.statusText, 'hello')
  })
})

test.group('ErrorResponse.isErrorResponse()', () => {
  test('returns true when is an error response', ({ assert }) => {
    const response = ErrorResponse.fromError('test')
    assert.isTrue(ErrorResponse.isErrorResponse(response.toJSON()))
  })

  test('returns false otherwise', ({ assert }) => {
    const response = HttpResponse.fromValues(200)
    assert.isFalse(ErrorResponse.isErrorResponse(response.toJSON()))
  })
})

test.group('ErrorResponse.fromLegacy()', () => {
  test('sets the kind', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      error: new Error('test'),
      status: 200,
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.kind, HttpResponseKind)
  })

  test('sets the status', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      error: new Error('test'),
      status: 200,
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.status, 200)
  })

  test('sets the default status', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      error: new Error('test'),
      // @ts-expect-error Used in testing
      status: undefined,
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.status, 0)
  })

  test('sets the error', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      error: new Error('test'),
      status: 200,
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.error.message, 'test')
  })

  test('sets the default error', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      // @ts-expect-error Used in testing
      error: undefined,
      status: 100,
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.error.message, 'Unknown error')
  })

  test('sets the headers', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      error: new Error('test'),
      status: 200,
      headers: 'x-test: true',
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.headers, 'x-test: true')
  })

  test('sets the statusText', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      error: new Error('test'),
      status: 200,
      statusText: 'Super OK',
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.statusText, 'Super OK')
  })

  test('sets the payload from a string', async ({ assert }) => {
    const init: LegacyErrorResponse = {
      error: new Error('test'),
      status: 200,
      payload: 'I am an error',
    }
    const response = await ErrorResponse.fromLegacy(init)
    assert.equal(response.payload, 'I am an error')
  })
})
