import { test } from '@japa/runner'
import {
  IRequestProxyInit,
  HttpRequestKind,
  RequestLog,
  IBasicAuthorization,
  ProxyService,
  Exception,
  SetDataStep,
  SetVariableStep,
} from '../../../../src/index.js'

test.group('RequestProxy', (group) => {
  let message: IRequestProxyInit

  group.each.setup(({ context }) => {
    message = {
      kind: HttpRequestKind,
      request: {
        url: `http://localhost:${context.httpConfig.httpPort}/v1/get`,
        method: 'GET',
      },
      authorization: [],
    }
  })

  test('throws an error when no request', async ({ assert }) => {
    // @ts-expect-error Used in testing
    delete message.request
    const service = new ProxyService()
    let error: Exception
    try {
      await service.proxyRequest(message)
      error = new Exception('Not thrown', { status: 0 })
    } catch (e) {
      error = e as Exception
    }
    assert.ok(error, 'has the error')
    assert.equal(error.status, 400, 'has the error code')
    assert.equal(error.message, 'Invalid request', 'has the message')
    assert.equal(error.help, 'The "request" parameter is required.')
  })

  test('returns an error when the request has no URL', async ({ assert }) => {
    // @ts-expect-error Used in testing
    delete message.request.url
    let error: Exception
    const service = new ProxyService()
    try {
      await service.proxyRequest(message)
      error = new Exception('Not thrown', { status: 0 })
    } catch (e) {
      error = e as Exception
    }
    assert.ok(error, 'has the error')
    assert.equal(error.status, 400, 'has the error code')
    assert.equal(error.message, 'Invalid request', 'has the message')
    assert.equal(error.help, 'The "request.url" parameter is required.')
  })

  test('proxies a GET request', async ({ assert }) => {
    message.request.headers = 'x-test: true\nauthorization: xyz'
    message.request.url += '?a=b#c'
    const service = new ProxyService()
    const response = await service.proxyRequest(message)
    const data = new RequestLog(response.result)
    const echo = JSON.parse((await data.response?.readPayloadAsString()) as string)
    assert.equal(echo.headers['x-test'], 'true')
    assert.equal(echo.url, '/?a=b')
  })

  test('adds the authorization data', async ({ assert }) => {
    message.authorization = [
      {
        kind: 'Core#RequestAuthorization',
        enabled: true,
        type: 'basic',
        valid: true,
        config: {
          username: 'a',
          password: 'b',
        } as IBasicAuthorization,
      },
    ]
    const service = new ProxyService()
    const response = await service.proxyRequest(message)
    const data = new RequestLog(response.result)
    const echo = JSON.parse((await data.response?.readPayloadAsString()) as string)
    assert.equal(echo.headers['authorization'], 'Basic YTpi')
  })

  test('adds the variables', async ({ assert, httpConfig }) => {
    message.variables = {
      httpPort: String(httpConfig.httpPort),
    }
    message.request.url = 'http://localhost:{httpPort}/v1/get'
    const service = new ProxyService()
    const response = await service.proxyRequest(message)
    const data = new RequestLog(response.result)
    const echo = JSON.parse((await data.response?.readPayloadAsString()) as string)
    assert.equal(echo.baseUrl, '/v1/get')
  })

  test('adds the request flows', async ({ assert }) => {
    message.flows = [
      {
        trigger: 'request',
        actions: [
          {
            steps: [new SetDataStep('val1').toJSON(), new SetVariableStep('var1').toJSON()],
          },
        ],
      },
    ]
    const service = new ProxyService()
    const response = await service.proxyRequest(message)
    const vars = response.variables as Record<string, string>
    assert.equal(vars.var1, 'val1')
  })

  test('proxies a body', async ({ assert }) => {
    message.request.method = 'POST'
    message.request.headers = `content-type: application/json`
    message.request.payload = '{"test":true}'
    const service = new ProxyService()
    const response = await service.proxyRequest(message)
    const data = new RequestLog(response.result)
    const echo = JSON.parse((await data.response?.readPayloadAsString()) as string)
    assert.equal(echo.body, '{"test":true}')
  })
})
