// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="../../../../test/src/custom_typings/chai.d.ts" />
/* eslint-disable no-undef */
import { ZuiTooltipElement } from '@zywave/zui-tooltip';
import { isPopoverSupported } from '@zywave/zui-base/dist/utils/support-popover.js';
import { assert } from '@esm-bundle/chai';
import { randSentence, randString, sleep } from '../../../../test/src/util/helpers';
import { conditionalTest } from '../../../../test/src/util/mocha-helpers';

const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') !== -1;

function simulatePointerEvent(el: HTMLElement, type: string) {
  return el?.dispatchEvent(new PointerEvent(type));
}

suite('zui-tooltip', () => {
  let element: ZuiTooltipElement;

  setup(() => {
    element = document.createElement('zui-tooltip') as ZuiTooltipElement;

    document.body.appendChild(element);
  });

  teardown(() => {
    document.body.removeChild(element);
  });

  test('initializes as a ZuiTooltipElement', () => {
    assert.instanceOf(element, ZuiTooltipElement);
  });

  test('Default tooltip position is set to "right" on initialization', () => {
    assert.equal(element.position, 'right');
  });

  test('Set a tooltip position', () => {
    element.position = 'bottom';
    assert.equal(element.position, 'bottom');
  });

  test('Set tooltip text', async () => {
    const text: string = randSentence();
    element.innerText = text;

    await element.updateComplete;

    assert.strictEqual(element.textContent, text);
  });

  test('Customize tooltip trigger', async () => {
    const trigger: HTMLButtonElement = document.createElement('button');
    trigger.innerText = randString();
    trigger.slot = 'trigger';
    element.appendChild(trigger);

    await element.updateComplete;

    const triggerSlot: HTMLSlotElement | null | undefined = element.shadowRoot?.querySelector('slot[name=trigger]');
    const triggerSlotAssignedNodes: Node[] | undefined = triggerSlot?.assignedNodes();
    const triggerSlotAssignedElement = triggerSlotAssignedNodes?.[0] as HTMLButtonElement;

    assert.strictEqual(triggerSlotAssignedElement, trigger);
  });

  conditionalTest(!IS_FIREFOX, 'Reset tooltip position to "right" when position attribute is removed', async () => {
    element.innerText = randSentence();

    await element.updateComplete;

    const trigger: HTMLElement | null | undefined = element.shadowRoot?.querySelector('.trigger');

    // trigger positioning logic via pointerenter event
    simulatePointerEvent(trigger!, 'pointerenter');

    await sleep(1);

    // record old (default/right) tooltip position values
    const tooltip: HTMLElement | null | undefined = element.shadowRoot?.querySelector('.tooltip');
    const tooltipComputedStyle = window.getComputedStyle(tooltip!);
    const oldPositionTopValue = tooltipComputedStyle?.getPropertyValue('top');
    const oldPositionLeftValue = tooltipComputedStyle?.getPropertyValue('left');

    // change tooltip position and trigger positioning logic via pointerenter event
    element.setAttribute('position', 'bottom');
    simulatePointerEvent(trigger!, 'pointerenter');

    await sleep(1);

    // record new (bottom) tooltip position values and compare with old values
    const newPositionTopValue = tooltipComputedStyle?.getPropertyValue('top');
    const newPositionLeftValue = tooltipComputedStyle?.getPropertyValue('left');
    assert.notEqual(oldPositionTopValue, newPositionTopValue);
    assert.notEqual(oldPositionLeftValue, newPositionLeftValue);

    // remove position attribute to reset tooltip position to default/right
    element.removeAttribute('position');
    simulatePointerEvent(trigger!, 'pointerenter');

    await sleep(1);

    // capture reset tooltip position values and compare with old values
    const resetPositionTopValue = tooltipComputedStyle?.getPropertyValue('top');
    const resetPositionLeftValue = tooltipComputedStyle?.getPropertyValue('left');
    assert.equal(resetPositionTopValue, oldPositionTopValue);
    assert.equal(resetPositionLeftValue, oldPositionLeftValue);

    // check if position attribute is removed
    assert.notExists(element.position);
  });

  conditionalTest(
    !IS_FIREFOX,
    'Automatically flip tooltip position to opposite direction when it is too close to the edge of the viewport',
    async () => {
      element.innerText = randSentence();

      await element.updateComplete;

      const trigger: HTMLElement | null | undefined = element.shadowRoot?.querySelector('.trigger');
      const oldPosition: string = element.position;

      assert.exists(element.position);
      assert.equal(element.position, 'right');
      simulatePointerEvent(trigger!, 'pointerenter');
      simulatePointerEvent(trigger!, 'pointerover');

      await sleep(1);

      // move tooltip to right edge of viewport
      element.style.position = 'absolute';
      element.style.top = '100px';
      element.style.right = '0';

      await sleep(1);

      // simulate interaction with tooltip
      simulatePointerEvent(trigger!, 'pointerenter');
      simulatePointerEvent(trigger!, 'pointerover');
      simulatePointerEvent(trigger!, 'pointerleave');

      await sleep(1);

      assert.notEqual(oldPosition, element.position);
    }
  );

  test('Hide tooltip component when popover is not supported in the browser', async () => {
    // start with a blank page
    document.body.removeChild(element);

    // backup original popover methods
    const originalPopover = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'popover');
    const originalShowPopover = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'showPopover');
    const originalHidePopover = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'hidePopover');
    const originalTogglePopover = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'togglePopover');

    // remove popover methods to simulate no popover support
    delete window.HTMLElement.prototype.popover;
    delete window.HTMLElement.prototype.showPopover;
    delete window.HTMLElement.prototype.hidePopover;
    delete window.HTMLElement.prototype.togglePopover;

    assert.isTrue(!isPopoverSupported());

    // add tooltip to the page
    document.body.appendChild(element);

    await element.updateComplete;

    const trigger: HTMLElement | null | undefined = element.shadowRoot?.querySelector('.trigger');
    const tooltip: HTMLElement | null | undefined = element.shadowRoot?.querySelector('.tooltip');
    const tooltipComputedStyle = window.getComputedStyle(tooltip!);
    const tooltipDisplay = tooltipComputedStyle?.getPropertyValue('display');

    // don't show tooltip when popover is not supported
    simulatePointerEvent(trigger!, 'pointerenter');
    assert.isTrue(tooltipDisplay === 'none', `pointerenter: ${tooltipDisplay}`);

    simulatePointerEvent(trigger!, 'pointerover');
    assert.isTrue(tooltipDisplay === 'none', `pointerover: ${tooltipDisplay}`);

    // restore popover methods
    Object.defineProperty(window.HTMLElement.prototype, 'popover', originalPopover!);
    Object.defineProperty(window.HTMLElement.prototype, 'showPopover', originalShowPopover!);
    Object.defineProperty(window.HTMLElement.prototype, 'hidePopover', originalHidePopover!);
    Object.defineProperty(window.HTMLElement.prototype, 'togglePopover', originalTogglePopover!);
  });
});
