import { confirmDialog, requestSubmitWithFallback } from './app-confirm-dialog.js';

const escapeAttr = (value) => {
  const raw = String(value || '');
  if (typeof CSS !== 'undefined' && CSS && typeof CSS.escape === 'function') {
    return CSS.escape(raw);
  }
  return raw.replace(/["\\]/g, '\\$&');
};

const toContainerElement = (value) => {
  if (!value) {
    return document.querySelector('.app-details-container') || document.body || document.documentElement;
  }
  if (typeof value === 'string') {
    return document.querySelector(value);
  }
  if (value instanceof HTMLElement) {
    return value;
  }
  if (value === document) {
    return document.documentElement;
  }
  return null;
};

const toFormElement = (value) => {
  if (!value) {
    return null;
  }
  if (value instanceof HTMLFormElement) {
    return value;
  }
  if (typeof value === 'string') {
    const element = document.querySelector(value);
    return element instanceof HTMLFormElement ? element : null;
  }
  return null;
};

const normalizeSubmitLock = (value) => {
  const normalized = String(value || '').trim().toLowerCase();
  return normalized === 'none' ? 'none' : 'form';
};

const belongsToForm = (control, form) => {
  if (!(control instanceof HTMLElement) || !(form instanceof HTMLFormElement)) {
    return false;
  }
  const owner = control.form;
  if (owner instanceof HTMLFormElement) {
    return owner === form;
  }
  const controlFormAttr = String(control.getAttribute('form') || '').trim();
  const formId = String(form.id || '').trim();
  if (controlFormAttr !== '' && formId !== '') {
    return controlFormAttr === formId;
  }
  return form.contains(control);
};

const isSubmitControl = (control) => {
  if (!(control instanceof HTMLElement)) {
    return false;
  }
  if (control instanceof HTMLButtonElement) {
    const type = String(control.getAttribute('type') || 'submit').trim().toLowerCase();
    return type === '' || type === 'submit';
  }
  if (control instanceof HTMLInputElement) {
    const type = String(control.type || '').trim().toLowerCase();
    return type === 'submit' || type === 'image';
  }
  return false;
};

const findSubmitControlsForForm = (form) => {
  if (!(form instanceof HTMLFormElement)) {
    return [];
  }
  const controls = [];
  controls.push(
    ...Array.from(form.querySelectorAll('button, input')).filter(isSubmitControl)
  );
  const formId = String(form.id || '').trim();
  if (formId !== '') {
    const escapedId = escapeAttr(formId);
    controls.push(
      ...Array.from(document.querySelectorAll(`button[form="${escapedId}"], input[form="${escapedId}"]`))
        .filter(isSubmitControl)
    );
  }
  return Array.from(new Set(controls)).filter((control) => belongsToForm(control, form));
};

const pickSubmitter = (event, form, submitterByForm) => {
  const eventSubmitter = event.submitter instanceof HTMLElement ? event.submitter : null;
  if (eventSubmitter && belongsToForm(eventSubmitter, form)) {
    return eventSubmitter;
  }
  const fallback = submitterByForm.get(form);
  if (fallback && belongsToForm(fallback, form)) {
    return fallback;
  }
  return null;
};

const readConfirmMessage = (submitter, form) => {
  const fromSubmitter = String(submitter?.getAttribute('data-detail-confirm-message') || '').trim();
  if (fromSubmitter !== '') {
    return fromSubmitter;
  }
  return String(form.getAttribute('data-detail-confirm-message') || '').trim();
};

const readConfirmOption = (submitter, form, detailSuffix, genericSuffix = detailSuffix) => {
  const detailAttr = `data-detail-confirm-${detailSuffix}`;
  const genericAttr = `data-confirm-${genericSuffix}`;
  return String(
    submitter?.getAttribute(detailAttr)
    || submitter?.getAttribute(genericAttr)
    || form.getAttribute(detailAttr)
    || form.getAttribute(genericAttr)
    || ''
  ).trim();
};

const readActionKind = (submitter, form) => String(
  submitter?.getAttribute('data-detail-action-kind')
  || form.getAttribute('data-detail-action-kind')
  || ''
).trim();

const buildConfirmOptions = (message, submitter, form) => ({
  message,
  title: readConfirmOption(submitter, form, 'title'),
  confirmLabel: readConfirmOption(submitter, form, 'confirm-label'),
  cancelLabel: readConfirmOption(submitter, form, 'cancel-label'),
  variant: readConfirmOption(submitter, form, 'variant'),
  focus: readConfirmOption(submitter, form, 'focus'),
  describedBy: readConfirmOption(submitter, form, 'describedby')
    || readConfirmOption(submitter, form, 'described-by'),
  actionKind: readActionKind(submitter, form)
});

const readSubmitLockMode = (submitter, form) => normalizeSubmitLock(
  submitter?.getAttribute('data-detail-submit-lock')
  || form.getAttribute('data-detail-submit-lock')
  || 'form'
);

const disableSubmitControlsForForm = (form, submitter, options = {}) => {
  const controls = findSubmitControlsForForm(form);
  controls.forEach((control) => {
    if (submitter instanceof HTMLElement && control === submitter) {
      // Keep the clicked submitter enabled so its name/value stays part of form submission.
      return;
    }
    control.disabled = true;
  });
  if (submitter instanceof HTMLElement) {
    submitter.setAttribute('aria-busy', 'true');
  }
  if (options.markFormBusy === true) {
    form.setAttribute('aria-busy', 'true');
  }
};

export function initDetailActionPolicy(options = {}) {
  const enabled = options.enabled !== false;
  if (!enabled) {
    return {
      isSubmitting: () => false,
      destroy: () => {}
    };
  }

  const container = toContainerElement(options.container);
  if (!(container instanceof HTMLElement)) {
    return {
      isSubmitting: () => false,
      destroy: () => {}
    };
  }

  if (container._detailActionPolicyApi) {
    return container._detailActionPolicyApi;
  }

  const submittingForms = new Set();
  const submitterByForm = new Map();
  const confirmedSubmitterByForm = new WeakMap();

  const onClickCapture = (event) => {
    const target = event.target instanceof HTMLElement
      ? event.target.closest('button, input')
      : null;
    if (!(target instanceof HTMLElement) || !isSubmitControl(target) || !container.contains(target)) {
      return;
    }
    const form = target.form instanceof HTMLFormElement ? target.form : null;
    if (!(form instanceof HTMLFormElement)) {
      return;
    }
    submitterByForm.set(form, target);
  };

  const onSubmitCapture = (event) => {
    const form = event.target instanceof HTMLFormElement ? event.target : null;
    if (!(form instanceof HTMLFormElement) || !container.contains(form)) {
      return;
    }

    const submitter = pickSubmitter(event, form, submitterByForm);
    const lockMode = readSubmitLockMode(submitter, form);
    if (lockMode !== 'none' && submittingForms.has(form)) {
      event.preventDefault();
      return;
    }

    const bypassSubmitter = confirmedSubmitterByForm.get(form);
    if (bypassSubmitter && (bypassSubmitter === true || bypassSubmitter === submitter || bypassSubmitter === null)) {
      confirmedSubmitterByForm.delete(form);
    } else {
      const confirmMessage = readConfirmMessage(submitter, form);
      if (confirmMessage !== '') {
        event.preventDefault();
        event.stopPropagation();

        void confirmDialog.confirm(buildConfirmOptions(confirmMessage, submitter, form))
          .then((approved) => {
            if (!approved || !document.contains(form)) {
              return;
            }
            const submitterForSubmit = submitter && belongsToForm(submitter, form) ? submitter : null;
            confirmedSubmitterByForm.set(form, submitterForSubmit || true);
            requestSubmitWithFallback(form, submitterForSubmit);
          });
        return;
      }
    }

    if (lockMode === 'none') {
      return;
    }

    submittingForms.add(form);
    disableSubmitControlsForForm(form, submitter, { markFormBusy: options.markFormBusy === true });
  };

  container.addEventListener('click', onClickCapture, true);
  container.addEventListener('submit', onSubmitCapture, true);

  const api = {
    isSubmitting: (formRef = null) => {
      const form = toFormElement(formRef);
      if (form) {
        return submittingForms.has(form);
      }
      return submittingForms.size > 0;
    },
    destroy: () => {
      container.removeEventListener('click', onClickCapture, true);
      container.removeEventListener('submit', onSubmitCapture, true);
      submittingForms.clear();
      submitterByForm.clear();
      delete container._detailActionPolicyApi;
    }
  };

  container._detailActionPolicyApi = api;
  return api;
}
