import { assert, fixture, html, nextFrame } from '@open-wc/testing'
import sinon from 'sinon'
import UiChip from '../../../src/md/chip/internals/Chip.js'
import { UiMock } from '../../helpers/UiMock.js'
import { ColorConverter } from '@api-client/core/browser.js'
import '../../../src/md/chip/ui-chip.js'
import '../../../src/md/icons/ui-icon.js'

describe('md', () => {
  describe('UiButton', () => {
    describe('Assist chip', () => {
      async function basicFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="assist">Enabled</ui-chip>`)
      }

      async function disabledFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="assist" disabled>Disabled</ui-chip>`)
      }

      async function iconFixture(): Promise<UiChip> {
        return fixture(
          html`<ui-chip type="assist">
            <ui-icon slot="icon" icon="add"></ui-icon>
            With icon
          </ui-chip>`
        )
      }

      async function elevatedFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="assist" elevated>Elevated</ui-chip>`)
      }

      async function disabledElevatedFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="assist" elevated disabled>Elevated</ui-chip>`)
      }

      it('renders border around a regular assist chip', async () => {
        const element = await basicFixture()
        const styles = getComputedStyle(element)
        const m3Color = styles.getPropertyValue('--md-sys-color-outline-variant')
        const borderColor = ColorConverter.normalizeColor(styles.borderColor)
        const compareColor = ColorConverter.normalizeColor(m3Color)
        assert.equal(borderColor, compareColor, 'has the m3 color')
        assert.equal(styles.borderWidth, '1px', 'has the width')
      })

      it('dispatches the click event on space press', async () => {
        const element = await basicFixture()
        const spy = sinon.spy()
        element.addEventListener('click', spy)
        await UiMock.keyPress(element, 'Space')
        assert.isTrue(spy.calledOnce)
      })

      it('dispatches the click event on space enter', async () => {
        const element = await basicFixture()
        const spy = sinon.spy()
        element.addEventListener('click', spy)
        await UiMock.keyPress(element, 'Enter')
        assert.isTrue(spy.calledOnce)
      })

      it('does not dispatch the click event on disabled chip', async () => {
        const element = await disabledFixture()
        const spy = sinon.spy()
        element.addEventListener('click', spy)
        await UiMock.keyPress(element, 'Space')
        await UiMock.keyPress(element, 'Enter')
        assert.isFalse(spy.called)
      })

      it('does not dispatch the select event', async () => {
        const element = await disabledFixture()
        const spy = sinon.spy()
        element.addEventListener('event', spy)
        await UiMock.keyPress(element, 'Space')
        assert.isFalse(spy.called)
      })

      it('renders suffix icon', async () => {
        const element = await iconFixture()
        const surface = element.shadowRoot!.querySelector<HTMLDivElement>('.surface')!
        assert.isTrue(surface.classList.contains('has-icon'), 'the surface has the has-icon class')
        const contentStyles = getComputedStyle(surface)
        assert.equal(contentStyles.paddingLeft.trim(), '8px', 'the container has the padding')
        const icon = element.querySelector('ui-icon')!
        const iconStyles = getComputedStyle(icon)
        assert.equal(iconStyles.marginRight.trim(), '8px', 'the icon has the margin')
      })

      it('has elevated styles', async () => {
        const element = await elevatedFixture()
        const surface = element.shadowRoot!.querySelector<HTMLDivElement>('.surface')!
        const containerStyles = getComputedStyle(surface)
        assert.equal(containerStyles.borderWidth, '0px', 'has no border')
        assert.isNotEmpty(containerStyles.boxShadow, 'has a box shadow')
      })

      it('has no elevated styles when disabled', async () => {
        const element = await disabledElevatedFixture()
        const surface = element.shadowRoot!.querySelector<HTMLDivElement>('.surface')!
        const containerStyles = getComputedStyle(surface)
        assert.equal(containerStyles.boxShadow, 'none', 'has no box shadow')
      })
    })

    describe('Filter chip', () => {
      async function basicFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="filter">Enabled</ui-chip>`)
      }

      async function disabledFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="filter" disabled>Disabled</ui-chip>`)
      }

      async function removableFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="filter" removable>removable</ui-chip>`)
      }

      async function listFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="filter" list>List</ui-chip>`)
      }

      it('renders border around the chip', async () => {
        const element = await basicFixture()
        const styles = getComputedStyle(element)
        const m3Color = styles.getPropertyValue('--md-sys-color-outline-variant')
        const borderColor = ColorConverter.normalizeColor(styles.borderColor)
        const compareColor = ColorConverter.normalizeColor(m3Color)
        assert.equal(borderColor, compareColor, 'has the m3 color')
        assert.equal(styles.borderWidth, '1px', 'has the width')
      })

      it('dispatches the click event on space press', async () => {
        const element = await basicFixture()
        const spy = sinon.spy()
        element.addEventListener('click', spy)
        await UiMock.keyPress(element, 'Space')
        assert.isTrue(spy.calledOnce)
      })

      it('dispatches the click event on enter', async () => {
        const element = await basicFixture()
        const spy = sinon.spy()
        element.addEventListener('click', spy)
        await UiMock.keyPress(element, 'Enter')
        assert.isTrue(spy.calledOnce)
      })

      it('toggles the chip', async () => {
        const element = await basicFixture()
        element.click()
        await nextFrame()
        assert.isTrue(element.checked, 'the element is checked')
        const icon = element.shadowRoot!.querySelector('.leading-icon.check-mark')
        assert.ok(icon, 'renders the check mark icon')
      })

      it('does not dispatch the click event on disabled chip', async () => {
        const element = await disabledFixture()
        const spy = sinon.spy()
        element.addEventListener('click', spy)
        await UiMock.keyPress(element, 'Space')
        await UiMock.keyPress(element, 'Enter')
        assert.isFalse(spy.called)
      })

      it('dispatches the select event', async () => {
        const element = await basicFixture()
        const spy = sinon.spy()
        element.addEventListener('select', spy)
        await UiMock.keyPress(element, 'Space')
        assert.isTrue(spy.calledOnce)
      })

      it('does not dispatch the select event when disabled', async () => {
        const element = await disabledFixture()
        const spy = sinon.spy()
        element.addEventListener('select', spy)
        await UiMock.keyPress(element, 'Space')
        assert.isFalse(spy.called)
      })

      it('does not render the close icon', async () => {
        const element = await removableFixture()
        const node = element.shadowRoot!.querySelector('.trailing-icon')
        assert.notOk(node)
      })

      it('renders the list icon', async () => {
        const element = await listFixture()
        const node = element.shadowRoot!.querySelector('.trailing-icon')
        assert.ok(node, 'has the icon')
      })

      it('is accessible in a regular state', async () => {
        const element = await basicFixture()
        await assert.isAccessible(element)
      })

      it('is accessible when removable', async () => {
        const element = await removableFixture()
        await assert.isAccessible(element)
      })

      it('is accessible when has a list', async () => {
        const element = await listFixture()
        await assert.isAccessible(element)
      })

      it('is accessible when active', async () => {
        const element = await listFixture()
        element.click()
        await nextFrame()
        await assert.isAccessible(element)
      })
    })
  })
})
