import { test } from '@japa/runner'
import { Json2Xml } from '../../../src/data/Json2Xml.js'

test.group('integration tests', (group) => {
  let converter: Json2Xml
  group.each.setup(() => {
    converter = new Json2Xml()
  })

  test('converts a simple JSON object', async ({ assert }) => {
    assert.equal(await converter.serializeJson({ a: {} }), '<root><a/></root>')
    assert.equal(await converter.serializeJson({ a: null }), '<root><a/></root>')
    assert.equal(await converter.serializeJson({ a: [] }), '<root><a/></root>')
    assert.equal(await converter.serializeJson({ a: -1 }), '<root><a>-1</a></root>')
    assert.equal(await converter.serializeJson({ a: false }), '<root><a>false</a></root>')
    assert.equal(await converter.serializeJson({ a: 'a value' }), '<root><a>a value</a></root>')
    assert.equal(await converter.serializeJson({ a: 'b' }), '<root><a>b</a></root>')
  })

  test('converts a simple JSON array', async ({ assert }) => {
    assert.equal(await converter.serializeJson([{ a: {} }]), '<root><a/></root>')
    assert.equal(await converter.serializeJson([{ a: null }]), '<root><a/></root>')
    assert.equal(await converter.serializeJson([{ a: [] }]), '<root><a/></root>')
    assert.equal(await converter.serializeJson([{ a: -1 }]), '<root><a>-1</a></root>')
    assert.equal(await converter.serializeJson([{ a: false }]), '<root><a>false</a></root>')
    assert.equal(await converter.serializeJson([{ a: 'a value' }]), '<root><a>a value</a></root>')
    assert.equal(await converter.serializeJson([{ a: 'b' }]), '<root><a>b</a></root>')
    assert.equal(
      await converter.serializeJson([{ a: 'b' }, { c: false }, { d: 123 }, { e: -0.7 }]),
      '<root><a>b</a><c>false</c><d>123</d><e>-0.7</e></root>'
    )
  })

  test('converts deeply nested JSON object', async ({ assert }) => {
    assert.equal(
      await converter.serializeJson([{ a: [{ b: [{ c: 1 }, { c: 2 }, { c: 3 }] }] }]),
      '<root><a><b><c>1</c><c>2</c><c>3</c></b></a></root>'
    )
    assert.equal(
      await converter.serializeJson([{ a: [{ b: [{ c: 1 }, { c: [] }, { c: 3 }] }] }]),
      '<root><a><b><c>1</c><c/><c>3</c></b></a></root>'
    )
  })

  test('converts an object with an array', async ({ assert }) => {
    const input = {
      items: [
        {
          name: 'n1',
          value: 'v1',
        },
        {
          name: 'n2',
        },
      ],
    }
    await converter.convert(input)
    const result = await converter.serialize()
    assert.equal(result, '<root><items><name>n1</name><value>v1</value><name>n2</name></items></root>')
  })

  test('accepts a custom root element', async ({ assert }) => {
    assert.equal(await converter.serializeJson({ a: {} }, 'items'), '<items><a/></items>')
  })

  test('wraps a value into a cdata', async ({ assert }) => {
    assert.equal(
      await converter.serializeJson({ a: '<span>test</span>' }),
      '<root><a><![CDATA[<span>test</span>]]></a></root>'
    )
  })

  test('uses the synthetic APIimplementation in node', ({ assert }) => {
    assert.isTrue(converter.syntheticApi)
  })

  test('throws when parsing a scalar value', async ({ assert }) => {
    let message: string | undefined
    try {
      await converter.serializeJson('test')
    } catch (e) {
      const err = e as Error
      message = err.message
    }
    assert.equal(message, 'Invalid object. Unable to process non-object property.')
  })
})

test.group('serialize()', (group) => {
  let converter: Json2Xml
  group.each.setup(() => {
    converter = new Json2Xml()
  })

  test('throws when no document', async ({ assert }) => {
    let message: string | undefined
    try {
      await converter.serialize()
    } catch (e) {
      const err = e as Error
      message = err.message
    }
    assert.equal(message, 'No document created.')
  })
})

test.group('processArray()', (group) => {
  let converter: Json2Xml
  group.each.setup(() => {
    converter = new Json2Xml()
  })

  test('throws when no array argument', ({ assert }) => {
    let message: string | undefined
    try {
      // @ts-expect-error Used in testing
      converter.processArray({})
    } catch (e) {
      const err = e as Error
      message = err.message
    }
    assert.equal(message, 'Expected array. object given.')
  })
})

test.group('processObject()', (group) => {
  let converter: Json2Xml
  group.each.setup(() => {
    converter = new Json2Xml()
  })

  test('throws when not initialized', ({ assert }) => {
    let message: string | undefined
    try {
      converter.processObject({})
    } catch (e) {
      const err = e as Error
      message = err.message
    }
    assert.equal(message, 'Library not initialized. Call the convert() function.')
  })
})

test.group('processScalar()', (group) => {
  let converter: Json2Xml
  group.each.setup(() => {
    converter = new Json2Xml()
  })

  test('throws when not initialized', ({ assert }) => {
    let message: string | undefined
    try {
      converter.processScalar('test')
    } catch (e) {
      const err = e as Error
      message = err.message
    }
    assert.equal(message, 'Library not initialized. Call the convert() function.')
  })

  test('throws when invalid type', async ({ assert }) => {
    const doc = await converter.getDocument()
    converter.doc = doc
    const rootNode = doc.createElement('root')
    doc.append(rootNode)
    converter.currentNode = rootNode

    let message: string | undefined
    try {
      // @ts-expect-error Used in testing
      converter.processScalar({})
    } catch (e) {
      const err = e as Error
      message = err.message
    }
    assert.equal(message, 'Invalid scalar type: object.')
  }).skip()
})
