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

  test('has OAuth2 namespace', ({ assert }) => {
    assert.typeOf(EventTypes.Authorization.OAuth2, 'object')
  })

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

  test('has "{prop}" property for OAuth2')
    .with([
      { prop: 'authorize', value: 'oauth2authorize' },
      { prop: 'removeToken', value: 'oauth2removetoken' },
    ])
    .run(({ assert }, { prop, value }) => {
      // @ts-expect-error Used in testing
      assert.equal(EventTypes.Authorization.OAuth2[prop], value)
    })

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

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

  test('has "{prop}" property for Oidc')
    .with([
      { prop: 'authorize', value: 'oidcauthorize' },
      { prop: 'removeTokens', value: 'oidcremovetokens' },
    ])
    .run(({ assert }, { prop, value }) => {
      // @ts-expect-error Used in testing
      assert.equal(EventTypes.Authorization.Oidc[prop], value)
    })

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

test.group('Events > Authorization > Events.Authorization > OAuth2 > authorize()', () => {
  const config = { responseType: 'implicit' }

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

  test('has the configuration on the detail', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Authorization.OAuth2.authorize, spy)
    Events.Authorization.OAuth2.authorize(config)
    globalThis.removeEventListener(EventTypes.Authorization.OAuth2.authorize, spy)
    const e = spy.args[0][0]
    const cnf = e.detail
    delete cnf.result
    assert.deepEqual(cnf, config)
  })
})

test.group('Events > Authorization > Events.Authorization > OAuth2 > removeToken()', () => {
  const config = { clientId: 'id', authorizationUri: 'test' }

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

  test('has the configuration on the detail', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Authorization.OAuth2.removeToken, spy)
    Events.Authorization.OAuth2.removeToken(config)
    globalThis.removeEventListener(EventTypes.Authorization.OAuth2.removeToken, spy)
    const e = spy.args[0][0]
    const cnf = e.detail
    delete cnf.result
    assert.deepEqual(cnf, config)
  })
})

test.group('Events > Authorization > Events.Authorization > Oidc > authorize()', () => {
  const config = { responseType: 'implicit' }

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

  test('has the configuration on the detail', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Authorization.Oidc.authorize, spy)
    Events.Authorization.Oidc.authorize(config)
    globalThis.removeEventListener(EventTypes.Authorization.Oidc.authorize, spy)
    const e = spy.args[0][0]
    const cnf = e.detail
    delete cnf.result
    assert.deepEqual(cnf, config)
  })
})

test.group('Events > Authorization > Events.Authorization > Oidc > removeToken()', () => {
  const config = { clientId: 'id', authorizationUri: 'test' }

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

  test('has the configuration on the detail', async ({ assert }) => {
    const spy = sinon.spy()
    globalThis.addEventListener(EventTypes.Authorization.Oidc.removeTokens, spy)
    Events.Authorization.Oidc.removeTokens(config) // Calling removeToken
    globalThis.removeEventListener(EventTypes.Authorization.Oidc.removeTokens, spy)
    const e = spy.args[0][0]
    const cnf = e.detail
    delete cnf.result
    assert.deepEqual(cnf, config)
  })
})
