/* global window, document, navigator, CustomEvent, HTMLElement, HTMLFormElement, MutationObserver, fetch, atob, btoa, Blob, URL, sessionStorage, localStorage, location, crypto */
(() => {
  if (window.WRNexusAuth) return;

  const mounted = new WeakSet();
  const requestControllers = new WeakMap();
  const cleanups = new WeakMap();
  const recoveryMounted = new WeakSet();
  const recoveryCleanups = new WeakMap();
  const flowMounted = new WeakSet();
  const flowCleanups = new WeakMap();
  const MFA_STORAGE_KEY = "wrnexus.auth.mfa";
  const DEVICE_STORAGE_KEY = "wrnexus.auth.device";

  function fromBase64Url(value) {
    const input = String(value);
    if (!input || !/^[A-Za-z0-9_-]+$/.test(input) || input.length % 4 === 1) {
      throw new TypeError("Passkey data must be canonical Base64URL without padding");
    }
    const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
    const binary = atob(normalized + "=".repeat((4 - (normalized.length % 4)) % 4));
    const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
    if (toBase64Url(bytes) !== input) {
      throw new TypeError("Passkey data must be canonical Base64URL without padding");
    }
    return bytes;
  }

  function toBase64Url(value) {
    const bytes =
      value instanceof ArrayBuffer
        ? new Uint8Array(value)
        : new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
    let binary = "";
    for (const byte of bytes) binary += String.fromCharCode(byte);
    return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
  }

  function publicKeyCreationOptions(options) {
    return {
      ...options,
      challenge: fromBase64Url(options.challenge),
      user: { ...options.user, id: fromBase64Url(options.user.id) },
      excludeCredentials: (options.excludeCredentials || []).map((item) => ({
        ...item,
        id: fromBase64Url(item.id),
      })),
    };
  }

  function publicKeyRequestOptions(options) {
    return {
      ...options,
      challenge: fromBase64Url(options.challenge),
      allowCredentials: (options.allowCredentials || []).map((item) => ({
        ...item,
        id: fromBase64Url(item.id),
      })),
    };
  }

  function credentialToJson(credential) {
    if (!credential) return null;
    const response = credential.response;
    const base = {
      id: credential.id,
      type: credential.type,
      rawId: toBase64Url(credential.rawId),
      authenticatorAttachment: credential.authenticatorAttachment,
      clientExtensionResults: credential.getClientExtensionResults
        ? credential.getClientExtensionResults()
        : {},
    };
    if (response && "attestationObject" in response) {
      return {
        ...base,
        response: {
          clientDataJSON: toBase64Url(response.clientDataJSON),
          attestationObject: toBase64Url(response.attestationObject),
          transports: response.getTransports ? response.getTransports() : [],
        },
      };
    }
    return {
      ...base,
      response: {
        clientDataJSON: toBase64Url(response.clientDataJSON),
        authenticatorData: toBase64Url(response.authenticatorData),
        signature: toBase64Url(response.signature),
        userHandle: response.userHandle ? toBase64Url(response.userHandle) : null,
      },
    };
  }

  function csrfHeaders() {
    const match = document.cookie.match(/(?:^|;\s*)wire-csrf=([^;]+)/);
    return match ? { "x-csrf-token": decodeURIComponent(match[1]) } : {};
  }

  async function json(url, body, signal) {
    const response = await fetch(url, {
      method: "POST",
      credentials: "same-origin",
      headers: {
        "content-type": "application/json",
        accept: "application/json",
        ...csrfHeaders(),
      },
      body: JSON.stringify(body),
      signal,
    });
    const payload = await response.json().catch(() => ({}));
    if (!response.ok || payload.ok === false) {
      const error = new Error(
        payload.message || payload.error || `Request failed (${response.status})`,
      );
      error.status = response.status;
      error.payload = payload;
      throw error;
    }
    return payload;
  }

  function setState(element, state, message) {
    element.dataset.authState = state;
    element.setAttribute("aria-busy", state === "loading" ? "true" : "false");
    const status = element.querySelector("[data-auth-status]");
    if (status) {
      status.textContent = message || "";
      status.hidden = !message;
    }
    const button = element.matches("button") ? element : element.querySelector("button");
    if (button) button.disabled = state === "loading";
  }

  function dispatch(element, name, detail) {
    element.dispatchEvent(new CustomEvent(name, { bubbles: true, detail }));
  }

  function navigate(target) {
    if (!target) return false;
    const url = new URL(target, location.href);
    if (url.origin !== location.origin) return false;
    if (window.__wrnexusNavigate) {
      window.__wrnexusNavigate(url.pathname + url.search + url.hash);
    } else {
      location.assign(url.href);
    }
    return true;
  }

  function randomDeviceId() {
    if (crypto.randomUUID) return crypto.randomUUID();
    const bytes = crypto.getRandomValues(new Uint8Array(24));
    return toBase64Url(bytes);
  }

  function deviceFingerprint() {
    try {
      let value = localStorage.getItem(DEVICE_STORAGE_KEY);
      if (!value) {
        value = randomDeviceId();
        localStorage.setItem(DEVICE_STORAGE_KEY, value);
      }
      return value;
    } catch {
      return randomDeviceId();
    }
  }

  function toggle(element, visible) {
    if (!element) return;
    element.hidden = !visible;
    element.classList.toggle("hidden", !visible);
  }

  function setMessage(element, message, isError = false) {
    if (!element) return;
    element.textContent = message || "";
    element.hidden = !message;
    element.classList.toggle("hidden", !message);
    element.dataset.status = isError ? "error" : "success";
  }

  function setFormBusy(form, busy) {
    form?.querySelectorAll("[type=submit]").forEach((button) => {
      button.disabled = busy;
    });
  }

  async function registerPasskey(element) {
    setState(element, "loading", element.dataset.loadingMessage || "Preparing passkey…");
    try {
      if (!window.PublicKeyCredential || !navigator.credentials || !window.AbortController) {
        throw new Error("Passkeys are not supported in this browser");
      }
      const controller = new window.AbortController();
      requestControllers.get(element)?.abort();
      requestControllers.set(element, controller);
      const optionsPayload = await json(
        element.dataset.optionsEndpoint || "/api/auth/passkeys/register/options",
        {},
        controller.signal,
      );
      const credential = await navigator.credentials.create({
        publicKey: publicKeyCreationOptions(optionsPayload.options),
        signal: controller.signal,
      });
      const result = await json(
        element.dataset.verifyEndpoint || "/api/auth/passkeys/register/verify",
        {
          key: optionsPayload.key,
          response: credentialToJson(credential),
          name: element.dataset.passkeyName || "Passkey",
        },
        controller.signal,
      );
      setState(element, "success", element.dataset.successMessage || "Passkey added.");
      dispatch(element, "auth:passkey-registered", result);
    } catch (error) {
      if (error && error.name === "AbortError") return;
      setState(
        element,
        "error",
        error instanceof Error ? error.message : "Passkey registration failed",
      );
      dispatch(element, "auth:error", { error });
    }
  }

  async function authenticatePasskey(element) {
    setState(element, "loading", element.dataset.loadingMessage || "Waiting for your passkey…");
    try {
      if (!window.PublicKeyCredential || !navigator.credentials || !window.AbortController) {
        throw new Error("Passkeys are not supported in this browser");
      }
      const controller = new window.AbortController();
      requestControllers.get(element)?.abort();
      requestControllers.set(element, controller);
      const identifier =
        element.dataset.identifier ||
        element.closest("form")?.querySelector("[name=identifier]")?.value ||
        element.closest("[data-auth-sign-in]")?.querySelector("form [name=identifier]")?.value ||
        undefined;
      const optionsPayload = await json(
        element.dataset.optionsEndpoint || "/api/auth/passkeys/login/options",
        { identifier },
        controller.signal,
      );
      const credential = await navigator.credentials.get({
        publicKey: publicKeyRequestOptions(optionsPayload.options),
        signal: controller.signal,
        mediation: element.dataset.conditional === "true" ? "conditional" : "optional",
      });
      const result = await json(
        element.dataset.verifyEndpoint || "/api/auth/passkeys/login/verify",
        {
          key: optionsPayload.key,
          response: credentialToJson(credential),
        },
        controller.signal,
      );
      setState(element, "success", element.dataset.successMessage || "Signed in.");
      dispatch(element, "auth:passkey-authenticated", result);
      if (element.dataset.redirect) navigate(element.dataset.redirect);
    } catch (error) {
      if (error && error.name === "AbortError") return;
      const detail = error?.payload || {};
      if (detail.code === "mfa-required" && detail.mfaToken) {
        sessionStorage.setItem(MFA_STORAGE_KEY, JSON.stringify(detail));
        dispatch(element, "auth:mfa-required", detail);
        navigate(element.dataset.mfaHref || "/two-factor");
        return;
      }
      setState(element, "error", error instanceof Error ? error.message : "Passkey sign-in failed");
      dispatch(element, "auth:error", { error, ...detail });
    }
  }

  function recoveryCodes(element) {
    return Array.from(element.querySelectorAll("[data-recovery-code]"))
      .map((item) => String(item.textContent || "").trim())
      .filter(Boolean);
  }

  function renderRecoveryCodes(element, codes) {
    const list = element.querySelector("[data-recovery-code-list]");
    if (!list || !Array.isArray(codes)) return;
    list.textContent = "";
    for (const code of codes) {
      const item = document.createElement("code");
      item.dataset.recoveryCode = "";
      item.className = "rounded bg-[var(--wire-color-surface)] px-2 py-1.5 text-center";
      item.textContent = String(code);
      list.appendChild(item);
    }
  }

  function downloadRecoveryCodes(element) {
    const codes = recoveryCodes(element);
    if (!codes.length) return;
    const blob = new Blob([codes.join("\n") + "\n"], { type: "text/plain;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = element.dataset.recoveryFilename || "wrnexus-recovery-codes.txt";
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
  }

  function mountRecoveryCodes(element) {
    if (!(element instanceof HTMLElement) || recoveryMounted.has(element)) return;
    recoveryMounted.add(element);
    const download = element.querySelector("[data-recovery-download]");
    const onDownload = () => downloadRecoveryCodes(element);
    const onSuccess = (event) => {
      if (event.target instanceof HTMLFormElement && event.detail?.codes) {
        renderRecoveryCodes(element, event.detail.codes);
      }
    };
    download?.addEventListener("click", onDownload);
    element.addEventListener("wire:success", onSuccess);
    recoveryCleanups.set(element, () => {
      download?.removeEventListener("click", onDownload);
      element.removeEventListener("wire:success", onSuccess);
    });
  }

  function mountOtpSignIn(element) {
    if (!(element instanceof HTMLElement) || flowMounted.has(element)) return;
    flowMounted.add(element);
    const requestForm = element.querySelector("[data-auth-otp-request]");
    const completeForm = element.querySelector("[data-auth-otp-complete]");
    const back = element.querySelector("[data-auth-otp-back]");
    const onSuccess = (event) => {
      if (event.target !== requestForm || !event.detail?.id) return;
      const challenge = completeForm?.querySelector("[name=challengeId]");
      if (challenge) challenge.value = String(event.detail.id);
      toggle(requestForm, false);
      toggle(completeForm, true);
      completeForm?.querySelector("[name=code]")?.focus();
      dispatch(element, "auth:otp-requested", event.detail);
    };
    const onError = (event) => {
      if (event.target !== completeForm) return;
      const detail = event.detail || {};
      if (detail.code !== "mfa-required" || !detail.mfaToken) return;
      sessionStorage.setItem(MFA_STORAGE_KEY, JSON.stringify(detail));
      dispatch(element, "auth:mfa-required", detail);
      navigate(element.dataset.mfaHref || "/two-factor");
    };
    const onBack = () => {
      toggle(completeForm, false);
      toggle(requestForm, true);
      requestForm?.querySelector("[name=identifier]")?.focus();
    };
    element.addEventListener("wire:success", onSuccess);
    element.addEventListener("wire:error", onError);
    back?.addEventListener("click", onBack);
    flowCleanups.set(element, () => {
      element.removeEventListener("wire:success", onSuccess);
      element.removeEventListener("wire:error", onError);
      back?.removeEventListener("click", onBack);
    });
  }

  function mountSignIn(element) {
    if (!(element instanceof HTMLElement) || flowMounted.has(element)) return;
    flowMounted.add(element);
    const form = element.querySelector("form[data-schema]");
    const fingerprintInput = form?.querySelector("[name=deviceFingerprint]");
    const deviceNameInput = form?.querySelector("[name=deviceName]");
    if (fingerprintInput && !fingerprintInput.value) fingerprintInput.value = deviceFingerprint();
    if (deviceNameInput && !deviceNameInput.value) {
      deviceNameInput.value = String(navigator.userAgent || navigator.platform || "Browser").slice(
        0,
        120,
      );
    }
    const onError = (event) => {
      if (!(event.target instanceof HTMLFormElement)) return;
      const detail = event.detail || {};
      if (detail.code !== "mfa-required" || !detail.mfaToken) return;
      sessionStorage.setItem(MFA_STORAGE_KEY, JSON.stringify(detail));
      dispatch(element, "auth:mfa-required", detail);
      navigate(element.dataset.mfaHref || "/two-factor");
    };
    element.addEventListener("wire:error", onError);
    flowCleanups.set(element, () => element.removeEventListener("wire:error", onError));
  }

  function readMfaState() {
    try {
      return JSON.parse(sessionStorage.getItem(MFA_STORAGE_KEY) || "null");
    } catch {
      return null;
    }
  }

  function mountMfaChallenge(element) {
    if (!(element instanceof HTMLElement) || flowMounted.has(element)) return;
    flowMounted.add(element);
    const form = element.querySelector("[data-auth-mfa-form]");
    const tokenInput = form?.querySelector("[name=mfaToken]");
    const challengeInput = form?.querySelector("[name=challengeId]");
    const methodInput = form?.querySelector("[data-auth-mfa-method]");
    const codeInput = form?.querySelector("[name=code]");
    const status = form?.querySelector("[data-auth-mfa-status]");
    const saved = readMfaState();
    if (tokenInput && !tokenInput.value && saved?.mfaToken) {
      tokenInput.value = String(saved.mfaToken);
    }
    const allowed = Array.isArray(saved?.requires?.mfa) ? saved.requires.mfa : [];
    const initialMethod = element.dataset.mfaInitialMethod;
    if (methodInput && allowed.length) {
      Array.from(methodInput.options).forEach((option) => {
        option.disabled = !allowed.includes(option.value);
        option.hidden = option.disabled;
      });
      const selected = allowed.includes(initialMethod)
        ? initialMethod
        : allowed.includes(methodInput.value)
          ? methodInput.value
          : allowed[0];
      if (selected) methodInput.value = selected;
    } else if (methodInput && initialMethod) {
      methodInput.value = initialMethod;
    }

    const onMethodChange = () => {
      if (challengeInput) challengeInput.value = "";
      setMessage(status, "");
      if (codeInput) {
        codeInput.value = "";
        codeInput.setAttribute(
          "inputmode",
          methodInput?.value === "recovery-code" ? "text" : "numeric",
        );
      }
    };

    const onSubmit = async (event) => {
      const method = methodInput?.value;
      if (!form || !["email-otp", "sms-otp"].includes(method) || challengeInput?.value) return;
      event.preventDefault();
      event.stopImmediatePropagation();
      if (!tokenInput?.value) {
        setMessage(status, "Sign-in verification has expired. Start again.", true);
        return;
      }
      setFormBusy(form, true);
      setMessage(status, "Sending a one-time code…");
      try {
        const result = await json(element.dataset.mfaOtpAction || "/api/auth/mfa/otp", {
          mfaToken: tokenInput.value,
          method,
        });
        if (challengeInput) challengeInput.value = String(result.id || "");
        setMessage(status, "A one-time code has been sent.");
        codeInput?.focus();
      } catch (error) {
        setMessage(
          status,
          error instanceof Error ? error.message : "Unable to send a one-time code",
          true,
        );
      } finally {
        setFormBusy(form, false);
      }
    };
    const onSuccess = (event) => {
      if (event.target !== form) return;
      sessionStorage.removeItem(MFA_STORAGE_KEY);
    };
    onMethodChange();
    methodInput?.addEventListener("change", onMethodChange);
    form?.addEventListener("submit", onSubmit, true);
    element.addEventListener("wire:success", onSuccess);
    flowCleanups.set(element, () => {
      methodInput?.removeEventListener("change", onMethodChange);
      form?.removeEventListener("submit", onSubmit, true);
      element.removeEventListener("wire:success", onSuccess);
    });
  }

  function mountAuthenticatorSetup(element) {
    if (!(element instanceof HTMLElement) || flowMounted.has(element)) return;
    flowMounted.add(element);
    const startForm = element.querySelector("[data-auth-authenticator-start]");
    const details = element.querySelector("[data-auth-authenticator-details]");
    const confirmForm = element.querySelector("[data-auth-authenticator-confirm]");
    const secret = element.querySelector("[data-auth-authenticator-secret]");
    const uri = element.querySelector("[data-auth-authenticator-uri]");
    const onSuccess = (event) => {
      if (event.target === startForm && event.detail?.credentialId) {
        const credential = confirmForm?.querySelector("[name=credentialId]");
        if (credential) credential.value = String(event.detail.credentialId);
        if (secret) secret.textContent = String(event.detail.secret || "");
        if (uri) uri.textContent = String(event.detail.uri || "");
        toggle(startForm, false);
        toggle(details, true);
        confirmForm?.querySelector("[name=code]")?.focus();
        dispatch(element, "auth:authenticator-created", event.detail);
      }
      if (event.target === confirmForm) {
        dispatch(element, "auth:authenticator-enabled", event.detail);
      }
    };
    element.addEventListener("wire:success", onSuccess);
    flowCleanups.set(element, () => element.removeEventListener("wire:success", onSuccess));
  }

  function mountElement(element) {
    if (!(element instanceof HTMLElement) || mounted.has(element)) return;
    mounted.add(element);
    const action = element.dataset.authPasskey;
    const button = element.matches("button") ? element : element.querySelector("button");
    if (!button || !action) return;
    const handler = () =>
      action === "register" ? registerPasskey(element) : authenticatePasskey(element);
    button.addEventListener("click", handler);
    cleanups.set(element, () => button.removeEventListener("click", handler));
  }

  function mount(root = document) {
    if (root instanceof HTMLElement && root.matches("[data-auth-passkey]")) mountElement(root);
    root.querySelectorAll?.("[data-auth-passkey]").forEach(mountElement);
    if (root instanceof HTMLElement && root.matches("[data-auth-recovery-codes]"))
      mountRecoveryCodes(root);
    root.querySelectorAll?.("[data-auth-recovery-codes]").forEach(mountRecoveryCodes);
    if (root instanceof HTMLElement && root.matches("[data-auth-otp-sign-in]"))
      mountOtpSignIn(root);
    root.querySelectorAll?.("[data-auth-otp-sign-in]").forEach(mountOtpSignIn);
    if (root instanceof HTMLElement && root.matches("[data-auth-sign-in]")) mountSignIn(root);
    root.querySelectorAll?.("[data-auth-sign-in]").forEach(mountSignIn);
    if (root instanceof HTMLElement && root.matches("[data-auth-mfa-challenge]"))
      mountMfaChallenge(root);
    root.querySelectorAll?.("[data-auth-mfa-challenge]").forEach(mountMfaChallenge);
    if (root instanceof HTMLElement && root.matches("[data-auth-authenticator-setup]"))
      mountAuthenticatorSetup(root);
    root.querySelectorAll?.("[data-auth-authenticator-setup]").forEach(mountAuthenticatorSetup);
  }

  function unmount(root = document) {
    const elements = [];
    if (root instanceof HTMLElement && root.matches("[data-auth-passkey]")) elements.push(root);
    root.querySelectorAll?.("[data-auth-passkey]").forEach((element) => elements.push(element));
    for (const element of elements) {
      requestControllers.get(element)?.abort();
      requestControllers.delete(element);
      cleanups.get(element)?.();
      cleanups.delete(element);
      mounted.delete(element);
    }

    const recoveryElements = [];
    if (root instanceof HTMLElement && root.matches("[data-auth-recovery-codes]"))
      recoveryElements.push(root);
    root
      .querySelectorAll?.("[data-auth-recovery-codes]")
      .forEach((element) => recoveryElements.push(element));
    for (const element of recoveryElements) {
      recoveryCleanups.get(element)?.();
      recoveryCleanups.delete(element);
      recoveryMounted.delete(element);
    }

    const flowElements = [];
    const selector =
      "[data-auth-otp-sign-in],[data-auth-sign-in],[data-auth-mfa-challenge],[data-auth-authenticator-setup]";
    if (root instanceof HTMLElement && root.matches(selector)) flowElements.push(root);
    root.querySelectorAll?.(selector).forEach((element) => flowElements.push(element));
    for (const element of flowElements) {
      flowCleanups.get(element)?.();
      flowCleanups.delete(element);
      flowMounted.delete(element);
    }
  }

  window.WRNexusAuth = { mount, unmount, registerPasskey, authenticatePasskey };
  window.__wrnexusRuntimes = window.__wrnexusRuntimes || {};
  window.__wrnexusRuntimes.auth = { mount, unmount };

  if (document.readyState === "loading")
    document.addEventListener("DOMContentLoaded", () => mount(document), { once: true });
  else mount(document);

  new MutationObserver((records) => {
    for (const record of records)
      for (const node of record.addedNodes) if (node instanceof HTMLElement) mount(node);
  }).observe(document.documentElement, { childList: true, subtree: true });
})();
