import { test } from '@japa/runner'
import { AmfLoader } from '../AmfLoader.js'
import { AmfSerializer } from '../../../../src/amf/AmfSerializer.js'
import type { IAmfDocument, IAmfScalarShape } from '../../../../src/amf/definitions/Amf.js'
import type { IApiScalarNode } from '../../../../src/amf/definitions/Shapes.js'
import { AmfNamespace } from '../../../../src/amf/definitions/Namespace.js'

const loader = new AmfLoader()
test.group('RAML parameters', (group) => {
  let api: IAmfDocument
  let serializer: AmfSerializer
  group.setup(async () => {
    api = await loader.getGraph('amf-helper-api')
    serializer = new AmfSerializer()
    serializer.amf = api
  })

  test('has the name and the paramName', ({ assert }) => {
    const op = loader.lookupOperation(api, '/parameters', 'get')
    const ex = serializer._computeExpects(op)!
    const headers = loader.readHeaders(ex)!
    const result = serializer.parameter(headers[0]) // Accept
    assert.equal(result.name, 'Accept', 'has the name')
    assert.equal(result.paramName, 'Accept', 'has the paramName')
  })

  test('has the description', ({ assert }) => {
    const op = loader.lookupOperation(api, '/parameters', 'get')
    const ex = serializer._computeExpects(op)!
    const headers = loader.readHeaders(ex)!
    const result = serializer.parameter(headers[0]) // Accept
    assert.equal(result.description, "Selects the response's media type, when supported.", 'has the name')
  })

  test('has the required', ({ assert }) => {
    const op = loader.lookupOperation(api, '/parameters', 'get')
    const ex = serializer._computeExpects(op)!
    const headers = loader.readHeaders(ex)!
    const header = headers[1] // x-required
    const result = serializer.parameter(header)
    assert.isTrue(result.required)
  })

  test('has the binding', ({ assert }) => {
    const op = loader.lookupOperation(api, '/parameters', 'get')
    const ex = serializer._computeExpects(op)!
    const headers = loader.readHeaders(ex)!
    const header = headers[1] // x-required
    const result = serializer.parameter(header)
    assert.equal(result.binding, 'header')
  })

  test('has the example', ({ assert }) => {
    const op = loader.lookupOperation(api, '/parameters', 'get')
    const ex = serializer._computeExpects(op)!
    const headers = loader.readHeaders(ex)!
    const header = headers[3] // If-Modified-Since
    const result = serializer.parameter(header)
    // the example is passed to the schema.
    assert.deepEqual(result.examples, [])
  })

  test('has the examples', ({ assert }) => {
    const op = loader.lookupOperation(api, '/parameters', 'get')
    const ex = serializer._computeExpects(op)!
    const qp = loader.readQueryParameters(ex)!
    const param = qp[3] // combo
    const result = serializer.parameter(param)
    // the example is passed to the schema.
    assert.deepEqual(result.examples, [])
  })
})

test.group('OAS parameters', (group) => {
  let api: IAmfDocument
  let serializer: AmfSerializer
  group.setup(async () => {
    api = await loader.getGraph('petstore')
    serializer = new AmfSerializer()
    serializer.amf = api
  })

  test('has the OAS properties', ({ assert }) => {
    const op = loader.lookupOperation(api, '/pet/{petId}', 'post')
    const ex = serializer._computeExpects(op)!
    const params = loader.readQueryParameters(ex)!
    const param = params.find(
      (p) => serializer._getValue(p, AmfNamespace.aml.vocabularies.apiContract.paramName) === 'name'
    )!
    const result = serializer.parameter(param)
    assert.isFalse(result.required, 'is not required')
    assert.isTrue(result.deprecated, 'is deprecated')
    assert.isTrue(result.allowEmptyValue, 'is allowEmptyValue')
    assert.isTrue(result.explode, 'is explode')
    assert.isTrue(result.allowReserved, 'is allowReserved')
    assert.equal(result.style, 'form', 'has style')
  })

  test('has the content property', ({ assert }) => {
    const op = loader.lookupOperation(api, '/pet/{petId}', 'post')
    const ex = serializer._computeExpects(op)!
    const params = loader.readQueryParameters(ex)!
    const param = params.find(
      (p) => serializer._getValue(p, AmfNamespace.aml.vocabularies.apiContract.paramName) === 'filter'
    )!
    const result = serializer.parameter(param)
    const [p] = result.payloads
    assert.include(p.types, AmfNamespace.aml.vocabularies.apiContract.Payload, 'has the type')
    assert.deepEqual(p.customDomainProperties, [], 'has empty customDomainProperties')
    assert.deepEqual(p.examples, [], 'has empty examples')
    assert.equal(p.mediaType, 'application/json', 'has mediaType')
    assert.typeOf(p.schema, 'object', 'has schema')
  })

  test('has the examples property', ({ assert }) => {
    // it seems that AMF moves examples from parameter into the schema.
    // This copies examples from the schema to the top level parameter so we can test it.
    // Possibly this happens during the resolution phase.
    const op = loader.lookupOperation(api, '/pet/findByStatus', 'get')
    const ex = serializer._computeExpects(op)!
    const params = loader.readQueryParameters(ex)!
    const param = params.find(
      (p) => serializer._getValue(p, AmfNamespace.aml.vocabularies.apiContract.paramName) === 'status'
    )!
    // @ts-expect-error Used in testing
    const schema = param[serializer._getAmfKey(AmfNamespace.aml.vocabularies.shapes.schema)][0] as IAmfScalarShape
    const key = serializer._getAmfKey(AmfNamespace.aml.vocabularies.apiContract.examples)
    // @ts-expect-error Used in testing
    const info = schema[key]
    // @ts-expect-error Used in testing
    param[key] = info
    const result = serializer.parameter(param)
    const { examples } = result
    assert.typeOf(examples, 'array', 'has examples')
    assert.lengthOf(examples, 1, 'has  a single example')
    const [example] = examples
    assert.equal(example.id, info[0]['@id'], 'has the id')
    assert.include(example.types, AmfNamespace.aml.vocabularies.apiContract.Example, 'has the type')
    assert.deepEqual(example.customDomainProperties, [], 'has empty customDomainProperties')
    assert.isTrue(example.strict, 'has strict')
    assert.equal(example.value, 'pending', 'has the value')
    const { structuredValue } = example
    assert.typeOf(structuredValue, 'object', 'has the structuredValue')
    assert.equal(structuredValue!.name, 'scalar_1', 'has the name')
    const typed = structuredValue as IApiScalarNode
    assert.equal(typed.value, 'pending', 'has the value')
    assert.equal(typed.dataType, AmfNamespace.w3.xmlSchema.string, 'has the dataType')
  })
})
