import { test } from '@japa/runner'
import { Request, IRequest, Kind as RequestKind, updatedSymbol, midnightSymbol } from '../../../src/models/Request.js'
import { HttpRequest, IHttpRequest, Kind as HttpRequestKind } from '../../../src/models/HttpRequest.js'
import { ErrorResponse } from '../../../src/models/ErrorResponse.js'
import { RequestLog, Kind as RequestLogKind } from '../../../src/models/RequestLog.js'
import { RequestConfig, Kind as RequestConfigKind } from '../../../src/models/RequestConfig.js'
import { RequestAuthorization, Kind as RequestAuthorizationKind } from '../../../src/models/RequestAuthorization.js'
import { ARCSavedRequest } from '../../../src/models/legacy/request/ArcRequest.js'
import { ISafePayload } from '../../../src/lib/transformers/PayloadSerializer.js'
import { Certificate, Kind as CertificateKind, IP12Certificate } from '../../../src/models/ClientCertificate.js'
import { SetCookieConfig } from '../../../src/models/legacy/actions/Actions.js'
import { FlowRequestDataEnum, FlowSourceEnum } from '../../../src/models/http-flows/HttpFlows.js'
import { ReadDataStep } from '../../../src/models/http-flows/steps/data/ReadDataStep.js'
import { HttpResponseKind, ThingKind } from '../../../src/models/kinds.js'

test.group('Models > Request > Request.fromUrl()', () => {
  const url = 'https://dot.com'

  test('sets the request data', ({ assert }) => {
    const result = Request.fromUrl(url)
    assert.equal(result.kind, RequestKind)
    assert.typeOf(result.created, 'number')
    assert.typeOf(result.updated, 'number')
    const { expects, info } = result
    assert.typeOf(expects, 'object', 'sets the expects')
    assert.ok(info, 'sets the info')

    assert.equal(expects.method, 'GET')
    assert.equal(expects.url, url)
    assert.equal(info.name, url)
  })
})

test.group('Models > Request > Request.fromName()', () => {
  const name = 'a name'

  test('sets the request data', ({ assert }) => {
    const result = Request.fromName(name)
    assert.equal(result.kind, RequestKind)
    assert.typeOf(result.created, 'number')
    assert.typeOf(result.updated, 'number')
    const { expects, info } = result
    assert.typeOf(expects, 'object', 'sets the expects')
    assert.ok(info, 'sets the info')

    assert.equal(expects.method, 'GET')
    assert.equal(expects.url, '')
    assert.equal(info.name, name)
  })
})

test.group('Models > Request > Request.fromHttpRequest()', () => {
  test('sets the request data', ({ assert }) => {
    const schema: IHttpRequest = {
      kind: HttpRequestKind,
      url: 'https://dot.com',
      headers: 'a: b',
      method: 'PUT',
      payload: 'test',
    }
    const result = Request.fromHttpRequest(schema)

    assert.equal(result.kind, RequestKind)
    assert.typeOf(result.created, 'number')
    assert.typeOf(result.updated, 'number')
    const { expects, info } = result
    assert.typeOf(expects, 'object', 'sets the expects')
    assert.typeOf(info, 'EventTarget', 'sets the info')

    assert.equal(expects.url, schema.url)
    assert.equal(expects.method, schema.method)
    assert.equal(expects.headers, schema.headers)
    assert.equal(expects.payload, schema.payload)
    assert.equal(info.name, schema.url)
  })
})

test.group('Models > Request > Request.fromLegacy()', () => {
  test('throws when unknown object', async ({ assert }) => {
    let thrown = false
    try {
      // @ts-expect-error Used in testing
      await Request.fromLegacy(undefined)
    } catch {
      thrown = true
    }
    assert.isTrue(thrown)
  })

  test('sets the created and updated', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      created: 1234567890,
      updated: 987654321,
    })
    assert.equal(instance.created, 1234567890)
    assert.equal(instance.updated, 987654321)
  })

  test('sets the default created', async ({ assert }) => {
    const now = Date.now()
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
    })
    assert.approximately(instance.created, now, 100)
  })

  test('sets the default updated', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
    })
    assert.equal(instance.updated, instance.created)
  })

  test('sets the expects object', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      headers: 'x-a: b',
      payload: 'test-payload',
    })
    const { expects } = instance
    assert.equal(expects.method, 'PUT')
    assert.equal(expects.url, 'https://dot.com')
    assert.equal(expects.headers, 'x-a: b')
    assert.equal(expects.payload, 'test-payload')
  })

  test('sets the info object', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
    })
    const { info } = instance
    assert.equal(info.name, 'test')
  })

  test('sets the payload from a Blob', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      blob: 'test',
    })
    const { expects } = instance
    assert.ok(expects.payload, 'has the payload')
    const payload = expects.payload as ISafePayload
    assert.equal(payload.type, 'blob')
    assert.deepEqual(payload.data, 'test')
  })

  test('sets the payload from a multipart', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      multipart: [
        {
          name: 'a',
          isFile: false,
          value: 'b',
        },
      ],
    })
    const { expects } = instance
    assert.ok(expects.payload, 'has the payload')
    const payload = expects.payload as ISafePayload
    assert.equal(payload.type, 'formdata')
    assert.deepEqual(payload.data, [
      {
        name: 'a',
        isFile: false,
        value: 'b',
      },
    ])
  })

  test('sets the payload from an ArrayBuffer', async ({ assert }) => {
    const encoder = new TextEncoder()
    const view = encoder.encode('test')

    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      payload: Uint8Array.from(view).buffer,
    })
    const { expects } = instance
    assert.ok(expects.payload, 'has the payload')
    const payload = expects.payload as ISafePayload
    assert.equal(payload.type, 'arraybuffer')
    assert.deepEqual(payload.data, [116, 101, 115, 116])
  })

  test('translates the legacy actions object', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      actions: {
        request: [
          {
            condition: {
              source: 'body',
              alwaysPass: true,
              path: 'a.b.c',
              type: 'request',
            },
            actions: [
              {
                priority: 0,
                type: 'request',
                name: 'set-cookie',
                config: {
                  source: {
                    source: 'body',
                    type: 'request',
                    path: 'abc',
                  },
                  name: 'A name',
                } as SetCookieConfig,
              },
            ],
            enabled: true,
            type: 'request',
          },
        ],
        response: [
          {
            condition: {
              source: 'body',
              alwaysPass: true,
              path: 'a.b.c',
              type: 'response',
            },
            actions: [
              {
                priority: 0,
                type: 'response',
                config: {
                  source: {
                    source: 'body',
                    type: 'request',
                    path: 'abc',
                  },
                  name: 'A name',
                } as SetCookieConfig,
                name: 'set-cookie',
              },
            ],
            enabled: true,
            type: 'response',
          },
        ],
      },
    })
    const { flows } = instance
    assert.ok(flows, 'has flows')

    assert.lengthOf(flows!, 2, 'has two flows')
    const [f1, f2] = flows!

    assert.equal(f1.trigger, 'request', 'has the request flow')
    assert.equal(f2.trigger, 'response', 'has the response flow')
  })

  test('translates the config object', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      config: {
        enabled: true,
        defaultHeaders: true,
        followRedirects: true,
        hosts: [],
        ignoreSessionCookies: false,
        nativeTransport: true,
        timeout: 500,
        validateCertificates: true,
        variables: [],
      },
    })
    const { config } = instance

    assert.typeOf(config, 'object', 'has the UI definition')
    assert.equal(config!.kind, RequestConfigKind)

    assert.isTrue(config!.enabled)
    assert.isTrue(config!.followRedirects)
    assert.isTrue(config!.validateCertificates)
    assert.isTrue(config!.defaultHeaders)
    assert.isFalse(config!.ignoreSessionCookies)
    assert.equal(config!.timeout, 500)
    assert.typeOf(config!.hosts, 'array')
    assert.typeOf(config!.variables, 'array')
  })

  test('translates the authorization', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      authorization: [
        {
          enabled: true,
          type: 'oauth 2',
          valid: true,
          config: {},
        },
      ],
    })

    const { authorization } = instance
    assert.typeOf(authorization, 'array')
    const [info] = authorization!
    assert.equal(info.enabled, true)
  })

  test('creates the log with the request data', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      transportRequest: {
        endTime: 123456,
        httpMessage: 'test message',
        method: 'PUT',
        startTime: 789456,
        url: 'https://dot.com',
        headers: 'x-a: b',
        payload: 'test-payload',
      },
    })
    const { log } = instance
    assert.ok(log, 'has the log')
    assert.equal(log!.kind, RequestLogKind)
    assert.ok(log!.request, 'has the log.request')
    assert.notOk(log!.response, 'has no log.response')
    assert.notOk(log!.redirects, 'has no log.redirects')
    assert.equal(log!.request!.kind, HttpRequestKind)
  })

  test('creates the log with the response data', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      response: {
        loadingTime: 1,
        status: 200,
      },
    })
    const { log } = instance
    assert.ok(log, 'has the log')
    assert.equal(log!.kind, RequestLogKind)
    assert.ok(log!.response, 'has the log.response')
    assert.notOk(log!.request, 'has no log.request')
    assert.notOk(log!.redirects, 'has no log.redirects')
    assert.equal(log!.response!.kind, HttpResponseKind)
    assert.equal(log!.response!.status, 200)
  })

  test('creates the log with an error response data', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      response: {
        loadingTime: 1,
        status: 0,
        error: new Error('test'),
      },
    })
    const { log } = instance
    assert.ok(log, 'has the log')
    assert.equal(log!.kind, RequestLogKind)
    assert.ok(log!.response, 'has the log.response')
    assert.notOk(log!.request, 'has no log.request')
    assert.notOk(log!.redirects, 'has no log.redirects')
    assert.equal(log!.response!.kind, HttpResponseKind)
    assert.equal(log!.response!.status, 0)
    const err = log!.response as ErrorResponse
    assert.equal(err.error.message, 'test')
  })

  test('creates the log with redirects data', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      response: {
        loadingTime: 1,
        status: 200,
        redirects: [
          {
            endTime: 12345,
            response: {
              status: 307,
            },
            startTime: 9876,
            url: 'https://rdr.com',
            timings: {
              blocked: 1,
              connect: 2,
              dns: 3,
              receive: 4,
              send: 5,
              wait: 6,
            },
          },
        ],
      },
    })
    const { log } = instance
    assert.ok(log, 'has the log')
    const { redirects } = log!
    assert.typeOf(redirects, 'array')
    assert.lengthOf(redirects!, 1)
  })

  test('creates the log with size data', async ({ assert }) => {
    const instance = await Request.fromLegacy({
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
      response: {
        loadingTime: 1,
        status: 200,
        size: {
          request: 200,
          response: 400,
        },
      },
    })
    const { log } = instance
    assert.ok(log, 'has the log')
    const { size } = log!
    assert.typeOf(size, 'object')
    assert.equal(size!.request, 200)
    assert.equal(size!.response, 400)
  })

  test('sets the default HTTP method', async ({ assert }) => {
    const info: ARCSavedRequest = {
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
    }
    // @ts-expect-error Used in testing
    delete info.method
    const instance = await Request.fromLegacy(info)
    assert.equal(instance.expects.method, 'GET')
  })

  test('sets the default request url', async ({ assert }) => {
    const info: ARCSavedRequest = {
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
    }
    // @ts-expect-error Used in testing
    delete info.url
    const instance = await Request.fromLegacy(info)
    assert.equal(instance.expects.url, '')
  })

  test('sets the default request name', async ({ assert }) => {
    const info: ARCSavedRequest = {
      method: 'PUT',
      name: 'test',
      url: 'https://dot.com',
    }
    // @ts-expect-error Used in testing
    delete info.name
    const instance = await Request.fromLegacy(info)
    assert.equal(instance.info.name, 'Unnamed request')
  })
})

test.group('Models > Request > #created', () => {
  test('sets the value', ({ assert }) => {
    const instance = new Request()
    instance.created = 1234
    assert.equal(instance.created, 1234)
  })

  test('sets the current time when value is missing', ({ assert }) => {
    const instance = new Request()
    const now = Date.now()
    instance.created = undefined
    assert.approximately(instance.created, now, 100)
  })
})

test.group('Models > Request > #updated', () => {
  test('sets the value', ({ assert }) => {
    const instance = new Request()
    instance.updated = 1234
    assert.equal(instance.updated, 1234)
  })

  test('sets the created time when value is missing', ({ assert }) => {
    const instance = new Request()
    instance.updated = undefined
    assert.equal(instance.updated, instance.created)
  })

  test('sets the midnight value', ({ assert }) => {
    const instance = new Request()
    instance.updated = 1641774295483
    assert.typeOf(instance.midnight, 'number')
  })
})

test.group('Models > Request > #midnight', () => {
  test('sets the value', ({ assert }) => {
    const instance = new Request()
    instance.midnight = 1234
    assert.equal(instance.midnight, 1234)
  })

  test('sets the default value when the value is missing', ({ assert }) => {
    const instance = new Request()
    instance[updatedSymbol] = 1641774295483
    instance.midnight = undefined
    assert.typeOf(instance.midnight, 'number')
  })

  test('reads the default value when not set', ({ assert }) => {
    const instance = new Request()
    instance[updatedSymbol] = 1641774295483
    // @ts-expect-error Used in testing
    instance[midnightSymbol] = undefined
    assert.typeOf(instance.midnight, 'number')
  })
})

test.group('Models > Request > constructor()', () => {
  test('creates the default values', ({ assert }) => {
    const now = Date.now()
    const instance = new Request()
    assert.equal(instance.kind, RequestKind)

    assert.typeOf(instance.created, 'number')
    assert.typeOf(instance.updated, 'number')

    assert.approximately(instance.created, now, 100)
    assert.approximately(instance.updated, now, 100)

    const { expects, info } = instance
    assert.typeOf(expects, 'object', 'sets the expects')
    assert.typeOf(info, 'EventTarget', 'sets the info')

    assert.equal(expects.method, 'GET')
    assert.equal(expects.url, '')
    assert.equal(expects.kind, HttpRequestKind)

    assert.equal(info.name, '')
    assert.equal(info.kind, ThingKind)
  })

  test('creates values from the schema', ({ assert }) => {
    const now = Date.now()
    const schema: IRequest = {
      kind: RequestKind,
      created: now,
      updated: now,
      expects: {
        url: 'https://dot.com',
        headers: 'x-test: true',
        method: 'PUT',
      },
      info: {
        name: 'a request',
        kind: ThingKind,
      },
    }
    const instance = new Request(schema)

    assert.equal(instance.kind, RequestKind)
    assert.equal(instance.created, now)
    assert.equal(instance.updated, now)
    const { expects, info } = instance
    assert.typeOf(expects, 'object', 'sets the expects')
    assert.typeOf(info, 'EventTarget', 'sets the info')
    assert.equal(expects.method, 'PUT')
    assert.equal(expects.url, 'https://dot.com')
    assert.equal(expects.kind, HttpRequestKind)

    assert.equal(info.name, 'a request')
    assert.equal(info.kind, ThingKind)
  })

  test('creates values from the JSON schema string', ({ assert }) => {
    const now = Date.now()
    const schema: IRequest = {
      kind: RequestKind,
      created: now,
      updated: now,
      expects: {
        url: 'https://dot.com',
        headers: 'x-test: true',
        method: 'PUT',
      },
      info: {
        name: 'a request',
        kind: ThingKind,
      },
    }
    const instance = new Request(JSON.stringify(schema))

    assert.equal(instance.kind, RequestKind)
    assert.equal(instance.created, now)
    assert.equal(instance.updated, now)
    const { expects, info } = instance
    assert.typeOf(expects, 'object', 'sets the expects')
    assert.typeOf(info, 'EventTarget', 'sets the info')
    assert.equal(expects.method, 'PUT')
    assert.equal(expects.url, 'https://dot.com')
    assert.equal(expects.kind, HttpRequestKind)

    assert.equal(info.name, 'a request')
    assert.equal(info.kind, ThingKind)
  })
})

test.group('Models > Request > new()', (group) => {
  let instance: Request
  group.each.setup(() => {
    instance = new Request()
  })

  test('sets the passed expects', ({ assert }) => {
    const schema = instance.toJSON()
    schema.expects.url = 'https://api.com'
    schema.expects.method = 'PATCH'
    schema.expects.headers = 'x-header: true'
    schema.expects.payload = 'a message'
    instance.new(schema)

    assert.equal(instance.expects.url, 'https://api.com')
    assert.equal(instance.expects.method, 'PATCH')
    assert.equal(instance.expects.headers, 'x-header: true')
    assert.equal(instance.expects.payload, 'a message')
  })

  test('sets the default expects', ({ assert }) => {
    const schema = instance.toJSON()
    // @ts-expect-error Used in testing
    delete schema.expects
    instance.new(schema)

    assert.equal(instance.expects.url, '')
    assert.equal(instance.expects.method, 'GET')
    assert.isUndefined(instance.expects.headers)
    assert.isUndefined(instance.expects.payload)
  })

  test('sets the passed info', ({ assert }) => {
    const schema = instance.toJSON()
    schema.info.name = 'a'
    schema.info.description = 'b'
    schema.info.version = 'c'
    instance.new(schema)

    assert.equal(instance.info.name, 'a')
    assert.equal(instance.info.description, 'b')
    assert.equal(instance.info.version, 'c')
  })

  test('sets the default info', ({ assert }) => {
    const schema = instance.toJSON()
    // @ts-expect-error Used in testing
    delete schema.info
    instance.new(schema)

    assert.equal(instance.info.name, '')
    assert.isUndefined(instance.info.description)
    assert.isUndefined(instance.info.version)
  })

  test('sets the passed log', ({ assert }) => {
    const schema = instance.toJSON()
    schema.log = {
      kind: RequestLogKind,
      request: {
        startTime: 1,
        url: 'test',
      },
    }
    instance.new(schema)

    assert.typeOf(instance.log, 'object')
    assert.equal(instance.log!.kind, RequestLogKind)
    assert.typeOf(instance.log!.request, 'object')
    assert.equal(instance.log!.request!.startTime, 1)
  })

  test('sets the log to undefined when missing', ({ assert }) => {
    const schema = instance.toJSON()
    schema.log = {
      kind: RequestLogKind,
      request: {
        startTime: 1,
        url: 'test',
      },
    }
    instance.new(schema)
    assert.typeOf(instance.log, 'object')
    // now delete it and re-set.
    delete schema.log
    instance.new(schema)
    assert.isUndefined(instance.log) // Corrected: schema.log was asserted, should be instance.log
  })

  test('sets the config', ({ assert }) => {
    const schema = instance.toJSON()
    schema.config = {
      enabled: true,
      kind: RequestConfigKind,
      timeout: 10,
    }
    instance.new(schema)

    assert.typeOf(instance.config, 'object')
    assert.equal(instance.config!.timeout, 10)
  })

  test('sets the config to undefined when missing', ({ assert }) => {
    const schema = instance.toJSON()
    schema.config = {
      enabled: true,
      kind: RequestConfigKind,
      timeout: 10,
    }
    instance.new(schema)
    delete schema.config
    instance.new(schema)
    assert.isUndefined(instance.config)
  })

  test('sets the authorization', ({ assert }) => {
    const schema = instance.toJSON()
    schema.authorization = [
      {
        kind: RequestAuthorizationKind,
        enabled: true,
        type: 'oauth 2',
        valid: true,
        config: {},
      },
    ]
    instance.new(schema)

    assert.typeOf(instance.authorization, 'array')
    assert.equal(instance.authorization![0].type, 'oauth 2')
  })

  test('sets the authorization to undefined when missing', ({ assert }) => {
    const schema = instance.toJSON()
    schema.authorization = [
      {
        kind: RequestAuthorizationKind,
        enabled: true,
        type: 'oauth 2',
        valid: true,
        config: {},
      },
    ]
    instance.new(schema)
    delete schema.authorization
    instance.new(schema)

    assert.isUndefined(instance.authorization)
  })

  test('sets the created', ({ assert }) => {
    const schema = instance.toJSON()
    schema.created = 1234567
    instance.new(schema)
    assert.equal(instance.created, 1234567)
  })

  test('sets the created as current time', ({ assert }) => {
    const now = Date.now()
    const schema = instance.toJSON()
    delete schema.created
    instance.new(schema)
    assert.approximately(instance.created, now, 100)
  })

  test('sets the updated', ({ assert }) => {
    const schema = instance.toJSON()
    schema.updated = 1234567
    instance.new(schema)
    assert.equal(instance.updated, 1234567)
  })

  test('sets the updated to created when missing', ({ assert }) => {
    const schema = instance.toJSON()
    delete schema.updated
    instance.new(schema)
    assert.equal(instance.updated, instance.created)
  })

  test('sets the midnight', ({ assert }) => {
    const schema = instance.toJSON()
    schema.midnight = 1234567
    instance.new(schema)
    assert.equal(instance.midnight, 1234567)
  })

  test('sets the flows', ({ assert }) => {
    const schema = instance.toJSON()
    schema.flows = [
      {
        trigger: 'request',
        condition: {
          steps: [new ReadDataStep(FlowSourceEnum.request, '', FlowRequestDataEnum.body).toJSON()],
        },
        actions: [
          {
            steps: [],
          },
        ],
      },
    ]
    instance.new(schema)

    const { flows } = instance
    assert.typeOf(flows, 'array', 'has flows')

    const [flow] = flows!
    assert.lengthOf(flow.actions, 1)
  })

  test('sets the flows to undefined when missing', ({ assert }) => {
    const schema = instance.toJSON()
    schema.flows = [
      {
        trigger: 'request',
        condition: {
          steps: [new ReadDataStep(FlowSourceEnum.request, '', FlowRequestDataEnum.body).toJSON()],
        },
        actions: [
          {
            steps: [],
          },
        ],
      },
    ]
    instance.new(schema)
    delete schema.flows
    instance.new(schema)

    assert.isUndefined(instance.flows)
  })

  test('sets the assertions', ({ assert }) => {
    const schema = instance.toJSON()
    schema.assertions = [
      {
        steps: [
          {
            kind: 'test',
          },
        ],
      },
    ]
    instance.new(schema)

    const { assertions } = instance
    assert.typeOf(assertions, 'array', 'has assertions')

    const [assertion] = assertions!
    assert.lengthOf(assertion.steps, 1)
  })

  test('sets the assertions to undefined when missing', ({ assert }) => {
    const schema = instance.toJSON()
    schema.assertions = [
      {
        steps: [
          {
            kind: 'test',
          },
        ],
      },
    ]
    instance.new(schema)
    delete schema.assertions
    instance.new(schema)

    assert.isUndefined(instance.assertions)
  })

  test('sets the clientCertificate', ({ assert }) => {
    const schema = instance.toJSON()
    const cert: IP12Certificate = {
      cert: { data: 'test' },
      key: '123',
      kind: 'Core#Certificate',
      name: 'name',
      type: 'p12',
      created: 123456,
    }

    schema.clientCertificate = cert
    instance.new(schema)

    const { clientCertificate } = instance
    assert.ok(clientCertificate, 'has clientCertificate')
    assert.equal(clientCertificate!.kind, CertificateKind)
  })

  test('sets the clientCertificate to undefined when missing', ({ assert }) => {
    const schema = instance.toJSON()
    const cert: IP12Certificate = {
      cert: { data: 'test' },
      key: '123',
      kind: 'Core#Certificate',
      name: 'name',
      type: 'p12',
      created: 123456,
    }
    schema.clientCertificate = cert
    instance.new(schema)
    delete schema.clientCertificate
    instance.new(schema)

    assert.isUndefined(instance.clientCertificate)
  })
})

test.group('Models > Request > toJSON()', (group) => {
  let instance: Request
  group.each.setup(() => {
    instance = new Request()
  })

  test('sets the expects', ({ assert }) => {
    instance.expects.url = 'https://api.com'
    instance.expects.method = 'PATCH'
    instance.expects.headers = 'x-header: true'
    instance.expects.payload = 'a message'

    const result = instance.toJSON()

    assert.equal(result.expects.url, 'https://api.com')
    assert.equal(result.expects.method, 'PATCH')
    assert.equal(result.expects.headers, 'x-header: true')
    assert.equal(result.expects.payload, 'a message')
  })

  test('sets the info', ({ assert }) => {
    instance.info.name = 'a'
    instance.info.description = 'b'
    instance.info.version = 'c'

    const result = instance.toJSON()

    assert.equal(result.info.name, 'a')
    assert.equal(result.info.description, 'b')
    assert.equal(result.info.version, 'c')
  })

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

  test('sets the updated, created, and midnight', ({ assert }) => {
    const now = Date.now()
    instance.created = now
    instance.updated = now
    instance.midnight = 1234

    const result = instance.toJSON()

    assert.equal(result.created, now)
    assert.equal(result.updated, now)
    assert.equal(result.midnight, 1234)
  })

  test('sets the log', ({ assert }) => {
    instance.log = new RequestLog({
      kind: RequestLogKind,
      request: {
        startTime: 1,
        url: 'test',
      },
    })

    const result = instance.toJSON()

    assert.typeOf(result.log, 'object')
    assert.equal(result.log!.kind, RequestLogKind)
    assert.typeOf(result.log!.request, 'object')
    assert.equal(result.log!.request!.startTime, 1)
  })

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

  test('sets the config', ({ assert }) => {
    instance.config = new RequestConfig({
      enabled: true,
      kind: RequestConfigKind,
      timeout: 10,
    })
    const result = instance.toJSON()
    assert.typeOf(result.config, 'object')
    assert.equal(result.config!.timeout, 10)
  })

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

  test('sets the authorization', ({ assert }) => {
    instance.authorization = [
      new RequestAuthorization({
        kind: RequestAuthorizationKind,
        enabled: true,
        type: 'oauth 2',
        valid: true,
        config: {},
      }),
    ]
    const result = instance.toJSON()

    assert.typeOf(result.authorization, 'array')
    assert.equal(result.authorization![0].type, 'oauth 2')
  })

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

  test('sets the flows', ({ assert }) => {
    instance.flows = [
      {
        trigger: 'request',
        condition: {
          steps: [new ReadDataStep(FlowSourceEnum.request, '', FlowRequestDataEnum.body).toJSON()],
        },
        actions: [
          {
            steps: [],
          },
        ],
      },
    ]
    const result = instance.toJSON()
    const { flows } = result
    assert.typeOf(flows, 'array', 'has flows')

    const [flow] = flows!
    assert.lengthOf(flow.actions, 1)
  })

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

  test('sets the clientCertificate', ({ assert }) => {
    const cert: IP12Certificate = {
      cert: { data: 'test' },
      key: '123',
      kind: 'Core#Certificate',
      name: 'name',
      type: 'p12',
      created: 123456,
    }
    instance.clientCertificate = new Certificate(cert)
    const result = instance.toJSON()
    const { clientCertificate } = result
    assert.ok(clientCertificate, 'has clientCertificate')
    assert.equal(clientCertificate!.key, '123')
  })

  test('sets the clientCertificate to undefined when missing', ({ assert }) => {
    const result = instance.toJSON()
    assert.isUndefined(result.clientCertificate)
  })
})

test.group('Models > Request > setInfo()', (group) => {
  let instance: Request
  group.each.setup(() => {
    instance = new Request()
  })

  test('sets the info from the schema', ({ assert }) => {
    instance.setInfo({
      kind: ThingKind,
      name: 'test',
    })
    assert.equal(instance.info.name, 'test')
  })

  test('sets the updated', ({ assert }) => {
    const now = Date.now()
    instance.setInfo({
      kind: ThingKind,
      name: 'test',
    })
    assert.approximately(instance.updated, now, 100)
  })
})

test.group('Models > Request > getExpects()', (group) => {
  let instance: Request
  group.each.setup(() => {
    instance = new Request()
  })

  test('sets the expects object when missing', ({ assert }) => {
    // @ts-expect-error Used in testing
    delete instance.expects
    const result = instance.getExpects()
    assert.typeOf(result, 'object', 'returns an object')
    assert.deepEqual(result, instance.expects, 'sets the expects on the instance')
  })

  test('returns the expects object', ({ assert }) => {
    instance.expects = new HttpRequest({
      kind: HttpRequestKind,
      url: 'domain.com',
    })
    const result = instance.getExpects()
    assert.deepEqual(result, instance.expects)
  })
})

test.group('Models > Request > getConfig()', (group) => {
  let instance: Request
  group.each.setup(() => {
    instance = new Request()
  })

  test('sets the config object when missing', ({ assert }) => {
    delete instance.config
    const result = instance.getConfig()
    assert.typeOf(result, 'object', 'returns an object')
    assert.deepEqual(result, instance.config, 'sets the config on the instance')
  })

  test('returns the config object', ({ assert }) => {
    instance.config = new RequestConfig({
      kind: RequestConfigKind,
      enabled: true,
    })
    const result = instance.getConfig()
    assert.deepEqual(result, instance.config)
  })
})

test.group('Models > Request > setLog()', (group) => {
  let instance: Request
  group.each.setup(() => {
    instance = new Request()
  })

  test('sets the log object from the schema', ({ assert }) => {
    const schema = new RequestLog({
      kind: RequestLogKind,
      request: {
        startTime: 1,
        url: 'test',
      },
    }).toJSON()
    instance.setLog(schema)

    assert.ok(instance.log, 'has the log')
    assert.equal(instance.log!.request!.url, 'test')
  })

  test('sets the updated', ({ assert }) => {
    const schema = new RequestLog({
      kind: RequestLogKind,
      request: {
        startTime: 1,
        url: 'test',
      },
    }).toJSON()
    const now = Date.now()
    instance.setLog(schema)

    assert.approximately(instance.updated, now, 100)
  })
})
