import { warnOnce } from './app-dom.js';

const VARIANTS = new Set(['default', 'warning', 'danger']);
const FOCUS_TARGETS = new Set(['confirm', 'cancel']);
const DANGER_KEYWORDS = /(delete|deleted|purge|revoke|remove|destroy|danger|erase|drop|wipe|lösch|loesch|entfern|widerruf)/i;
const FOCUSABLE_SELECTOR = [
  'button:not([disabled])',
  'a[href]',
  'input:not([type="hidden"]):not([disabled])',
  'select:not([disabled])',
  'textarea:not([disabled])',
  '[tabindex]:not([tabindex="-1"])'
].join(',');

const trimValue = (value) => String(value ?? '').trim();

const getNativeConfirm = () => {
  const fallback = window?.['confirm'];
  return typeof fallback === 'function' ? fallback.bind(window) : null;
};

const normalizeVariant = (value) => {
  const variant = trimValue(value).toLowerCase();
  return VARIANTS.has(variant) ? variant : '';
};

const normalizeFocus = (value) => {
  const focus = trimValue(value).toLowerCase();
  return FOCUS_TARGETS.has(focus) ? focus : '';
};

const inferVariant = ({ variant = '', actionKind = '', message = '' } = {}) => {
  const explicit = normalizeVariant(variant);
  if (explicit !== '') {
    return explicit;
  }
  const action = trimValue(actionKind).toLowerCase();
  if (action.includes('danger') || action.includes('delete') || action.includes('purge') || action.includes('revoke')) {
    return 'danger';
  }
  return DANGER_KEYWORDS.test(trimValue(message)) ? 'danger' : 'default';
};

const getFocusableElements = (dialog) => Array.from(dialog.querySelectorAll(FOCUSABLE_SELECTOR))
  .filter((element) => !element.hasAttribute('hidden'))
  .filter((element) => element.getAttribute('aria-hidden') !== 'true');

const requestSubmitWithFallback = (form, submitter) => {
  if (!(form instanceof HTMLFormElement)) {
    return;
  }
  if (typeof form.requestSubmit === 'function') {
    if (submitter instanceof HTMLElement) {
      try {
        form.requestSubmit(submitter);
        return;
      } catch {
        // Fallback below.
      }
    }
    form.requestSubmit();
    return;
  }
  if (submitter instanceof HTMLElement && typeof submitter.click === 'function') {
    submitter.click();
    return;
  }
  form.submit();
};

class ConfirmDialogController {
  constructor() {
    this.queue = Promise.resolve();
  }

  enqueue(task) {
    const next = this.queue.then(task, task);
    this.queue = next.then(() => undefined, () => undefined);
    return next;
  }

  resolveElements() {
    const dialog = document.querySelector('[data-app-confirm-dialog]');
    if (!(dialog instanceof HTMLDialogElement)) {
      return null;
    }
    const titleEl = dialog.querySelector('[data-app-confirm-title]');
    const messageEl = dialog.querySelector('[data-app-confirm-message]');
    const noteEl = dialog.querySelector('[data-app-confirm-note]');
    const cancelButton = dialog.querySelector('[data-app-confirm-cancel]');
    const confirmButton = dialog.querySelector('[data-app-confirm-confirm]');
    const closeButton = dialog.querySelector('[data-app-confirm-close]');

    if (!(titleEl instanceof HTMLElement) || !(messageEl instanceof HTMLElement) || !(cancelButton instanceof HTMLButtonElement) || !(confirmButton instanceof HTMLButtonElement)) {
      warnOnce('UI_CONFIRM_DIALOG_INVALID', 'Missing confirm dialog elements', { module: 'confirm-dialog' });
      return null;
    }

    return {
      dialog,
      titleEl,
      messageEl,
      noteEl: noteEl instanceof HTMLElement ? noteEl : null,
      cancelButton,
      confirmButton,
      closeButton: closeButton instanceof HTMLButtonElement ? closeButton : null
    };
  }

  fallbackConfirm(options = {}) {
    const message = trimValue(options.message);
    if (message === '') {
      return Promise.resolve(true);
    }
    const fallback = getNativeConfirm();
    if (!fallback) {
      return Promise.resolve(false);
    }
    return Promise.resolve(Boolean(fallback(message)));
  }

  run(options = {}) {
    const message = trimValue(options.message);
    if (message === '') {
      return Promise.resolve(true);
    }

    const elements = this.resolveElements();
    if (!elements || typeof elements.dialog.showModal !== 'function') {
      return this.fallbackConfirm({ message });
    }

    return new Promise((resolve) => {
      const {
        dialog,
        titleEl,
        messageEl,
        noteEl,
        cancelButton,
        confirmButton,
        closeButton
      } = elements;

      const variant = inferVariant(options);
      const focusTarget = normalizeFocus(options.focus) || (variant === 'danger' ? 'cancel' : 'confirm');
      const dataset = dialog.dataset || {};
      const title = trimValue(options.title) || trimValue(dataset.defaultTitle) || 'Please confirm';
      const confirmLabel = trimValue(options.confirmLabel) || trimValue(dataset.defaultConfirmLabel) || 'Confirm';
      const cancelLabel = trimValue(options.cancelLabel) || trimValue(dataset.defaultCancelLabel) || 'Cancel';
      const describedByExternal = trimValue(options.describedBy);
      const note = trimValue(options.note) || (variant === 'danger' ? trimValue(dataset.defaultDangerNote) : '');

      const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : null;
      const cleanupCallbacks = [];
      let resolved = false;

      const addCleanup = (fn) => {
        cleanupCallbacks.push(fn);
      };

      const cleanup = () => {
        while (cleanupCallbacks.length > 0) {
          const fn = cleanupCallbacks.pop();
          try {
            fn();
          } catch {
            // no-op
          }
        }
      };

      const finish = (approved) => {
        if (resolved) {
          return;
        }
        resolved = true;
        cleanup();
        try {
          if (dialog.open) {
            dialog.close(approved ? 'confirm' : 'cancel');
          }
        } catch {
          // no-op
        }
        document.body.classList.remove('modal-is-open');
        if (activeElement && document.contains(activeElement)) {
          window.requestAnimationFrame(() => {
            activeElement.focus({ preventScroll: true });
          });
        }
        resolve(Boolean(approved));
      };

      const onCancelClick = (event) => {
        event.preventDefault();
        finish(false);
      };
      const onCloseClick = (event) => {
        event.preventDefault();
        finish(false);
      };
      const onConfirmClick = (event) => {
        event.preventDefault();
        finish(true);
      };
      const onDialogCancel = (event) => {
        event.preventDefault();
        finish(false);
      };
      const onBackdropClick = (event) => {
        if (event.target === dialog) {
          finish(false);
        }
      };
      const onClose = () => {
        finish(dialog.returnValue === 'confirm');
      };
      const onKeyDown = (event) => {
        if (event.key !== 'Tab') {
          return;
        }
        const focusable = getFocusableElements(dialog);
        if (!focusable.length) {
          event.preventDefault();
          return;
        }
        const first = focusable[0];
        const last = focusable[focusable.length - 1];
        const active = document.activeElement;
        if (event.shiftKey) {
          if (active === first || !dialog.contains(active)) {
            event.preventDefault();
            last.focus();
          }
          return;
        }
        if (active === last) {
          event.preventDefault();
          first.focus();
        }
      };

      titleEl.textContent = title;
      messageEl.textContent = message;
      cancelButton.textContent = cancelLabel;
      confirmButton.textContent = confirmLabel;
      dialog.setAttribute('data-confirm-variant', variant);
      confirmButton.classList.remove('primary', 'warning', 'danger');
      if (variant === 'danger') {
        confirmButton.classList.add('danger');
      } else if (variant === 'warning') {
        confirmButton.classList.add('warning');
      } else {
        confirmButton.classList.add('primary');
      }

      if (noteEl) {
        if (note === '') {
          noteEl.hidden = true;
          noteEl.textContent = '';
        } else {
          noteEl.hidden = false;
          noteEl.textContent = note;
        }
      }

      const describedByIds = [
        trimValue(messageEl.id),
        noteEl && !noteEl.hidden ? trimValue(noteEl.id) : '',
        describedByExternal
      ].filter(Boolean);
      if (describedByIds.length) {
        dialog.setAttribute('aria-describedby', describedByIds.join(' '));
      } else {
        dialog.removeAttribute('aria-describedby');
      }

      cancelButton.addEventListener('click', onCancelClick);
      if (closeButton) {
        closeButton.addEventListener('click', onCloseClick);
      }
      confirmButton.addEventListener('click', onConfirmClick);
      dialog.addEventListener('cancel', onDialogCancel);
      dialog.addEventListener('click', onBackdropClick);
      dialog.addEventListener('close', onClose);
      dialog.addEventListener('keydown', onKeyDown);

      addCleanup(() => cancelButton.removeEventListener('click', onCancelClick));
      if (closeButton) {
        addCleanup(() => closeButton.removeEventListener('click', onCloseClick));
      }
      addCleanup(() => confirmButton.removeEventListener('click', onConfirmClick));
      addCleanup(() => dialog.removeEventListener('cancel', onDialogCancel));
      addCleanup(() => dialog.removeEventListener('click', onBackdropClick));
      addCleanup(() => dialog.removeEventListener('close', onClose));
      addCleanup(() => dialog.removeEventListener('keydown', onKeyDown));

      document.body.classList.add('modal-is-open');

      try {
        dialog.showModal();
      } catch {
        cleanup();
        document.body.classList.remove('modal-is-open');
        resolve(this.fallbackConfirm({ message }));
        return;
      }

      window.requestAnimationFrame(() => {
        if (focusTarget === 'cancel') {
          cancelButton.focus();
          return;
        }
        confirmButton.focus();
      });
    });
  }

  confirm(options = {}) {
    return this.enqueue(() => this.run(options));
  }
}

export const confirmDialog = new ConfirmDialogController();
export { inferVariant as inferConfirmVariant, normalizeVariant as normalizeConfirmVariant, normalizeFocus as normalizeConfirmFocus, requestSubmitWithFallback };
