import { test } from '@japa/runner'
import {
  createLogger,
  CoreEngine,
  IHttpRequest,
  HttpEngineOptions,
  RequestAuthorizationKind,
  PayloadSerializer,
} from '../../../../src/index.js'

const logger = createLogger()

test.group('prepareMessage()', () => {
  const bytes = [0x74, 0x65, 0x73, 0x74, 0x0a, 0x74, 0x65, 0x73, 0x74]

  test('returns a Buffer', async ({ assert, httpConfig }) => {
    const request = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'GET',
      headers: 'Host: test.com\nContent-Length: 0',
      payload: 'abc',
    }
    const engine = new CoreEngine(request, { logger })
    const result = await engine.prepareMessage()
    assert.isTrue(result instanceof Buffer)
  })

  test('ignores payload for GET requests', async ({ assert, httpConfig }) => {
    const request = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'GET',
      headers: 'Host: test.com\nContent-Length: 0',
      payload: 'abc',
    }
    const engine = new CoreEngine(request, { logger })
    const result = await engine.prepareMessage()
    assert.lengthOf(result.toString().split('\n'), 5)
  })

  test('creates a message with the passed payload', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'POST',
      headers: 'content-type: text/plain',
      payload: PayloadSerializer.stringifyBuffer(Buffer.from([...bytes])),
    }
    const opts: HttpEngineOptions = {
      timeout: 50000,
      followRedirects: false,
      logger,
    }
    const engine = new CoreEngine(request, opts)
    const result = await engine.prepareMessage()
    assert.lengthOf(result.toString().split('\n'), 7)
  })

  test('adds NTLM request headers from payload processing', async ({ assert, httpConfig }) => {
    const request = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'GET',
      headers: 'Host: test.com',
    }
    const opts: HttpEngineOptions = {
      logger,
      authorization: [
        {
          kind: RequestAuthorizationKind,
          type: 'ntlm',
          enabled: true,
          valid: true,
          config: {
            domain: 'domain.com',
            username: 'test',
            password: 'test',
          },
        },
      ],
    }

    const engine = new CoreEngine(request, opts)
    const result = await engine.prepareMessage()
    const headers = engine.request.headers as string
    assert.equal(headers.indexOf('NTLM '), -1, 'Headers are not altered')
    assert.isAbove(result.toString().indexOf('NTLM '), 0, 'Adds headers to body')
  })

  test('adds content length header', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'POST',
      headers: 'content-type: text/plain',
      payload: PayloadSerializer.stringifyBuffer(Buffer.from([...bytes])),
    }
    const opts: HttpEngineOptions = {
      logger,
    }
    const engine = new CoreEngine(request, opts)
    const result = await engine.prepareMessage()
    const headers = engine.sentRequest.headers as string
    const search = headers.indexOf('content-length: 9')
    assert.isAbove(search, 0)
    assert.isAbove(result.toString().indexOf('content-length: 9'), 0)
  })

  test('adds the default headers', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'POST',
      headers: 'content-type: text/plain',
      payload: PayloadSerializer.stringifyBuffer(Buffer.from([...bytes])),
    }
    const opts: HttpEngineOptions = {
      logger,
      defaultHeaders: true,
    }
    const engine = new CoreEngine(request, opts)
    await engine.prepareMessage()

    assert.include(engine.sentRequest.headers, 'user-agent: api client', 'user-agent is set')
    assert.include(engine.sentRequest.headers, 'accept: */*', 'accept is set')
  })

  test('adds missing content-type from payloadOptions', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'POST',
      headers: '',
      payload: PayloadSerializer.stringifyBuffer(Buffer.from([...bytes])),
      payloadOptions: {
        mime: 'test/text',
      },
    }
    const opts: HttpEngineOptions = {
      logger,
    }
    const engine = new CoreEngine(request, opts)
    await engine.prepareMessage()
    assert.include(engine.sentRequest.headers, 'content-type: test/text')
  })

  test('does not add missing content-type when payloadOptions is not set', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'POST',
      headers: '',
      payload: PayloadSerializer.stringifyBuffer(Buffer.from([...bytes])),
    }
    const opts: HttpEngineOptions = {
      logger,
    }
    const engine = new CoreEngine(request, opts)
    await engine.prepareMessage()
    assert.notInclude(engine.sentRequest.headers, 'content-type')
  })

  test('does not override content-type with payloadOptions when has the header', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'POST',
      headers: 'content-type: text/plain',
      payload: PayloadSerializer.stringifyBuffer(Buffer.from([...bytes])),
      payloadOptions: {
        mime: 'test/text',
      },
    }
    const opts: HttpEngineOptions = {
      logger,
    }
    const engine = new CoreEngine(request, opts)
    await engine.prepareMessage()
    assert.include(engine.sentRequest.headers, 'content-type: text/plain')
  })

  test('does not add content-type for non-payload requests', async ({ assert, httpConfig }) => {
    const request: IHttpRequest = {
      url: `http://localhost:${httpConfig.httpPort}/api/endpoint?query=param`,
      method: 'GET',
      headers: '',
      payloadOptions: {
        mime: 'test/text',
      },
    }
    const opts: HttpEngineOptions = {
      logger,
    }
    const engine = new CoreEngine(request, opts)
    await engine.prepareMessage()
    assert.notInclude(engine.sentRequest.headers, 'content-type')
  })
})
