import type { MaybeComputedElementRef } from '@robonen/vue';
import { defaultWindow, tryOnScopeDispose, unrefElement } from '@robonen/vue';
import { hideOthers } from '@robonen/platform/browsers';
import { watch } from 'vue';

type Undo = () => void;

const CLOSED_POPOVER_SELECTOR = '[popover]:not(:popover-open)';

/**
 * Isolated so the try/catch doesn't inhibit Turbofan optimization of the
 * watcher callback. `:popover-open` throws in browsers that don't support it
 * (e.g. Safari 18).
 */
function isInClosedPopover(el: Element): boolean {
  try {
    return el.closest(CLOSED_POPOVER_SELECTOR) !== null;
  }
  catch {
    return false;
  }
}

/**
 * @name useHideOthers
 * @category Browser
 * @description Hides every sibling element of `target` from assistive technologies
 * by setting `aria-hidden="true"`. Automatically restores the original state when
 * the target is removed or the component scope is disposed. Skips elements living
 * inside a closed native `[popover]` to avoid double-hiding.
 *
 * @param {MaybeComputedElementRef} target Element whose siblings should be aria-hidden
 *
 * @since 0.0.1
 */
export function useHideOthers(target: MaybeComputedElementRef): void {
  if (!defaultWindow) return;

  let undo: Undo | undefined;

  watch(() => unrefElement(target), (raw) => {
    if (undo) {
      undo();
      undo = undefined;
    }

    const el = raw as HTMLElement | undefined;
    if (!el || isInClosedPopover(el)) return;

    undo = hideOthers(el);
  }, { flush: 'post' });

  tryOnScopeDispose(() => {
    if (undo) {
      undo();
      undo = undefined;
    }
  });
}
