<script lang="ts">
  import IconButton from '../iconbutton/IconButton.svelte';
  import { Cross24 } from '@mozaic-ds/icons-svelte';
  import { customElementForwardEvents } from '../../utils';
  import { onMount } from 'svelte';
  import type { ActionListboxItem } from '../actionlistboxitem/ActionListboxItem.svelte';
  import type { Button } from '../button/Button.svelte';

  /**
   * An action list is a contextual menu that presents a list of available actions related to a specific element or interface area. It allows users to quickly access functions such as editing, sharing, deleting, or navigating to sub-actions. Action Lists are commonly triggered by buttons, icons (e.g., three-dot menus), or right-click interactions, ensuring a clean and efficient UI.
   *
   * @slot item - The content displayed in the listbox item.
   * @slot activator - The trigger used to toggle the listbox. It should contain one Button or IconButton.
   * @event close {CustomEvent<void>} - Emits when the close button is clicked.
   */
  interface Props {
    [key: string]: any;
    /**
     * If `true`, displays the listbox when using the activator slot.
     */
    open?: boolean;
    /**
     * title displayed in mobile version.
     */
    title?: string;
    /**
     * Defines the position of the listbox relative to its trigger or container.
     */
    position?: 'top' | 'bottom' | 'left' | 'right';
    /**
     * Callback to trigger on close button click.
     */
    onclose?: () => void;
  }

  let {
    open = $bindable(),
    title,
    position = 'bottom',
    onclose,
    class: className,
    ...attrs
  }: Props = $props();

  // TODO: Remove when typescript is up to date
  type PopoverOptions = {
    source: HTMLElement;
  };

  type Popover = {
    showPopover: (options?: PopoverOptions) => void;
    hidePopover: (options?: PopoverOptions) => void;
  };

  const id = crypto.randomUUID();

  let element: HTMLElement;
  let listbox: HTMLElement;
  let activator: HTMLElement | null = $state(null);

  function handleClose() {
    onclose?.();

    const event = new CustomEvent('close', {
      bubbles: true,
      composed: true,
    });
    element.dispatchEvent(event);

    open = false;
  }

  function getFocusableElements(): HTMLElement[] {
    const slot = listbox?.querySelector('slot[name="item"]') as HTMLSlotElement;
    if (!slot) return [];

    const assignedElements = slot.assignedElements() as HTMLElement[];
    return assignedElements.filter((el) => {
      return !el.hasAttribute('disabled') && el.getAttribute('disabled') !== 'true';
    });
  }

  function focusListItem(first?: boolean) {
    const items = getFocusableElements();
    if (!items.length) return;

    let focusedIndex = 0;

    if (!first) {
      const currentIndex = items.findIndex((item) => item === document.activeElement);

      focusedIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;
    }

    (items[focusedIndex] as ActionListboxItem)?.focus();
  }

  function toggleListbox() {
    if (!listbox || !activator) return;

    if (open) {
      (listbox as Popover).showPopover({ source: activator });
      focusListItem(true);
    } else {
      (listbox as Popover).hidePopover({ source: activator });
    }
  }

  function handleClickOutside(event: MouseEvent) {
    if (!open) return;

    const path = event.composedPath ? event.composedPath() : [];
    if (!path.includes(element) && !path.includes(activator!)) {
      open = false;
    }
  }

  function onKeydown(event: KeyboardEvent) {
    event.preventDefault();
    if (event.key === 'Tab') {
      focusListItem();
    }

    if (event.key === 'Escape') {
      open = false;
      const slot = activator?.querySelector('slot[name="activator"]') as HTMLSlotElement;
      const activatorBtn = slot.assignedElements()[0];
      (activatorBtn as Element & Button).focus();
    }
  }

  $effect(toggleListbox);

  onMount(() => {
    document.addEventListener('mousedown', handleClickOutside);

    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  });
</script>

<div bind:this={element} class={['mc-listbox', `mc-listbox--${position}`, className]} {...attrs}>
  <div
    bind:this={listbox}
    class="mc-listbox__content"
    {...$$slots.activator
      ? { id, popover: 'manual', role: 'dialog', tabindex: 0, onkeydown: onKeydown }
      : {}}
  >
    <div class="mc-listbox__header">
      {#if title}
        <h3 class="mc-listbox__title">{title}</h3>
      {/if}

      <span class="mc-listbox__close">
        <IconButton ghost aria-label="Close" onclick={handleClose}>
          <Cross24 slot="icon" />
        </IconButton>
      </span>
    </div>
    <div class="mc-listbox__body">
      <ul class="mc-action-list" role="menu">
        <slot name="item" />
      </ul>
    </div>
  </div>
</div>

{#if $$slots.activator}
  <div bind:this={activator}>
    <slot name="activator" />
  </div>
{/if}

<style>/**
 * Do not edit directly, this file was auto-generated.
 */
.mc-listbox:has(.mc-listbox__content:not([popover])), .mc-listbox:has(.mc-listbox__content:popover-open) {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 1;
  padding: 0.5rem;
  background-color: var(--overlay-color-background, rgba(0, 0, 0, 0.5));
  inset: 0;
  opacity: 0;
  position: fixed;
  pointer-events: none;
  transition: opacity 0.4s ease, visibility 0ms 0.4s;
  z-index: var(--overlay-z-index, 4);
}
.mc-listbox:has(.mc-listbox__content:not([popover])).is-visible, .mc-listbox:has(.mc-listbox__content:popover-open).is-visible {
  opacity: 1;
  pointer-events: all;
  transition: opacity 0.4s ease, visibility 0ms;
  visibility: visible;
}
.mc-listbox:has(.mc-listbox__content:not([popover])), .mc-listbox:has(.mc-listbox__content:popover-open) {
  opacity: 1;
}
@media (width >= 680px) {
  .mc-listbox:has(.mc-listbox__content:not([popover])), .mc-listbox:has(.mc-listbox__content:popover-open) {
    position: relative;
    display: block;
    padding: 0;
    background-color: transparent;
  }
}
.mc-listbox__content {
  border: var(--border-width-s, 0.0625rem) solid var(--color-border-primary, #cccccc);
  border-radius: var(--border-radius-m, 0.5rem);
  width: 100%;
  z-index: 10;
  background-color: var(--color-background-primary, #ffffff);
  pointer-events: all;
}
@media (width >= 680px) {
  .mc-listbox__content {
    min-width: 18.75rem;
  }
}
.mc-listbox__header {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  height: 4rem;
}
@media (width >= 680px) {
  .mc-listbox__header {
    display: none;
  }
}
.mc-listbox__title {
  line-height: var(--line-height-s, 1.3);
  font-weight: var(--font-weight-semi-bold, 600);
  font-size: var(--font-size-100, 0.875rem);
  color: var(--color-text-tertiary, #666666);
  text-align: center;
  margin: 0;
}
.mc-listbox__close {
  position: absolute;
  right: 1.25rem;
  width: 1.5rem;
  height: 1.5rem;
}
.mc-listbox__body {
  overflow: hidden;
}
@media (width >= 680px) {
  .mc-listbox--top .mc-listbox__content {
    margin: 0 0 8px;
    inset: auto;
    position-area: span-right top;
  }
  .mc-listbox--bottom .mc-listbox__content {
    margin: 8px 0 0;
    inset: auto;
    position-area: span-right bottom;
  }
  .mc-listbox--left .mc-listbox__content {
    margin: 0 8px 0 0;
    inset: auto;
    position-area: span-bottom left;
  }
  .mc-listbox--right .mc-listbox__content {
    margin: 0 0 0 8px;
    inset: auto;
    position-area: span-bottom right;
  }
}

.mc-action-list {
  padding: 0.5rem 0.5rem;
  margin: 0;
}
.mc-action-list__element {
  height: 2.5rem;
  cursor: pointer;
  border-radius: var(--border-radius-s, 0.25rem);
  flex-grow: 1;
}
.mc-action-list__element:hover {
  background-color: var(--action-listbox-color-background-hover, rgba(0, 0, 0, 0.05));
}
.mc-action-list__element--danger .mc-action-list__text {
  color: var(--action-listbox-color-text-danger, #c61112);
}
.mc-action-list__element--danger .mc-action-list__icon {
  fill: var(--action-listbox-color-text-danger, #c61112);
}
.mc-action-list__element--danger:hover {
  background-color: var(--action-listbox-color-background-danger-hover, rgba(198, 17, 18, 0.1));
}
.mc-action-list__element--disabled .mc-action-list__text {
  color: var(--action-listbox-color-text-disabled, #b3b3b3);
}
.mc-action-list__element--disabled .mc-action-list__icon {
  fill: var(--action-listbox-color-text-disabled, #b3b3b3);
}
.mc-action-list__element--disabled:hover {
  background-color: transparent;
}
.mc-action-list__element--disabled button {
  cursor: not-allowed;
}
.mc-action-list__button {
  background-color: transparent;
  color: var(--action-listbox-color-text-default, #000000);
  border: none;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0;
  padding: 0 0.25rem;
  cursor: pointer;
  width: 100%;
  height: 100%;
}
.mc-action-list__button:focus-visible {
  box-shadow: 0 0 0 0.125rem var(--focus-color-mid, var(--focus-color-outline-mid, #ffffff)), 0 0 0 0.25rem var(--focus-color-outer, var(--focus-color-outline-outer, #000000));
  outline: 0.125rem solid transparent;
  outline-offset: 0.125rem;
}
.mc-action-list__text {
  font-size: var(--font-size-150, 1rem);
  font-weight: var(--font-weight-regular, 400);
  line-height: var(--line-height-s, 1.3);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin: 0;
}
.mc-action-list__icon {
  width: 1.25rem;
  height: 1.25rem;
}
.mc-action-list__divider {
  margin: 0.5rem 0;
}

.mc-listbox__close {
  display: flex;
  align-items: center;
  justify-content: center;
}</style>
