import { test } from '@japa/runner'
import sinon from 'sinon'
import { EventTypes } from '../../../src/events/EventTypes.js'
import { Events } from '../../../src/events/Events.js'
import { ensureUnique } from './EventsTestHelpers.js'
import './events_polyfills.js'

test.group('EventTypes.Amf', () => {
  test('has the namespace', ({ assert }) => {
    assert.typeOf(EventTypes.Amf, 'object')
  })

  test('has frozen namespace', ({ assert }) => {
    assert.throws(() => {
      // @ts-expect-error Used in testing
      EventTypes.Amf = { read: '' }
    })
  })

  test('has "{prop}" property')
    .with([
      {
        prop: 'processApiLink',
        value: 'amfprocessapilink',
      },
      {
        prop: 'processBuffer',
        value: 'amfprocessbuffer',
      },
      {
        prop: 'processApiFile',
        value: 'amfprocessapifile',
      },
      {
        prop: 'selectApiMainFile',
        value: 'amfselectapimainfile',
      },
    ])
    .run(({ assert }, { prop, value }) => {
      // @ts-expect-error Used in testing
      assert.equal(EventTypes.Amf[prop], value)
    })

  test('has unique events on the namespace', () => {
    ensureUnique('EventTypes.Amf', EventTypes.Amf)
  })
})

test.group('processApiLink()', () => {
  const url = 'https://api.com'
  const mainFile = 'api.raml'
  const md5 = '123qwe'
  const packaging = 'zip'

  test('dispatches the event', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Amf.processApiLink, spy)
    await Events.Amf.processApiLink(url)
    assert.isTrue(spy.calledOnce)
  })

  test('sets the arguments on the detail object', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Amf.processApiLink, spy)
    await Events.Amf.processApiLink(url, mainFile, md5, packaging)
    globalThis.removeEventListener(EventTypes.Amf.processApiLink, spy)
    const { detail } = spy.args[0][0]
    assert.equal(detail.url, url, 'url is set')
    assert.equal(detail.mainFile, mainFile, 'mainFile is set')
    assert.equal(detail.md5, md5, 'md5 is set')
    assert.equal(detail.packaging, packaging, 'packaging is set')
  })

  test('returns the result', async ({ assert }) => {
    const parseResult = { model: '', type: { type: 'RAML 1.0' } }
    function handler(e: CustomEvent): void {
      globalThis.removeEventListener(EventTypes.Amf.processApiLink, handler as EventListener)
      e.preventDefault()
      e.detail.result = Promise.resolve(parseResult)
    }
    globalThis.addEventListener(EventTypes.Amf.processApiLink, handler as EventListener)
    const result = await Events.Amf.processApiLink(url)
    assert.equal(result, parseResult)
  })
})

test.group('processApiFile()', () => {
  const file = new File([], 'test.txt')

  test('dispatches the event', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Amf.processApiFile, spy)
    await Events.Amf.processApiFile(file)
    globalThis.removeEventListener(EventTypes.Amf.processApiFile, spy)
    assert.isTrue(spy.calledOnce)
  })

  test('sets the arguments on the detail object', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Amf.processApiFile, spy)
    await Events.Amf.processApiFile(file)
    globalThis.removeEventListener(EventTypes.Amf.processApiFile, spy)
    const { detail } = spy.args[0][0]
    assert.deepEqual(detail.file, file, 'file is set')
  })

  test('returns the result', async ({ assert }) => {
    const parseResult = { model: '', type: { type: 'RAML 1.0' } }
    function handler(e: CustomEvent): void {
      globalThis.removeEventListener(EventTypes.Amf.processApiFile, handler as EventListener)
      e.preventDefault()
      e.detail.result = Promise.resolve(parseResult)
    }
    globalThis.addEventListener(EventTypes.Amf.processApiFile, handler as EventListener)
    const result = await Events.Amf.processApiFile(file)
    assert.equal(result, parseResult)
  })
})

test.group('selectApiMainFile()', () => {
  const candidates = ['test']

  test('dispatches the event', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Amf.selectApiMainFile, spy)
    await Events.Amf.selectApiMainFile(candidates)
    globalThis.removeEventListener(EventTypes.Amf.selectApiMainFile, spy)
    assert.isTrue(spy.calledOnce)
  })

  test('sets the arguments on the detail object', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Amf.selectApiMainFile, spy)
    await Events.Amf.selectApiMainFile(candidates)
    globalThis.removeEventListener(EventTypes.Amf.selectApiMainFile, spy)
    const { detail } = spy.args[0][0]
    assert.deepEqual(detail.candidates, candidates, 'candidates is set')
  })

  test('returns the result', async ({ assert }) => {
    const selected = 'test'
    function handler(e: CustomEvent): void {
      globalThis.removeEventListener(EventTypes.Amf.selectApiMainFile, handler as EventListener)
      e.preventDefault()
      e.detail.result = Promise.resolve(selected)
    }
    globalThis.addEventListener(EventTypes.Amf.selectApiMainFile, handler as EventListener)
    const result = await Events.Amf.selectApiMainFile(candidates)
    assert.equal(result, selected)
  })
})
