const $ = (selector) => document.querySelector(selector);

function bufferToBase64url(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = "";
  for (const byte of bytes) {
    binary += String.fromCharCode(byte);
  }
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
}

function base64urlToBuffer(value) {
  const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
  const binary = atob(padded);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i += 1) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}

function normalizeCreationOptions(options) {
  return {
    ...options,
    challenge: base64urlToBuffer(options.challenge),
    user: {
      ...options.user,
      id: base64urlToBuffer(options.user.id),
    },
    excludeCredentials: (options.excludeCredentials || []).map((credential) => ({
      ...credential,
      id: base64urlToBuffer(credential.id),
    })),
  };
}

function normalizeRequestOptions(options) {
  return {
    ...options,
    challenge: base64urlToBuffer(options.challenge),
    allowCredentials: (options.allowCredentials || []).map((credential) => ({
      ...credential,
      id: base64urlToBuffer(credential.id),
    })),
  };
}

function credentialToJSON(credential) {
  const response = credential.response;
  const payload = {
    id: credential.id,
    rawId: bufferToBase64url(credential.rawId),
    type: credential.type,
    response: {},
  };

  if (response.clientDataJSON) {
    payload.response.clientDataJSON = bufferToBase64url(response.clientDataJSON);
  }
  if (response.attestationObject) {
    payload.response.attestationObject = bufferToBase64url(response.attestationObject);
  }
  if (response.authenticatorData) {
    payload.response.authenticatorData = bufferToBase64url(response.authenticatorData);
  }
  if (response.signature) {
    payload.response.signature = bufferToBase64url(response.signature);
  }
  if (response.userHandle) {
    payload.response.userHandle = bufferToBase64url(response.userHandle);
  }
  if (typeof response.getTransports === "function") {
    payload.response.transports = response.getTransports();
  }

  return payload;
}

async function postJson(url, body) {
  const response = await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });

  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new Error(payload.detail || payload.error || "Request failed");
  }

  return payload;
}

function setStatus(message, tone = "") {
  const banner = $("#status-banner");
  banner.textContent = message;
  banner.className = `status-banner ${tone}`.trim();
}

async function loadState() {
  const [config, session] = await Promise.all([
    fetch("/manager/api/config").then((response) => response.json()),
    fetch("/manager/api/session").then((response) => response.json()),
  ]);

  if (session.authenticated) {
    window.location.assign("/manager");
    return;
  }

  $("#manager-rp").textContent = `${config.rpName} on ${config.rpId}`;
  $("#bootstrap-card").hidden = !config.bootstrapEnabled;
  $("#bootstrap-note").textContent = config.hasAdmins
    ? "Bootstrap is disabled after the first manager account is enrolled. Use passkey sign-in or recovery instead."
    : "No manager users exist yet. Register the first developer passkey with the bootstrap secret.";

  if (!config.passkeysAvailable) {
    setStatus("Passkey support is not available on the server yet. Install the WebAuthn dependency first.", "error");
  } else if (!(window.PublicKeyCredential && navigator.credentials)) {
    setStatus("This browser does not support WebAuthn/passkeys.", "error");
  } else if (window.location.protocol !== "https:" && window.location.hostname !== "localhost") {
    setStatus("Passkeys require HTTPS on non-localhost origins.", "error");
  } else if (config.hasAdmins && !config.bootstrapEnabled) {
    setStatus("Sign in with an existing passkey or use a recovery code. Bootstrap is available only for first-time setup.");
  } else {
    setStatus("Passkeys are ready. Sign in to the hosted manager or bootstrap the first developer account.");
  }
}

async function handleBootstrap(event) {
  event.preventDefault();

  const username = $("#bootstrap-username").value.trim();
  const displayName = $("#bootstrap-display-name").value.trim();
  const bootstrapSecret = $("#bootstrap-secret").value;

  if (!username || !bootstrapSecret) {
    setStatus("Bootstrap username and secret are required.", "error");
    return;
  }

  setStatus("Generating passkey registration challenge...");
  const start = await postJson("/manager/api/bootstrap/start", {
    username,
    display_name: displayName || null,
    bootstrap_secret: bootstrapSecret,
  });

  const credential = await navigator.credentials.create({
    publicKey: normalizeCreationOptions(start.options),
  });

  await postJson("/manager/api/bootstrap/finish", {
    challenge_id: start.challengeId,
    credential: credentialToJSON(credential),
  });

  setStatus("Passkey registered. Redirecting to the manager...", "success");
  window.location.assign("/manager");
}

async function handleLogin(event) {
  event.preventDefault();

  const username = $("#login-username").value.trim();
  if (!username) {
    setStatus("Enter your manager username before using a passkey.", "error");
    return;
  }

  setStatus("Generating sign-in challenge...");
  const start = await postJson("/manager/api/login/start", { username });
  const credential = await navigator.credentials.get({
    publicKey: normalizeRequestOptions(start.options),
  });

  await postJson("/manager/api/login/finish", {
    challenge_id: start.challengeId,
    credential: credentialToJSON(credential),
  });

  setStatus("Signed in. Redirecting to the manager...", "success");
  window.location.assign("/manager");
}

async function handleRecovery(event) {
  event.preventDefault();

  const username = $("#recovery-username").value.trim();
  const recoveryCode = $("#recovery-code").value.trim();
  if (!username || !recoveryCode) {
    setStatus("Enter both your username and a recovery code.", "error");
    return;
  }

  setStatus("Validating recovery code and generating passkey challenge...");
  const start = await postJson("/manager/api/recovery/start", {
    username,
    recovery_code: recoveryCode,
  });

  const credential = await navigator.credentials.create({
    publicKey: normalizeCreationOptions(start.options),
  });

  await postJson("/manager/api/recovery/finish", {
    challenge_id: start.challengeId,
    credential: credentialToJSON(credential),
  });

  setStatus("Recovery succeeded. Redirecting to the manager...", "success");
  window.location.assign("/manager");
}

window.addEventListener("DOMContentLoaded", async () => {
  try {
    await loadState();
    $("#bootstrap-form").addEventListener("submit", (event) => {
      handleBootstrap(event).catch((error) => setStatus(error.message, "error"));
    });
    $("#login-form").addEventListener("submit", (event) => {
      handleLogin(event).catch((error) => setStatus(error.message, "error"));
    });
    $("#recovery-form").addEventListener("submit", (event) => {
      handleRecovery(event).catch((error) => setStatus(error.message, "error"));
    });
  } catch (error) {
    setStatus(error.message || "Unable to load manager configuration.", "error");
  }
});
