/* eslint-disable lit-a11y/tabindex-no-positive */
import { assert, fixture, html, nextFrame } from '@open-wc/testing';
import '../../../src/define/ui/ui-button.js';
import '../../../src/define/ui/ui-icon.js';
import UiButton from '../../../src/ui/button/UiButton.js';
import { UiMock } from '../../helpers/UiMock.js';

describe('UI', () => {
  describe('UiButton', () => {
    async function basicFixture(): Promise<UiButton> {
      return fixture(html`<ui-button>Label</ui-button>`);
    }

    async function formFixture(): Promise<HTMLFormElement> {
      return fixture(html`<form>
      <input name="text" value="abc" />
      <ui-button>Label</ui-button>
      </form>`);
    }

    async function submitFormFixture(): Promise<HTMLFormElement> {
      return fixture(html`<form>
      <input name="text" value="abc" />
      <ui-button name="button" value="ok" submit>Label</ui-button>
      </form>`);
    }

    async function disabledFormFixture(): Promise<HTMLFormElement> {
      return fixture(html`<form>
      <input name="text" value="abc" />
      <ui-button name="button" value="ok" submit disabled>Label</ui-button>
      </form>`);
    }

    async function disabledFixture(): Promise<UiButton> {
      return fixture(html`<ui-button disabled>Label</ui-button>`);
    }

    async function tabindexFixture(): Promise<UiButton> {
      return fixture(html`<ui-button tabindex="1">Label</ui-button>`);
    }

    async function iconFixture(): Promise<UiButton> {
      return fixture(html`<ui-button>
        <ui-icon slot="icon" icon="add"></ui-icon>
        Label
      </ui-button>`);
    }

    async function trailingIconFixture(): Promise<UiButton> {
      return fixture(html`<ui-button trailingIcon>
        <ui-icon slot="icon" icon="add"></ui-icon>
        Label
      </ui-button>`);
    }

    describe('Form button', () => {
      it('has formAssociated set', async () => {
        assert.isTrue(UiButton.formAssociated);
      });

      it('has no form when not in form', async () => {
        const button = await basicFixture();
        assert.isNull(button.form);
      });

      it('has form when in a form', async () => {
        const form = await formFixture();
        const button = form.querySelector('ui-button')!;
        assert.ok(button.form, 'has a form');
        assert.isTrue(button.form === form, 'has the parent form');
      });

      it('does not trigger submit when not "submit"', async () => {
        const form = await formFixture();
        const button = form.querySelector('ui-button')!;
        let event: Event | undefined;
        form.addEventListener('submit', (e: SubmitEvent) => {
          e.preventDefault();
          event = e;
        });
        button.click();
        assert.notOk(event);
      });

      it('removes the native "button" after submitting', async () => {
        const form = await submitFormFixture();
        const button = form.querySelector('ui-button')!;
        form.addEventListener('submit', (e: SubmitEvent) => {
          e.preventDefault();
        });
        button.click();
        const nativeButton = form.querySelector('button');
        assert.notOk(nativeButton);
      });

      it('registers the form values', async () => {
        const form = await submitFormFixture();
        const button = form.querySelector('ui-button')!;
        assert.isTrue(form.elements[1] === button, 'the form has the button');
      });

      it('submits the form when clicking on the button', async () => {
        const form = await submitFormFixture();
        const button = form.querySelector('ui-button')!;
        let event: SubmitEvent | undefined;
        form.addEventListener('submit', (e: SubmitEvent) => {
          e.preventDefault();
          event = e;
        });
        button.click();
        assert.ok(event, 'has the event');
        const submitter = event!.submitter as UiButton;
        assert.equal(submitter.value, 'ok', 'submitter has the value');
        assert.equal(submitter.name, 'button', 'submitter has the name');
      });

      it('submits the form with Space bar', async () => {
        const form = await submitFormFixture();
        const button = form.querySelector('ui-button')!;
        let event: SubmitEvent | undefined;
        form.addEventListener('submit', (e: SubmitEvent) => {
          e.preventDefault();
          event = e;
        });
        await UiMock.keyPress(button, 'Space');
        assert.ok(event);
      });

      it('submits the form with Enter', async () => {
        const form = await submitFormFixture();
        const button = form.querySelector('ui-button')!;
        let event: SubmitEvent | undefined;
        form.addEventListener('submit', (e: SubmitEvent) => {
          e.preventDefault();
          event = e;
        });
        await UiMock.keyPress(button, 'Enter');
        assert.ok(event);
      });
    });

    describe('Disabled button', () => {
      it('is not disabled by default', async () => {
        const button = await basicFixture();
        assert.isFalse(button.disabled);
      });

      it('has no aria-disabled by default', async () => {
        const button = await basicFixture();
        assert.isFalse(button.hasAttribute('aria-disabled'));
      });

      it('is is disabled via the markup', async () => {
        const button = await disabledFixture();
        assert.isTrue(button.disabled);
      });

      it('removes the tabindex', async () => {
        const button = await disabledFixture();
        assert.isFalse(button.hasAttribute('tabindex'));
      });

      it('sets the aria-disabled attribute', async () => {
        const button = await disabledFixture();
        assert.equal(button.getAttribute('aria-disabled'), 'true');
      });

      it('disables the button via property setter', async () => {
        const button = await basicFixture();
        button.disabled = true;
        assert.equal(button.getAttribute('aria-disabled'), 'true');
        assert.isFalse(button.hasAttribute('tabindex'));
      });

      it('restores the button from the disabled state', async () => {
        const button = await tabindexFixture();
        button.disabled = true;
        await nextFrame();
        button.disabled = false;
        await nextFrame();
        assert.equal(button.getAttribute('tabindex'), '1');
        assert.isFalse(button.hasAttribute('aria-disabled'));
      });

      it('does not trigger submit when disabled', async () => {
        const form = await disabledFormFixture();
        const button = form.querySelector('ui-button')!;
        let event: Event | undefined;
        form.addEventListener('submit', (e: SubmitEvent) => {
          e.preventDefault();
          event = e;
        });
        button.click();
        assert.notOk(event);
      });
    });

    describe('Icon placement', () => {
      it('places the icon as a prefix by default', async () => {
        const button = await iconFixture();
        const content = button.shadowRoot!.querySelector('.content');
        assert.equal(content?.children[0].localName, 'slot');
      });

      it('places the icon as a suffix', async () => {
        const button = await trailingIconFixture();
        const content = button.shadowRoot!.querySelector('.content');
        assert.equal(content?.children[1].localName, 'slot');
      });

      it('recognizes it has an icon', async () => {
        const button = await iconFixture();
        const surface = button.shadowRoot!.querySelector('.surface') as HTMLElement;
        assert.isTrue(surface.classList.contains('withIcon'));
      });
    });

    describe('Ripple effect', () => {
      it('does not render the ripple without interaction', async () => {
        const button = await basicFixture();
        const ripple = button.shadowRoot!.querySelector('ui-ripple');
        assert.notOk(ripple);
      });

      it('renders the ripple after click', async () => {
        const button = await basicFixture();
        const surface = button.shadowRoot!.querySelector('.surface') as HTMLElement; 
        surface.click();
        await nextFrame();
        const ripple = button.shadowRoot!.querySelector('ui-ripple');
        assert.ok(ripple);
      });
    });

    describe('a11y', () => {
      it('is accessible when in a default state', async () => {
        const button = await basicFixture();
        await assert.isAccessible(button);
      });

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