import { test } from '@japa/runner'
import { ApiSchemaGenerator } from '../../../src/amf/ApiSchemaGenerator.js'
import { IAmfDocument } from '../../../src/amf/definitions/Amf.js'
import { IApiAnyShape } from '../../../src/amf/definitions/Shapes.js'
import { AmfLoader } from './AmfLoader.js'

test.group('XML processing', (group) => {
  const loader = new AmfLoader()
  const xmlMime = 'application/xml'

  let model: IAmfDocument

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

  test('basic formatting and type name', ({ assert }) => {
    const payload = loader.getPayloads(model, '/IncludedInType', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<Person>', 'Type name is set')
    assert.include(result.renderValue, ' <error>false</error>', 'XML is formatted')
    assert.include(result.renderValue, ' <image>\n', 'Complex type is formatted')
    assert.include(result.renderValue, ' </image>\n', 'Complex type is formatted at the end')
  })

  test('adds attribute to parent type (no examples)', ({ assert }) => {
    const payload = loader.getPayloads(model, '/IncludedInType', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: false,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<Person error="false">', 'Type has attributes')
  })

  test('uses example from RAML example', ({ assert }) => {
    const payload = loader.getPayloads(model, '/typeExamples', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<firstName>Pawel</firstName>', 'has example value')
    assert.include(result.renderValue, '  <firstName>', 'example is formatted')
  })

  test('uses example from property example', ({ assert }) => {
    const payload = loader.getPayloads(model, '/propertyExamples', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<zip>00000</zip>', 'has example value')
    assert.include(result.renderValue, '<street></street>', 'has default empty value')
    assert.include(result.renderValue, '    <street></street>', 'example value is formatted')
  })

  test('renders array types', ({ assert }) => {
    const payload = loader.getPayloads(model, '/ArrayType', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<ArrayType>\n', 'has parent type name')
    // the type is not wrapped so it cannot have `Image` tag.
    // assert.include(result.renderValue, '  <Image>', 'has item type name');
  })

  test('generates examples for array type (inline included)', ({ assert }) => {
    const payload = loader.getPayloads(model, '/arrayPropertyGeneratedExamples', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<schema>\n', 'has parent type name')
    assert.include(result.renderValue, '  <test>false</test>', 'has property name')
    // the type is not wrapped so it cannot have `items` tag.
    // assert.include(result.renderValue, '  <items>', 'has item type name');
  })

  test('generates examples for wrapped annotation', ({ assert }) => {
    const payload = loader.getPayloads(model, '/wrappedXml', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<Imgs>\n', 'has parent type name')
    assert.include(result.renderValue, '  <something></something>', 'has parent properties')
    assert.include(result.renderValue, '  <images>', 'has wrapped outer element')
    assert.include(result.renderValue, '    <Image>', 'has wrapped object')
  })

  test('renders default name for an inline included types', ({ assert }) => {
    const payload = loader.getPayloads(model, '/user-raml-example', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<type>\n', 'has default name')
    assert.include(result.renderValue, '  <id>uid1</id>', 'has properties')
  })

  test('renders RAML named example', ({ assert }) => {
    const payload = loader.getPayloads(model, '/named-example', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<schema>\n', 'has AMF default name')
    assert.include(result.renderValue, '<countryCode>BE</countryCode>', 'has properties')
  })

  test('renders RAML linked named example', ({ assert }) => {
    const payload = loader.getPayloads(model, '/named-linked-example', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    assert.include(result.renderValue, '<schema>\n', 'has AMF default name')
    assert.include(result.renderValue, '<company>\n', 'has properties')
    assert.include(result.renderValue, '<countryCode>BE</countryCode>\n', 'has deep properties')
  })

  test('renders typed valued', ({ assert }) => {
    const payload = loader.getPayloads(model, '/data-types', 'post')[0]

    const anyShape = payload.schema as IApiAnyShape
    const result = ApiSchemaGenerator.asExample(anyShape, xmlMime, {
      renderExamples: true,
      renderOptional: true,
    })!

    assert.typeOf(result, 'object', 'generates the example')
    assert.typeOf(result.renderValue, 'string', 'has the value')

    const renderValue = result.renderValue as string

    assert.include(renderValue, '<typeString>String example</typeString>', 'has string value')
    assert.include(renderValue, '<typeNumber>123456</typeNumber>', 'has number value')
    assert.include(renderValue, '<typeInt>1234546</typeInt>', 'has int value')
    assert.include(renderValue, '<typeDecimal>10.67</typeDecimal>', 'has decimal value')
    assert.include(renderValue, '<typeBool>true</typeBool>', 'has boolean value')

    assert.include(renderValue, '<typeNull></typeNull>', 'has null value')
    assert.include(renderValue, '<typeNegativeInt>-12</typeNegativeInt>', 'has negative int value')
    assert.include(renderValue, '<typeNumberFormatInt64>8</typeNumberFormatInt64>', 'has Int64 value')
    assert.include(renderValue, '<typeNumFormatInt32>109298</typeNumFormatInt32>', 'has Num32 value')
    assert.include(renderValue, '<typeNumFormatInt16>2</typeNumFormatInt16>', 'has Num16 value')
    assert.include(renderValue, '<typeNumFormatInt8>1</typeNumFormatInt8>', 'has Num8 value')
    assert.include(renderValue, '<typeIntFormatInt8>12</typeIntFormatInt8>', 'has Int8 value')
    assert.include(renderValue, '<typeNumFormatInt>11</typeNumFormatInt>', 'has Num value')
    assert.include(renderValue, '<typeNumFormatLong>123456789</typeNumFormatLong>', 'has long value')
    assert.include(renderValue, '<typeNumFormatFloat>1234567.89</typeNumFormatFloat>', 'has float value')
    assert.include(renderValue, '<typeNumFormatDouble>1234.56789</typeNumFormatDouble>', 'has double value')
  })
})
