import { test } from '@japa/runner'
import { RequestLog, IRequestLog, Kind as RequestLogKind } from '../../../src/models/RequestLog.js'
import { ISentRequest, SentRequest } from '../../../src/models/SentRequest.js'
import { IResponse, Response } from '../../../src/models/Response.js'
import {
  IResponseRedirect,
  Kind as HttpResponseRedirectKind,
  ResponseRedirect,
} from '../../../src/models/ResponseRedirect.js'
import { ResponseRedirect as LegacyRedirect } from '../../../src/models/legacy/request/ArcResponse.js'

test.group('Models > RequestLog > RequestLog.fromRequest()', (group) => {
  let request: ISentRequest
  group.each.setup(() => {
    request = {
      startTime: 1234,
      endTime: 5678,
      url: 'https://dot.com',
    }
  })

  test('sets the kind', ({ assert }) => {
    const result = RequestLog.fromRequest(request)
    assert.equal(result.kind, RequestLogKind)
  })

  test('sets the request', ({ assert }) => {
    const result = RequestLog.fromRequest(request)
    assert.instanceOf(result.request, SentRequest)
    assert.equal(result.request!.url, request.url)
  })
})

test.group('Models > RequestLog > RequestLog.fromRequestResponse()', (group) => {
  let request: ISentRequest
  let response: IResponse
  group.each.setup(() => {
    request = {
      startTime: 1234,
      endTime: 5678,
      url: 'https://dot.com',
    }
    response = {
      loadingTime: 1,
      status: 200,
    }
  })

  test('sets the kind', ({ assert }) => {
    const result = RequestLog.fromRequestResponse(request, response)
    assert.equal(result.kind, RequestLogKind)
  })

  test('sets the request', ({ assert }) => {
    const result = RequestLog.fromRequestResponse(request, response)
    assert.instanceOf(result.request, SentRequest)
    assert.equal(result.request!.url, request.url)
  })

  test('sets the response', ({ assert }) => {
    const result = RequestLog.fromRequestResponse(request, response)
    assert.instanceOf(result.response, Response)
    assert.equal(result.response!.status, response.status)
  })
})

test.group('Models > RequestLog > constructor()', () => {
  test('creates default values', ({ assert }) => {
    const result = new RequestLog()
    assert.equal(result.kind, RequestLogKind)
    assert.isUndefined(result.request)
    assert.isUndefined(result.response)
    assert.isUndefined(result.redirects)
    assert.isUndefined(result.size)
    assert.isUndefined(result.assertions)
  })

  test('creates values from the schema', ({ assert }) => {
    const schema: IRequestLog = {
      kind: RequestLogKind,
      request: {
        startTime: 1234,
        endTime: 5678,
        url: 'https://dot.com',
      },
      response: {
        loadingTime: 1,
        status: 200,
      },
      redirects: [
        {
          startTime: 1234,
          endTime: 5678,
          url: 'https://dot.com',
          response: {
            status: 200,
          },
          kind: HttpResponseRedirectKind,
        },
      ],
      size: {
        request: 123,
        response: 456,
      },
      assertions: [
        {
          description: 't1',
          ok: true,
        },
      ],
    }
    const result = new RequestLog(schema)
    assert.equal(result.kind, RequestLogKind)
    assert.instanceOf(result.request, SentRequest)
    assert.equal(result.request!.url, 'https://dot.com')
    assert.instanceOf(result.response, Response)
    assert.equal(result.response!.status, 200)
    assert.isArray(result.redirects)
    assert.lengthOf(result.redirects!, 1)
    assert.instanceOf(result.redirects![0], ResponseRedirect)
    assert.isObject(result.size)
    assert.equal(result.size!.request, 123)
    assert.deepEqual(result.assertions, schema.assertions)
  })

  test('creates values from the JSON schema string', ({ assert }) => {
    const schema: IRequestLog = {
      kind: RequestLogKind,
      request: {
        startTime: 1234,
        endTime: 5678,
        url: 'https://dot.com',
      },
      response: {
        loadingTime: 1,
        status: 200,
      },
      assertions: [
        {
          description: 't1',
          ok: true,
        },
      ],
    }
    const result = new RequestLog(JSON.stringify(schema))
    assert.equal(result.kind, RequestLogKind)
    assert.instanceOf(result.request, SentRequest)
    assert.equal(result.request!.url, 'https://dot.com')
    assert.deepEqual(result.assertions, schema.assertions)
  })
})

test.group('Models > RequestLog > toJSON()', (group) => {
  let schema: IRequestLog
  let instance: RequestLog
  group.each.setup(() => {
    // Constructing with full objects to ensure instances are created for toJSON
    instance = new RequestLog({
      kind: RequestLogKind,
      request: new SentRequest({ startTime: 1234, endTime: 5678, url: 'https://dot.com' }).toJSON(),
      response: new Response({ loadingTime: 1, status: 200 }).toJSON(),
      redirects: [
        new ResponseRedirect({
          startTime: 1234,
          endTime: 5678,
          url: 'https://dot.com',
          response: new Response({ status: 200, loadingTime: 0 }).toJSON(),
          kind: HttpResponseRedirectKind,
        }).toJSON(),
      ],
      size: { request: 123, response: 456 },
      assertions: [{ description: 't1', ok: true }],
    })
    // schema is what we expect after toJSON()
    schema = instance.toJSON()
  })

  test('serializes the kind', ({ assert }) => {
    const result = instance.toJSON()
    assert.equal(result.kind, RequestLogKind)
  })

  test('serializes the request', ({ assert }) => {
    const result = instance.toJSON()
    assert.deepEqual(result.request, schema.request)
  })

  test('does not serialize the request when missing', ({ assert }) => {
    delete instance.request
    const result = instance.toJSON()
    assert.isUndefined(result.request)
  })

  test('serializes the size', ({ assert }) => {
    const result = instance.toJSON()
    assert.deepEqual(result.size, schema.size)
  })

  test('does not serialize the size when missing', ({ assert }) => {
    delete instance.size
    const result = instance.toJSON()
    assert.isUndefined(result.size)
  })

  test('serializes the response', ({ assert }) => {
    const result = instance.toJSON()
    assert.deepEqual(result.response, schema.response)
  })

  test('does not serialize the response when missing', ({ assert }) => {
    delete instance.response
    const result = instance.toJSON()
    assert.isUndefined(result.response)
  })

  test('serializes the redirects', ({ assert }) => {
    const result = instance.toJSON()
    assert.deepEqual(result.redirects, schema.redirects)
  })

  test('does not serialize the redirects when missing', ({ assert }) => {
    delete instance.redirects
    const result = instance.toJSON()
    assert.isUndefined(result.redirects)
  })

  test('serializes the assertions', ({ assert }) => {
    const result = instance.toJSON()
    assert.deepEqual(result.assertions, schema.assertions)
  })

  test('does not serialize the assertions when missing', ({ assert }) => {
    delete instance.assertions
    const result = instance.toJSON()
    assert.isUndefined(result.assertions)
  })
})

test.group('Models > RequestLog > addRedirect()', (group) => {
  let instance: RequestLog
  let redirectSchema: IResponseRedirect // Use IResponseRedirect for input
  group.each.setup(() => {
    instance = new RequestLog()
    redirectSchema = {
      // This is the input to addRedirect
      startTime: 1234,
      endTime: 5678,
      url: 'https://dot.com',
      response: { status: 200 }, // This will be converted to a Response instance
      kind: HttpResponseRedirectKind,
    }
  })

  test('adds a redirect and initializes the list', ({ assert }) => {
    instance.addRedirect(redirectSchema)
    assert.isArray(instance.redirects)
    assert.lengthOf(instance.redirects!, 1)
    assert.instanceOf(instance.redirects![0], ResponseRedirect)
  })

  test('returns the created redirect', ({ assert }) => {
    const result = instance.addRedirect(redirectSchema)
    assert.instanceOf(result, ResponseRedirect)
    assert.isTrue(result === instance.redirects![0])
    assert.equal(result.url, 'https://dot.com')
  })

  test('adds a redirect to already initialized list', ({ assert }) => {
    instance.addRedirect(redirectSchema)
    instance.addRedirect(redirectSchema)
    assert.lengthOf(instance.redirects!, 2)
  })
})

test.group('Models > RequestLog > addLegacyRedirect()', (group) => {
  let instance: RequestLog
  let legacyRedirectSchema: LegacyRedirect
  group.each.setup(() => {
    instance = new RequestLog()
    legacyRedirectSchema = {
      startTime: 1234,
      endTime: 5678,
      url: 'https://dot.com',
      response: {
        status: 200,
      },
    }
  })

  test('adds a redirect and initializes the list', async ({ assert }) => {
    await instance.addLegacyRedirect(legacyRedirectSchema)
    assert.isArray(instance.redirects)
    assert.lengthOf(instance.redirects!, 1)
    assert.instanceOf(instance.redirects![0], ResponseRedirect)
  })

  test('returns the created redirect', async ({ assert }) => {
    const result = await instance.addLegacyRedirect(legacyRedirectSchema)
    assert.instanceOf(result, ResponseRedirect)
    assert.equal(result.url, 'https://dot.com')
  })

  test('adds a redirect to already initialized list', async ({ assert }) => {
    await instance.addLegacyRedirect(legacyRedirectSchema)
    await instance.addLegacyRedirect(legacyRedirectSchema)
    assert.lengthOf(instance.redirects!, 2)
  })
})
