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('Events > Telemetry > EventTypes.Telemetry', () => {
  test('has the namespace', ({ assert }) => {
    assert.typeOf(EventTypes.Telemetry, 'object')
  })

  test('has "{prop}" property')
    .with([
      { prop: 'view', value: 'telemetryscreenview' },
      { prop: 'event', value: 'telemetryevent' },
      { prop: 'exception', value: 'telemetryexception' },
      { prop: 'social', value: 'telemetrysocial' },
      { prop: 'timing', value: 'telemetrytiming' },
    ])
    .run(({ assert }, { prop, value }) => {
      // @ts-expect-error Used in testing
      assert.equal(EventTypes.Telemetry[prop], value)
    })

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

test.group('Events > Telemetry > Events.Telemetry > view()', () => {
  const screenName = 'test-screen'

  test('dispatches the event', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.view, spy)
    Events.Telemetry.view(screenName)
    globalThis.removeEventListener(EventTypes.Telemetry.view, spy)
    assert.isTrue(spy.calledOnce)
  })

  test('the event has screen name on the detail', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.view, spy)
    Events.Telemetry.view(screenName)
    globalThis.removeEventListener(EventTypes.Telemetry.view, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.equal(e.detail.screenName, screenName)
  })

  test('the event has the custom configuration', async ({ assert }) => {
    const spy = sinon.spy()
    const custom = {
      customMetrics: [{ index: 1, value: 1 }],
      customDimensions: [{ index: 1, value: 'test' }],
    }
    globalThis.addEventListener(EventTypes.Telemetry.view, spy)
    Events.Telemetry.view(screenName, undefined, custom)
    globalThis.removeEventListener(EventTypes.Telemetry.view, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
    assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
  })
})

test.group('Events > Telemetry > Events.Telemetry > event()', () => {
  const init = {
    category: 'e-cat',
    action: 'e-act',
    label: 'e-label',
    value: 1,
  }

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

  test('the event has the detail object', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.event, spy)
    Events.Telemetry.event(init)
    globalThis.removeEventListener(EventTypes.Telemetry.event, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.deepEqual(e.detail, init)
  })
})

test.group('Events > Telemetry > Events.Telemetry > exception()', () => {
  const description = 'event-exception'
  const fatal = true

  test('dispatches the event', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
    Events.Telemetry.exception(description)
    globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
    assert.isTrue(spy.calledOnce)
  })

  test('the event has the description', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
    Events.Telemetry.exception(description)
    globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.equal(e.detail.description, description)
  })

  test('the event has the fatal', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
    Events.Telemetry.exception(description, fatal)
    globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.equal(e.detail.fatal, fatal)
  })

  test('the event has custom configuration', async ({ assert }) => {
    const spy = sinon.spy()
    const custom = {
      customMetrics: [{ index: 1, value: 1 }],
      customDimensions: [{ index: 1, value: 'test' }],
    }
    globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
    Events.Telemetry.exception(description, fatal, undefined, custom)
    globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
    assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
  })
})

test.group('Events > Telemetry > Events.Telemetry > social()', () => {
  const init = {
    network: 'e-network',
    action: 'e-action',
    target: 'e-target',
  }

  test('dispatches the event', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.social, spy)
    Events.Telemetry.social(init.network, init.action, init.target)
    globalThis.removeEventListener(EventTypes.Telemetry.social, spy)
    assert.isTrue(spy.calledOnce)
  })

  test('the event has the detail object', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.social, spy)
    Events.Telemetry.social(init.network, init.action, init.target)
    globalThis.removeEventListener(EventTypes.Telemetry.social, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.deepEqual(e.detail, init)
  })

  test('the event has custom configuration', async ({ assert }) => {
    const spy = sinon.spy()
    const custom = {
      customMetrics: [{ index: 1, value: 1 }],
      customDimensions: [{ index: 1, value: 'test' }],
    }
    globalThis.addEventListener(EventTypes.Telemetry.social, spy)
    Events.Telemetry.social(init.network, init.action, init.target, undefined, custom)
    globalThis.removeEventListener(EventTypes.Telemetry.social, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
    assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
  })
})

test.group('Events > Telemetry > Events.Telemetry > timing()', () => {
  const init = {
    category: 'e-category',
    variable: 'e-variable',
    value: 100,
    label: 'e-label',
  }

  test('dispatches the event', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.timing, spy)
    Events.Telemetry.timing(init.category, init.variable, init.value, init.label)
    globalThis.removeEventListener(EventTypes.Telemetry.timing, spy)
    assert.isTrue(spy.calledOnce)
  })

  test('the event has the detail object', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.timing, spy)
    Events.Telemetry.timing(init.category, init.variable, init.value, init.label)
    globalThis.removeEventListener(EventTypes.Telemetry.timing, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.deepEqual(e.detail, init)
  })

  test('the event has custom configuration', async ({ assert }) => {
    const spy = sinon.spy()
    const custom = {
      customMetrics: [{ index: 1, value: 1 }],
      customDimensions: [{ index: 1, value: 'test' }],
    }
    globalThis.addEventListener(EventTypes.Telemetry.timing, spy)
    Events.Telemetry.timing(init.category, init.variable, init.value, init.label, undefined, custom)
    globalThis.removeEventListener(EventTypes.Telemetry.timing, spy)
    const e = spy.args[0][0] as CustomEvent
    assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
    assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
  })
})

test.group('Events > Telemetry > Events.Telemetry > State', () => {
  test('set()', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Telemetry.State.set, spy)
    Events.Telemetry.State.set()
    globalThis.removeEventListener(EventTypes.Telemetry.State.set, spy)
    assert.isTrue(spy.calledOnce)
  })
})
