import { assert, fixture, html, nextFrame } from '@open-wc/testing';
import sinon from 'sinon';
import UiChip from '../../../src/ui/chip/UiChip.js';
import { UiMock } from '../../helpers/UiMock.js';
import { normalizeColor } from '../../../src/lib/color/ColorConverter.js';
import '../../../src/define/ui/ui-chip.js';
import '../../../src/define/ui/ui-icon.js';

describe('UI', () => {
  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 disableIconFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="assist" disabled>
          <ui-icon slot="icon" icon="add"></ui-icon>
            Disabled 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 container = element.shadowRoot?.querySelector('.container') as HTMLDivElement;
        const styles = getComputedStyle(container);
        const m3Color = styles.getPropertyValue('--md-sys-color-outline');
        const borderColor = normalizeColor(styles.borderColor);
        const compareColor = 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('.surface') as HTMLDivElement;
        assert.isTrue(surface.classList.contains('withIcon'), 'the surface has the withIcon class');
        const content = element.shadowRoot!.querySelector('.content') as HTMLDivElement;
        const contentStyles = getComputedStyle(content);
        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 disabled styles when disabled', async () => {
        const element = await disableIconFixture();
        const container = element.shadowRoot!.querySelector('.container') as HTMLDivElement;
        const containerStyles = getComputedStyle(container);
        assert.equal(containerStyles.opacity, '0.12', 'container has the opacity');
        const content = element.shadowRoot!.querySelector('.content') as HTMLDivElement;
        const contentStyles = getComputedStyle(content);
        assert.equal(contentStyles.opacity, '0.38', 'content has the opacity');
      });

      it('has elevated styles', async () => {
        const element = await elevatedFixture();
        const container = element.shadowRoot!.querySelector('.container') as HTMLDivElement;
        const containerStyles = getComputedStyle(container);
        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 container = element.shadowRoot!.querySelector('.container') as HTMLDivElement;
        const containerStyles = getComputedStyle(container);
        assert.equal(containerStyles.boxShadow, 'none', 'has no box shadow');
        assert.equal(containerStyles.opacity, '0.12', 'container has the opacity');
        const content = element.shadowRoot!.querySelector('.content') as HTMLDivElement;
        const contentStyles = getComputedStyle(content);
        assert.equal(contentStyles.opacity, '0.38', 'content has the opacity');
      });
    });

    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 closableFixture(): Promise<UiChip> {
        return fixture(html`<ui-chip type="filter" closable>Closable</ui-chip>`);
      }

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

      it('renders a border around the chip', async () => {
        const element = await basicFixture();
        const container = element.shadowRoot?.querySelector('.container') as HTMLDivElement;
        const styles = getComputedStyle(container);
        const m3Color = styles.getPropertyValue('--md-sys-color-outline');
        const borderColor = normalizeColor(styles.borderColor);
        const compareColor = 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 closableFixture();
        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('dispatches the list event', async () => {
        const element = await listFixture();
        const spy = sinon.spy();
        element.addEventListener('list', spy);
        const node = element.shadowRoot!.querySelector('.trailing-icon') as HTMLElement;
        node.click();
        assert.isTrue(spy.calledOnce);
      });

      it('prevents the "click" event when dispatching the "list" event', async () => {
        const element = await listFixture();
        const spy = sinon.spy();
        element.addEventListener('click', spy);
        const node = element.shadowRoot!.querySelector('.trailing-icon') as HTMLElement;
        node.click();
        assert.isFalse(spy.called);
      });

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

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

      it('is accessible when closable', async () => {
        const element = await closableFixture();
        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);
      });
    });
  });
});
