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

const loader = new AmfLoader()
const jsonMime = 'application/json'
const xmlMime = 'application/xml'
const urlEncodedMime = 'application/x-www-form-urlencoded'

test.group(jsonMime, (group) => {
  let reader: ApiExampleGenerator
  let model: IAmfDocument

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

  test('returns null when no value and no structured value', ({ assert }) => {
    const example: IApiDataExample = {
      id: '',
      strict: true,
      types: [ns.aml.vocabularies.document.NamedExamples],
      customDomainProperties: [],
    }
    const result = reader.read(example, jsonMime)
    assert.isUndefined(result)
  })

  test('returns the value when no mime type', ({ assert }) => {
    const example: IApiDataExample = {
      id: '',
      strict: true,
      types: [ns.aml.vocabularies.document.NamedExamples],
      value: 'test',
      customDomainProperties: [],
    }
    const result = reader.read(example)
    assert.strictEqual(result, 'test')
  })

  test('returns the value when corresponds to the mime type', ({ assert }) => {
    const example: IApiDataExample = {
      id: '',
      strict: true,
      types: [ns.aml.vocabularies.document.NamedExamples],
      value: '[]',
      customDomainProperties: [],
    }
    const result = reader.read(example, jsonMime)
    assert.strictEqual(result, '[]')
  })

  test('reads a number scalar value', ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarNumberWithExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], jsonMime)
    assert.strictEqual(result, '24')
  })

  test('reads a string array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'StringArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], jsonMime) as string
    const parsed = JSON.parse(result)
    assert.deepEqual(parsed, ['test', 'other'])
  })

  test('reads a number array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'NumberArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], jsonMime) as string
    const parsed = JSON.parse(result)
    assert.deepEqual(parsed, [1, 2])
  })

  test('reads a complex value value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarObjectUnionExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], jsonMime) as string
    const parsed = JSON.parse(result)
    assert.strictEqual(parsed.name, 'Pawel Uchida-Psztyc', 'has example.name')
    assert.strictEqual(parsed.id, '128a654bc54d898e43f', 'has example.id')
    assert.strictEqual(parsed.age, 30, 'has example.age')
    assert.strictEqual(parsed.sex, 'male', 'has example.sex')
    assert.strictEqual(parsed.newsletter, false, 'has example.newsletter')
    assert.strictEqual(parsed.tosAccepted, true, 'has example.tosAccepted')
  })
})

test.group(xmlMime, (group) => {
  let reader: ApiExampleGenerator
  let model: IAmfDocument

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

  test('returns the value when corresponds to the mime type', ({ assert }) => {
    const example: IApiDataExample = {
      id: '',
      strict: true,
      types: [ns.aml.vocabularies.document.NamedExamples],
      value: '<feed></feed>',
      customDomainProperties: [],
    }
    const result = reader.read(example, xmlMime)
    assert.strictEqual(result, '<feed></feed>')
  })

  test('reads a number scalar value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarNumberWithExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], xmlMime, shape)
    assert.strictEqual(result, '<ScalarNumberWithExample>24</ScalarNumberWithExample>\n')
  })

  test('reads a string array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'StringArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], xmlMime, shape)

    const parser = new DOMParser()
    const schema = parser.parseFromString(`<root>${result}</root>`, xmlMime)

    const nodes = schema.getElementsByTagName('StringArrayExample')
    assert.lengthOf(nodes, 2, 'has both example values')
    assert.equal((nodes[0].textContent as string).trim(), 'test', 'has first example value')
    assert.equal((nodes[1].textContent as string).trim(), 'other', 'has second example value')
  })

  test('reads an object array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'XmlArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], xmlMime, shape) as string

    const parser = new DOMParser()
    const schema = parser.parseFromString(result, xmlMime)

    const root = schema.getElementsByTagName('XmlArrayExample')[0]
    assert.ok(root, 'has the root node')
    const street = root.getElementsByTagName('street')[0]
    const city = root.getElementsByTagName('city')[0]
    assert.ok(street, 'has the street node')
    assert.ok(city, 'has the city node')
    assert.equal((street.textContent as string).trim(), '1234 Market street')
    assert.equal((city.textContent as string).trim(), 'San Francisco')
  })

  test('reads a number array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'NumberArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], xmlMime, shape)

    const parser = new DOMParser()
    const schema = parser.parseFromString(`<root>${result}</root>`, xmlMime)

    const nodes = schema.getElementsByTagName('NumberArrayExample')
    assert.lengthOf(nodes, 2, 'has both example values')
    assert.equal((nodes[0].textContent as string).trim(), '1', 'has first example value')
    assert.equal((nodes[1].textContent as string).trim(), '2', 'has second example value')
  })

  test('reads a complex value value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarObjectUnionExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], xmlMime, shape) as string

    const parser = new DOMParser()
    const schema = parser.parseFromString(result, xmlMime)

    const root = schema.getElementsByTagName('ScalarObjectUnionExample')[0]
    assert.ok(root, 'has the root node')
    const name = root.getElementsByTagName('name')[0]
    const id = root.getElementsByTagName('id')[0]
    const age = root.getElementsByTagName('age')[0]
    const sex = root.getElementsByTagName('sex')[0]
    const newsletter = root.getElementsByTagName('newsletter')[0]
    const tosAccepted = root.getElementsByTagName('tosAccepted')[0]
    assert.ok(name, 'has the name node')
    assert.ok(id, 'has the id node')
    assert.ok(age, 'has the age node')
    assert.ok(sex, 'has the sex node')
    assert.ok(newsletter, 'has the newsletter node')
    assert.ok(tosAccepted, 'has the tosAccepted node')
    assert.equal((name.textContent as string).trim(), 'Pawel Uchida-Psztyc')
    assert.equal((id.textContent as string).trim(), '128a654bc54d898e43f')
    assert.equal((age.textContent as string).trim(), '30')
    assert.equal((sex.textContent as string).trim(), 'male')
    assert.equal((newsletter.textContent as string).trim(), 'false')
    assert.equal((tosAccepted.textContent as string).trim(), 'true')
  })
})

test.group(urlEncodedMime, (group) => {
  let reader: ApiExampleGenerator
  let model: IAmfDocument

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

  test('returns the value when corresponds to the mime type', ({ assert }) => {
    const example: IApiDataExample = {
      id: '',
      strict: true,
      types: [ns.aml.vocabularies.document.NamedExamples],
      value: 'a=b&c=d',
      customDomainProperties: [],
    }
    const result = reader.read(example, urlEncodedMime)
    assert.strictEqual(result, 'a=b&c=d')
  })

  test('reads a number scalar value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarNumberWithExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], urlEncodedMime, shape)
    assert.strictEqual(result, 'ScalarNumberWithExample=24')
  })

  test('reads a string array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'StringArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], urlEncodedMime, shape)

    assert.strictEqual(result, 'StringArrayExample[]=test&StringArrayExample[]=other')
  })

  test('reads an object array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'XmlArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], urlEncodedMime, shape)

    assert.strictEqual(result, 'street=1234+Market+street&city=San+Francisco')
  })

  test('reads a number array value', async ({ assert }) => {
    const shape = loader.getShape(model, 'NumberArrayExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], urlEncodedMime, shape)

    assert.strictEqual(result, 'NumberArrayExample[]=1&NumberArrayExample[]=2')
  })

  test('reads a complex value value', async ({ assert }) => {
    const shape = loader.getShape(model, 'ScalarObjectUnionExample') as IApiAnyShape
    const result = reader.read(shape.examples[0], urlEncodedMime, shape) as string
    const parsed = new URLSearchParams(result)
    assert.equal(parsed.get('name'), 'Pawel Uchida-Psztyc')
    assert.equal(parsed.get('id'), '128a654bc54d898e43f')
    assert.equal(parsed.get('age'), '30')
    assert.equal(parsed.get('sex'), 'male')
    assert.equal(parsed.get('newsletter'), 'false')
    assert.equal(parsed.get('tosAccepted'), 'true')
  })
})
