import { test } from '@japa/runner'
import { DOMParser } from '@xmldom/xmldom'
import { AmfNamespace as ns } from '../../../src/amf/definitions/Namespace.js'
import { ApiSchemaGenerator, ISchemaExample } from '../../../src/amf/ApiSchemaGenerator.js'
import { AmfLoader } from './AmfLoader.js'
import { IAmfDocument } from '../../../src/amf/definitions/Amf.js'

const loader = new AmfLoader()
const jsonMime = 'application/json'
const xmlMime = 'application/xml'

test.group(`scalar values / ${jsonMime}`, (group) => {
  let model: IAmfDocument

  group.setup(async () => {
    model = await loader.getGraph('schema-api')
  })

  test('generates a schema without a value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarType')
    const result = ApiSchemaGenerator.asSchema(shape, jsonMime)
    assert.typeOf(result, 'string')
  })

  test('generates a schema without mocked value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarType')
    const result = ApiSchemaGenerator.asSchema(shape, jsonMime, {
      renderMocked: true,
    })
    assert.typeOf(result, 'string', 'is a string')
    assert.isNotEmpty(result, 'has a value')
  })

  test('generates a schema with the default value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithTraits')
    const result = ApiSchemaGenerator.asSchema(shape, jsonMime)
    assert.strictEqual(result, 'test')
  })

  test('generates a schema with the example value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithExample')
    const result = ApiSchemaGenerator.asSchema(shape, jsonMime, {
      renderExamples: true,
    })
    assert.strictEqual(result, 'A string')
  })

  test('generates a schema with the first enum value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithEnum')
    const result = ApiSchemaGenerator.asSchema(shape, jsonMime)
    assert.strictEqual(result, 'A')
  })

  // Apparently AMF does not move properties from the parent scalar shape
  test('generates a schema for a parent', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithParent')
    const result = ApiSchemaGenerator.asSchema(shape, jsonMime)
    assert.strictEqual(result, 'test')
  }).skip()

  test('generates the example', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithExample')
    const result = ApiSchemaGenerator.asExample(shape, jsonMime, {
      renderExamples: true,
    }) as ISchemaExample
    assert.typeOf(result, 'object', 'returns an object')
    assert.isTrue(result.strict, 'has the strict property')
    assert.deepEqual(result.types, [ns.aml.vocabularies.apiContract.Example], 'has the types property')
    assert.equal(result.mediaType, jsonMime, 'has the mediaType property')
    assert.strictEqual(result.renderValue, 'A string', 'has the value')
  })
})

test.group(`scalar values / ${xmlMime}`, (group) => {
  let model: IAmfDocument

  group.setup(async () => {
    model = await loader.getGraph('schema-api')
  })

  test('generates a schema without a value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarType')
    const result = ApiSchemaGenerator.asSchema(shape, xmlMime)

    const parser = new DOMParser()
    const doc = parser.parseFromString(result as string, 'application/xml')
    const node = doc.getElementsByTagName('ScalarType')[0]
    assert.ok(node, 'has the schema name')
    assert.equal((node.textContent as string).trim(), '')
  })

  test('generates a schema without a mocked value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarType')
    const result = ApiSchemaGenerator.asSchema(shape, xmlMime, {
      renderMocked: true,
    })

    const parser = new DOMParser()
    const doc = parser.parseFromString(result as string, 'application/xml')
    const node = doc.getElementsByTagName('ScalarType')[0]
    assert.ok(node, 'has the schema name')
    assert.isNotEmpty((node.textContent as string).trim())
  })

  test('generates a schema with the default value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithTraits')
    const result = ApiSchemaGenerator.asSchema(shape, xmlMime)
    assert.strictEqual(result, '<ScalarWithTraits>test</ScalarWithTraits>')
  })

  test('generates a schema with the example value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithExample')
    const result = ApiSchemaGenerator.asSchema(shape, xmlMime, {
      renderExamples: true,
    })
    assert.strictEqual(result, '<ScalarWithExample>A string</ScalarWithExample>')
  })

  test('generates a schema with the first enum value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithEnum')
    const result = ApiSchemaGenerator.asSchema(shape, xmlMime)
    assert.strictEqual(result, '<ScalarWithEnum>A</ScalarWithEnum>')
  })

  // Apparently AMF does not move properties from the parent scalar shape
  test('generates a schema for a parent', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithParent')
    const result = ApiSchemaGenerator.asSchema(shape, xmlMime)
    assert.strictEqual(result, '<ScalarWithParent>test</ScalarWithParent>')
  }).skip()

  test('generates the example', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarWithExample')
    const result = ApiSchemaGenerator.asExample(shape, xmlMime, {
      renderExamples: true,
    }) as ISchemaExample
    assert.typeOf(result, 'object', 'returns an object')
    assert.isTrue(result.strict, 'has the strict property')
    assert.deepEqual(result.types, [ns.aml.vocabularies.apiContract.Example], 'has the types property')
    assert.equal(result.mediaType, xmlMime, 'has the mediaType property')
    assert.strictEqual(result.renderValue, '<ScalarWithExample>A string</ScalarWithExample>', 'has the value')
  })
})
