import { assert, fixture, html } from '@open-wc/testing'
import sinon from 'sinon'
import UiDialog, { UiDialogClosingReason } from '../../../src/md/dialog/internals/Dialog.js'
import { UiMock } from '../../helpers/UiMock.js'
import UiButton from '../../../src/md/button/internals/button.js'

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

describe('md', () => {
  describe('Dialog', () => {
    async function basicFixture(): Promise<UiDialog> {
      return fixture(html` <ui-dialog> Content </ui-dialog>`)
    }

    async function modalFixture(): Promise<UiDialog> {
      return fixture(html` <ui-dialog modal> Content </ui-dialog>`)
    }

    async function iconFixture(): Promise<UiDialog> {
      return fixture(
        html` <ui-dialog>
          <ui-icon slot="icon" icon="deleteOutline"></ui-icon>
          Content
        </ui-dialog>`
      )
    }

    async function titleFixture(): Promise<UiDialog> {
      return fixture(
        html` <ui-dialog>
          <span slot="title">The title</span>
          Content
        </ui-dialog>`
      )
    }

    async function buttonFixture(): Promise<UiDialog> {
      return fixture(
        html` <ui-dialog>
          Content
          <ui-button color="text" slot="button">Learn more</ui-button>
          <ui-button color="text" slot="button" value="dismiss">Cancel</ui-button>
          <ui-button color="text" slot="button" value="confirm">Accept</ui-button>
        </ui-dialog>`
      )
    }

    describe('open/close', () => {
      it('opens the native dialog via the "open" attribute', async () => {
        const element = await basicFixture()
        assert.isFalse(element.dialog.open, 'native dialog is closed initially')

        element.open = true
        await element.updateComplete
        assert.isTrue(element.dialog.open, 'native dialog is opened')

        element.open = false
        await element.updateComplete
        assert.isFalse(element.dialog.open, 'native dialog is closed again')
      })

      it('closes the dialog via the Escape button', async () => {
        const element = await buttonFixture()
        // the dialog requires a focusable element
        const button = element.querySelector('ui-button')!
        button.focus()
        await UiMock.keyPress(element, 'Escape', { key: 'Escape' })
        assert.equal(element.open, false)
      })

      it('closes the dialog via the dismiss slotted button', async () => {
        const element = await buttonFixture()
        const button = element.querySelector('ui-button[value="dismiss"]') as UiButton
        button.click()
        assert.equal(element.open, false)
      })

      it('closes the dialog via the confirm slotted button', async () => {
        const element = await buttonFixture()
        const button = element.querySelector('ui-button[value="confirm"]') as UiButton
        button.click()
        assert.equal(element.open, false)
      })

      it('closes the dialog via the dismissLabel button', async () => {
        const element = await basicFixture()
        element.dismissLabel = 'Close'
        await element.updateComplete
        const button = element.shadowRoot!.querySelector('.internal-button[value="dismiss"]') as UiButton
        button.click()
        assert.equal(element.open, false)
      })

      it('closes the dialog via the confirmLabel button', async () => {
        const element = await basicFixture()
        element.confirmLabel = 'Close'
        await element.updateComplete
        const button = element.shadowRoot!.querySelector('.internal-button[value="confirm"]') as UiButton
        button.click()
        assert.equal(element.open, false)
      })

      it('dispatches the close event with cancelled = true', async () => {
        const element = await buttonFixture()
        const spy = sinon.spy()
        element.addEventListener('close', spy)
        const button = element.querySelector('ui-button[value="dismiss"]') as UiButton
        button.click()
        assert.isTrue(spy.calledOnce, 'the event was dispatched')
        const event = spy.args[0][0] as CustomEvent<UiDialogClosingReason>
        assert.isTrue(event.detail.cancelled, 'the cancelled flag is set')
      })

      it('dispatches the close event with cancelled = true', async () => {
        const element = await buttonFixture()
        const spy = sinon.spy()
        element.addEventListener('close', spy)
        const button = element.querySelector('ui-button[value="confirm"]') as UiButton
        button.click()
        assert.isTrue(spy.calledOnce, 'the event was dispatched')
        const event = spy.args[0][0] as CustomEvent<UiDialogClosingReason>
        assert.isFalse(event.detail.cancelled, 'the cancelled flag is not set')
      })

      it('dispatches the dialogValue', async () => {
        const element = await buttonFixture()
        element.dialogValue = 'test'
        const spy = sinon.spy()
        element.addEventListener('close', spy)
        const button = element.querySelector('ui-button[value="confirm"]') as UiButton
        button.click()
        assert.isTrue(spy.calledOnce, 'the event was dispatched')
        const event = spy.args[0][0] as CustomEvent<UiDialogClosingReason>
        assert.equal(event.detail.value, 'test', 'has the value')
      })
    })

    describe('modal dialog', () => {
      it('opens the dialog as modal', async () => {
        const element = await modalFixture()
        const spy = sinon.spy(element.dialog, 'showModal')
        element.open = true
        await element.updateComplete
        assert.isTrue(spy.calledOnce)
      })
    })

    describe('icon', () => {
      it('renders the icon slot', async () => {
        const element = await basicFixture()
        const slot = element.shadowRoot!.querySelector('slot[name="icon"]') as HTMLSlotElement
        assert.ok(slot, 'has the slot')
      })

      it('does not render padding around the icon slot', async () => {
        const element = await basicFixture()
        const container = element.shadowRoot!.querySelector('.icon') as HTMLElement
        assert.isFalse(container.classList.contains('with-icon'), 'has no with-icon class')
      })

      it('renders padding around the icon slot with an icon', async () => {
        const element = await iconFixture()
        const container = element.shadowRoot!.querySelector('.icon') as HTMLElement
        assert.isTrue(container.classList.contains('with-icon'), 'has the with-icon class')
      })
    })

    describe('title', () => {
      it('renders the title slot', async () => {
        const element = await basicFixture()
        const slot = element.shadowRoot!.querySelector('slot[name="title"]') as HTMLSlotElement
        assert.ok(slot, 'has the slot')
      })

      it('does not render padding around the title slot', async () => {
        const element = await basicFixture()
        const container = element.shadowRoot!.querySelector('.title') as HTMLElement
        assert.isFalse(container.classList.contains('with-title'), 'has no with-title class')
      })

      it('renders padding around the title slot with a title', async () => {
        const element = await titleFixture()
        const container = element.shadowRoot!.querySelector('.title') as HTMLElement
        assert.isTrue(container.classList.contains('with-title'), 'has the with-title class')
      })
    })

    describe('buttons', () => {
      it('renders the button slot', async () => {
        const element = await basicFixture()
        const slot = element.shadowRoot!.querySelector('slot[name="button"]') as HTMLSlotElement
        assert.ok(slot, 'has the slot')
      })

      it('does not render padding around the buttons slot', async () => {
        const element = await basicFixture()
        const container = element.shadowRoot!.querySelector('.buttons') as HTMLElement
        assert.isFalse(container.classList.contains('with-buttons'), 'has no with-buttons class')
      })

      it('renders the confirm button', async () => {
        const element = await basicFixture()
        element.confirmLabel = 'OK label'
        await element.updateComplete
        const container = element.shadowRoot!.querySelector('.buttons') as HTMLElement
        assert.isTrue(container.classList.contains('with-buttons'), 'has the with-buttons class')
        const button = element.shadowRoot!.querySelector('.internal-button') as UiButton
        assert.ok(button, 'has the button')
        assert.equal(button.value, 'confirm')
        assert.equal(button.textContent!.trim(), 'OK label')
      })

      it('renders the dismiss button', async () => {
        const element = await basicFixture()
        element.dismissLabel = 'Cancel label'
        await element.updateComplete
        const container = element.shadowRoot!.querySelector('.buttons') as HTMLElement
        assert.isTrue(container.classList.contains('with-buttons'), 'has the with-buttons class')
        const button = element.shadowRoot!.querySelector('.internal-button') as UiButton
        assert.ok(button, 'has the button')
        assert.equal(button.value, 'dismiss')
        assert.equal(button.textContent!.trim(), 'Cancel label')
      })

      it('renders the slotted buttons', async () => {
        const element = await buttonFixture()
        const container = element.shadowRoot!.querySelector('.buttons') as HTMLElement
        assert.isTrue(container.classList.contains('with-buttons'), 'has the with-buttons class')
      })
    })
  })
})
