import { assert, fixture, html, nextFrame, oneEvent } from '@open-wc/testing'
import sinon from 'sinon'
import UiIconButton from '../../../src/md/icon-button/internals/IconButton.js'
import { UiMock } from '../../helpers/UiMock.js'
import { IconType } from '../../../src/md/icons/Icons.js'

import '../../../src/md/icon-button/ui-icon-button.js'
import '../../../src/md/icons/ui-icon.js'

describe('md', () => {
  describe('UiIconButton', () => {
    async function basicFixture(icon: IconType): Promise<UiIconButton> {
      return fixture(html`<ui-icon-button><ui-icon .icon="${icon}"></ui-icon></ui-icon-button>`)
    }

    async function toggleFixture(icon: IconType): Promise<UiIconButton> {
      return fixture(html`<ui-icon-button toggle><ui-icon .icon="${icon}"></ui-icon></ui-icon-button>`)
    }

    async function tabindexFixture(icon: IconType): Promise<UiIconButton> {
      return fixture(html`<ui-icon-button tabindex="-1"><ui-icon .icon="${icon}"></ui-icon></ui-icon-button>`)
    }

    async function labeledFixture(icon: IconType): Promise<UiIconButton> {
      return fixture(
        html`<ui-icon-button aria-label="My button" title="My button description"
          ><ui-icon .icon="${icon}"></ui-icon
        ></ui-icon-button>`
      )
    }

    async function labeledToggleFixture(icon: IconType): Promise<UiIconButton> {
      return fixture(
        html`<ui-icon-button aria-label="My button" title="My button description" toggle
          ><ui-icon .icon="${icon}"></ui-icon
        ></ui-icon-button>`
      )
    }

    async function labeledSelectedToggleFixture(icon: IconType): Promise<UiIconButton> {
      return fixture(
        html`<ui-icon-button aria-label="My button" title="My button description" toggle selected
          ><ui-icon .icon="${icon}"></ui-icon
        ></ui-icon-button>`
      )
    }

    describe('toggle button', () => {
      it('sets button selected when pressing the button with space', async () => {
        const button = await toggleFixture('add')
        await UiMock.keyPress(button, 'Space')
        await nextFrame()
        assert.isTrue(button.selected, 'button is selected')
      })

      it('sets button selected when pressing the button with enter', async () => {
        const button = await toggleFixture('add')
        await UiMock.keyPress(button, 'Enter')
        await nextFrame()
        assert.isTrue(button.selected, 'button is selected')
      })

      it('sets button selected when clicking on the button', async () => {
        const button = await toggleFixture('add')
        button.click()
        await nextFrame()
        assert.isTrue(button.selected, 'button is selected')
      })

      it('sets "aria-pressed" when toggling the button', async () => {
        const button = await toggleFixture('add')
        await UiMock.keyPress(button, 'Space')
        await nextFrame()
        assert.equal(button.getAttribute('aria-pressed'), 'true')
      })

      it('dispatches the "toggle" event when toggling', async () => {
        const button = await toggleFixture('add')
        UiMock.keyPress(button, 'Space')
        const e = await oneEvent(button, 'toggle')
        assert.ok(e, 'dispatches the event')
      })

      it('does not dispatch the "toggle" event toggling attribute', async () => {
        const button = await toggleFixture('add')
        const spy = sinon.spy()
        button.addEventListener('toggle', spy)
        button.selected = true
        await nextFrame()
        assert.isFalse(spy.called)
      })

      it('dispatches the "click" event when toggling', async () => {
        const button = await toggleFixture('add')
        const spy = sinon.spy()
        button.addEventListener('click', spy)
        await UiMock.keyPress(button, 'Space')
        await nextFrame()
        assert.isTrue(spy.called)
      })
    })

    describe('non-toggle button', () => {
      it('clicks when pressing the button with space', async () => {
        const button = await basicFixture('add')
        const spy = sinon.spy()
        button.addEventListener('click', spy)
        await UiMock.keyPress(button, 'Space')
        await nextFrame()
        assert.isTrue(spy.calledOnce, 'event dispatched only once')
      })

      it('clicks when pressing the button with enter', async () => {
        const button = await basicFixture('add')
        const spy = sinon.spy()
        button.addEventListener('click', spy)
        await UiMock.keyPress(button, 'Enter')
        await nextFrame()
        assert.isTrue(spy.calledOnce, 'event dispatched only once')
      })
    })

    describe('a11y', () => {
      it('has the role', async () => {
        const button = await basicFixture('add')
        assert.equal(button.getAttribute('role'), 'button')
      })

      it('has the tabindex', async () => {
        const button = await basicFixture('add')
        assert.equal(button.getAttribute('tabindex'), '0')
      })

      it('respects the existing tabindex', async () => {
        const button = await tabindexFixture('add')
        assert.equal(button.getAttribute('tabindex'), '-1')
      })

      it('is accessible for regular button', async () => {
        const button = await labeledFixture('add')
        await assert.isAccessible(button)
      })

      it('is accessible when not selected', async () => {
        const button = await labeledToggleFixture('add')
        await assert.isAccessible(button)
      })

      it('is accessible when selected', async () => {
        const button = await labeledSelectedToggleFixture('add')
        await assert.isAccessible(button)
      })
    })
  })
})
