<script>
  import { post, get, del, put } from "../../lib/api.js";
  import { onMount } from "svelte";
  import { currentUser } from "../../lib/api.js";

  let currentPassword = $state("");
  let newPassword = $state("");
  let confirmPassword = $state("");
  let message = $state("");
  let error = $state("");
  let saving = $state(false);

  let passkeys = $state([]);
  let passkeyMsg = $state("");
  let passkeyErr = $state("");
  let passkeyLoading = $state(false);

  async function loadPasskeys() {
    try {
      const res = await get("/auth/passkey/list");
      passkeys = res.passkeys;
    } catch {}
  }

  function base64urlToBuffer(b64) {
    const s = b64.replace(/-/g, '+').replace(/_/g, '/');
    const raw = atob(s);
    return Uint8Array.from(raw, c => c.charCodeAt(0)).buffer;
  }

  function bufferToBase64url(buf) {
    const bytes = new Uint8Array(buf);
    let s = '';
    bytes.forEach(b => s += String.fromCharCode(b));
    return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
  }

  async function registerPasskey() {
    alert("registerPasskey called!");
    passkeyLoading = true;
    passkeyMsg = ""; passkeyErr = "";
    try {
      alert("Checking PublicKeyCredential support...");
      if (!window.PublicKeyCredential) {
        alert("PublicKeyCredential NOT supported!");
        throw new Error("Passkeys are not supported in this browser or context");
      }
      alert("Fetching registration options...");
      const opts = await post("/auth/passkey/register/options");
      alert("Got options: " + JSON.stringify(opts).substring(0, 100));
      opts.challenge = base64urlToBuffer(opts.challenge);
      opts.user.id = base64urlToBuffer(opts.user.id);
      if (opts.excludeCredentials) {
        opts.excludeCredentials = opts.excludeCredentials.map(c => ({ ...c, id: base64urlToBuffer(c.id) }));
      }
      const cred = await navigator.credentials.create({ publicKey: opts });
      if (!cred) {
        throw new Error("Passkey creation was cancelled");
      }
      const name = prompt("Name this passkey (e.g. MacBook, iPhone):", "Passkey") || "Passkey";
      const body = {
        id: cred.id,
        rawId: bufferToBase64url(cred.rawId),
        type: cred.type,
        name,
        response: {
          attestationObject: bufferToBase64url(cred.response.attestationObject),
          clientDataJSON: bufferToBase64url(cred.response.clientDataJSON),
        },
      };
      await post("/auth/passkey/register/verify", body);
      passkeyMsg = "Passkey registered successfully";
      await loadPasskeys();
    } catch (e) {
      console.error("Passkey registration error:", e);
      const errorMsg = e.body?.detail || e.message || "Failed to register passkey";
      passkeyErr = errorMsg;
      alert("Passkey Error: " + errorMsg + "\n\nFull error: " + JSON.stringify(e, null, 2));
    } finally {
      passkeyLoading = false;
    }
  }

  async function clearPasskeys() {
    if (!confirm("Remove all passkeys?")) return;
    passkeyLoading = true;
    passkeyMsg = ""; passkeyErr = "";
    try {
      await post("/auth/passkey/clear");
      passkeyMsg = "All passkeys removed";
      passkeys = [];
    } catch (e) {
      passkeyErr = e.body?.detail || "Failed to remove passkeys";
    }
    passkeyLoading = false;
  }

  async function deletePasskey(id, name) {
    if (!confirm(`Remove passkey "${name}"?`)) return;
    passkeyLoading = true;
    passkeyMsg = ""; passkeyErr = "";
    try {
      await post("/auth/passkey/delete", { id });
      passkeyMsg = `Passkey "${name}" removed`;
      await loadPasskeys();
    } catch (e) {
      passkeyErr = e.body?.detail || "Failed to remove passkey";
    }
    passkeyLoading = false;
  }

  async function changePassword() {
    error = "";
    message = "";
    if (newPassword !== confirmPassword) { error = "Passwords do not match"; return; }
    if (newPassword.length < 8) { error = "Password must be at least 8 characters"; return; }
    saving = true;
    try {
      await post("/auth/change-password", { current_password: currentPassword, new_password: newPassword });
      message = "Password changed successfully";
      currentPassword = ""; newPassword = ""; confirmPassword = "";
    } catch (e) {
      error = e.body?.detail || "Failed to change password";
    }
    saving = false;
  }

  let auditEntries = $state([]);
  let auditLoading = $state(false);
  let auditFilter = $state("all");

  const levelColors = { high: "text-rose-400", medium: "text-amber-400", low: "text-emerald-400", info: "text-surface-400" };
  const levelLabels = { high: "HIGH", medium: "MED", low: "LOW", info: "INFO" };

  let filteredEntries = $derived(auditFilter === "all" ? auditEntries : auditEntries.filter(e => e.level === auditFilter));

  async function loadAuditLog() {
    auditLoading = true;
    try {
      const res = await get("/system/audit-log?lines=200");
      auditEntries = res.entries.reverse();
    } catch {}
    auditLoading = false;
  }

  // Access Control
  const allPages = ["dashboard", "docker", "files", "terminal", "security", "openclaw", "chat-digest", "gitea", "settings"];
  const allSidebarLinks = ["Overview", "Docker", "Files", "Terminal", "Security", "Navidrome", "Jellyfin", "Audiobookshelf", "Immich", "OpenClaw", "Chat Digest", "Repos", "Gitea Web", "Stirling PDF", "Vaultwarden", "n8n", "Speedtest"];
  let rbacConfig = $state(null);
  let rbacLoading = $state(false);
  let rbacMsg = $state("");
  let rbacErr = $state("");
  let editingUser = $state("");
  let editPages = $state([]);
  let newUsername = $state("");
  let editingRole = $state("");
  let editRolePages = $state([]);
  let editRoleSidebarLinks = $state([]);

  async function loadRbac() {
    rbacLoading = true;
    try {
      rbacConfig = await get("/auth/rbac/config");
    } catch {}
    rbacLoading = false;
  }

  function startEdit(username, pages) {
    editingUser = username;
    editPages = [...pages];
    newUsername = "";
    editingRole = "";
  }

  function startAdd() {
    editingUser = "__new__";
    editPages = ["dashboard"];
    newUsername = "";
    editingRole = "";
  }

  function cancelEdit() {
    editingUser = "";
    editPages = [];
    newUsername = "";
    editingRole = "";
    editRolePages = [];
    editRoleSidebarLinks = [];
  }

  function startEditRole(role, pages, sidebarLinks) {
    editingRole = role;
    editRolePages = pages === "*" ? ["*"] : [...pages];
    editRoleSidebarLinks = sidebarLinks === "*" ? ["*"] : (sidebarLinks ? [...sidebarLinks] : ["*"]);
    editingUser = "";
  }

  function toggleRolePage(p) {
    if (p === "*") {
      editRolePages = ["*"];
    } else {
      if (editRolePages.includes("*")) editRolePages = [];
      if (editRolePages.includes(p)) editRolePages = editRolePages.filter(x => x !== p);
      else editRolePages = [...editRolePages, p];
    }
  }

  function toggleRoleSidebarLink(link) {
    if (link === "*") {
      editRoleSidebarLinks = ["*"];
    } else {
      if (editRoleSidebarLinks.includes("*")) editRoleSidebarLinks = [];
      if (editRoleSidebarLinks.includes(link)) editRoleSidebarLinks = editRoleSidebarLinks.filter(x => x !== link);
      else editRoleSidebarLinks = [...editRoleSidebarLinks, link];
    }
  }

  function togglePage(p) {
    if (editPages.includes(p)) editPages = editPages.filter(x => x !== p);
    else editPages = [...editPages, p];
  }

  async function saveOverride() {
    rbacMsg = ""; rbacErr = "";
    const uname = editingUser === "__new__" ? newUsername.trim() : editingUser;
    if (!uname) { rbacErr = "Username required"; return; }
    if (!editPages.length) { rbacErr = "Select at least one page"; return; }
    try {
      await put(`/auth/rbac/overrides/${encodeURIComponent(uname)}`, { pages: editPages });
      rbacMsg = `Saved override for ${uname}`;
      cancelEdit();
      await loadRbac();
    } catch (e) {
      rbacErr = e.body?.detail || "Failed to save";
    }
  }

  async function deleteOverride(username) {
    if (!confirm(`Remove override for "${username}"?`)) return;
    rbacMsg = ""; rbacErr = "";
    try {
      await del(`/auth/rbac/overrides/${encodeURIComponent(username)}`);
      rbacMsg = `Removed override for ${username}`;
      await loadRbac();
    } catch (e) {
      rbacErr = e.body?.detail || "Failed to delete";
    }
  }

  async function saveRoleDefault() {
    rbacMsg = ""; rbacErr = "";
    if (!editingRole) return;
    if (!editRolePages.length) { rbacErr = "Select at least one page or '*' for all"; return; }
    if (!editRoleSidebarLinks.length) { rbacErr = "Select at least one sidebar link or '*' for all"; return; }
    try {
      const pages = editRolePages.includes("*") ? "*" : editRolePages;
      const sidebar_links = editRoleSidebarLinks.includes("*") ? "*" : editRoleSidebarLinks;
      await put(`/auth/rbac/roles/${encodeURIComponent(editingRole)}`, { pages, sidebar_links });
      rbacMsg = `Updated role default for ${editingRole}`;
      cancelEdit();
      await loadRbac();
    } catch (e) {
      rbacErr = e.body?.detail || "Failed to save role";
    }
  }

  onMount(() => {
    loadPasskeys();
    if (currentUser.role === "admin") loadRbac();
  });
</script>

<div class="space-y-6">
  <div>
    <h1 class="text-2xl font-bold text-surface-900 dark:text-white tracking-tight">Settings</h1>
    <p class="text-sm text-surface-400 mt-1">Account and security settings</p>
  </div>

  <div class="bg-white dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-6 shadow-sm max-w-md">
    <h3 class="text-sm font-semibold text-surface-700 dark:text-surface-200 mb-4">Change Password</h3>

    {#if message}
      <div class="text-sm text-emerald-600 bg-emerald-50 px-3 py-2 rounded-lg mb-4">{message}</div>
    {/if}
    {#if error}
      <div class="text-sm text-rose-600 bg-rose-50 px-3 py-2 rounded-lg mb-4">{error}</div>
    {/if}

    <form onsubmit={(e) => { e.preventDefault(); changePassword(); }} class="space-y-3">
      <div>
        <label class="block text-xs font-medium text-surface-500 mb-1">Current Password</label>
        <input type="password" bind:value={currentPassword} required class="w-full px-3 py-2 text-sm border border-surface-200 dark:border-surface-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-surface-700 dark:text-white" />
      </div>
      <div>
        <label class="block text-xs font-medium text-surface-500 mb-1">New Password</label>
        <input type="password" bind:value={newPassword} required class="w-full px-3 py-2 text-sm border border-surface-200 dark:border-surface-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-surface-700 dark:text-white" />
      </div>
      <div>
        <label class="block text-xs font-medium text-surface-500 mb-1">Confirm New Password</label>
        <input type="password" bind:value={confirmPassword} required class="w-full px-3 py-2 text-sm border border-surface-200 dark:border-surface-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-surface-700 dark:text-white" />
      </div>
      <button type="submit" disabled={saving} class="w-full px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 rounded-lg transition-colors disabled:opacity-50">
        {saving ? "Saving..." : "Change Password"}
      </button>
    </form>
  </div>

  <div class="bg-white dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-6 shadow-sm max-w-md">
    <h3 class="text-sm font-semibold text-surface-700 dark:text-surface-200 mb-4">Passkeys</h3>

    {#if passkeyMsg}
      <div class="text-sm text-emerald-600 bg-emerald-50 px-3 py-2 rounded-lg mb-4">{passkeyMsg}</div>
    {/if}
    {#if passkeyErr}
      <div class="text-sm text-rose-600 bg-rose-50 px-3 py-2 rounded-lg mb-4">{passkeyErr}</div>
    {/if}

    {#if passkeys.length > 0}
      <div class="space-y-2 mb-4">
        {#each passkeys as pk}
          <div class="flex items-center justify-between px-3 py-2 bg-surface-50 dark:bg-surface-700 rounded-lg">
            <div>
              <span class="text-sm font-medium text-surface-700 dark:text-surface-200">{pk.name}</span>
              {#if pk.created_at}
                <span class="text-xs text-surface-400 ml-2">{new Date(pk.created_at).toLocaleDateString()}</span>
              {/if}
            </div>
            <button onclick={() => deletePasskey(pk.id, pk.name)} disabled={passkeyLoading} class="text-xs text-rose-500 hover:text-rose-700 disabled:opacity-50">Remove</button>
          </div>
        {/each}
      </div>
    {:else}
      <p class="text-sm text-surface-400 mb-4">No passkeys registered</p>
    {/if}

    <div class="flex gap-2">
      <button onclick={registerPasskey} disabled={passkeyLoading} class="px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 rounded-lg transition-colors disabled:opacity-50">
        Register new passkey
      </button>
      {#if passkeys.length > 1}
        <button onclick={clearPasskeys} disabled={passkeyLoading} class="px-4 py-2 text-sm font-medium text-rose-600 bg-rose-50 hover:bg-rose-100 rounded-lg transition-colors disabled:opacity-50">
          Remove all
        </button>
      {/if}
    </div>
  </div>

  {#if currentUser.role === "admin" && rbacConfig}
  <div class="bg-white dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-6 shadow-sm">
    <div class="flex items-center justify-between mb-4">
      <h3 class="text-sm font-semibold text-surface-700 dark:text-surface-200">Access Control</h3>
      <button onclick={loadRbac} disabled={rbacLoading} class="px-3 py-1.5 text-xs font-medium text-primary-600 bg-primary-50 hover:bg-primary-100 rounded-lg transition-colors disabled:opacity-50">
        {rbacLoading ? "Loading..." : "Refresh"}
      </button>
    </div>

    {#if rbacMsg}
      <div class="text-sm text-emerald-600 bg-emerald-50 px-3 py-2 rounded-lg mb-4">{rbacMsg}</div>
    {/if}
    {#if rbacErr}
      <div class="text-sm text-rose-600 bg-rose-50 px-3 py-2 rounded-lg mb-4">{rbacErr}</div>
    {/if}

    <!-- Role Defaults -->
    <div class="mb-4">
      <p class="text-xs font-medium text-surface-500 mb-2">Role Defaults</p>
      <div class="space-y-2">
        {#each Object.entries(rbacConfig.role_defaults || {}) as [role, cfg]}
          {#if editingRole === role}
            <div class="p-3 bg-surface-50 dark:bg-surface-700 rounded-lg space-y-2">
              <p class="text-xs font-medium text-surface-700 dark:text-surface-200">{role}</p>
              <p class="text-[10px] text-surface-500 mt-1">Pages (internal dashboard pages)</p>
              <div class="flex flex-wrap gap-1.5">
                <button onclick={() => toggleRolePage("*")} class="px-2 py-0.5 text-xs rounded-md transition-colors {editRolePages.includes('*') ? 'bg-primary-600 text-white' : 'bg-surface-200 dark:bg-surface-600 text-surface-500'}">* (all)</button>
                {#each allPages as p}
                  <button onclick={() => toggleRolePage(p)} class="px-2 py-0.5 text-xs rounded-md transition-colors {editRolePages.includes(p) ? 'bg-primary-600 text-white' : 'bg-surface-200 dark:bg-surface-600 text-surface-500'}">{p}</button>
                {/each}
              </div>
              <p class="text-[10px] text-surface-500 mt-2">Sidebar Links (all services)</p>
              <div class="flex flex-wrap gap-1.5">
                <button onclick={() => toggleRoleSidebarLink("*")} class="px-2 py-0.5 text-xs rounded-md transition-colors {editRoleSidebarLinks.includes('*') ? 'bg-primary-600 text-white' : 'bg-surface-200 dark:bg-surface-600 text-surface-500'}">* (all)</button>
                {#each allSidebarLinks as link}
                  <button onclick={() => toggleRoleSidebarLink(link)} class="px-2 py-0.5 text-xs rounded-md transition-colors {editRoleSidebarLinks.includes(link) ? 'bg-primary-600 text-white' : 'bg-surface-200 dark:bg-surface-600 text-surface-500'}">{link}</button>
                {/each}
              </div>
              <div class="flex gap-2">
                <button onclick={saveRoleDefault} class="px-3 py-1 text-xs font-medium text-white bg-primary-600 hover:bg-primary-700 rounded-md transition-colors">Save</button>
                <button onclick={cancelEdit} class="px-3 py-1 text-xs font-medium text-surface-600 hover:text-surface-800 dark:text-surface-400 dark:hover:text-surface-200">Cancel</button>
              </div>
            </div>
          {:else}
            <div class="flex items-center justify-between p-2 bg-surface-50 dark:bg-surface-700 rounded-lg">
              <div class="flex flex-col gap-1 text-xs">
                <div class="flex items-center gap-2">
                  <span class="font-medium text-surface-700 dark:text-surface-300 w-16">{role}</span>
                  <span class="text-surface-400">Pages: {cfg.pages === "*" ? "All" : (cfg.pages || []).join(", ")}</span>
                </div>
                <div class="flex items-center gap-2">
                  <span class="w-16"></span>
                  <span class="text-surface-400">Links: {cfg.sidebar_links === "*" ? "All" : (cfg.sidebar_links || ["*"]).join(", ")}</span>
                </div>
              </div>
              <button onclick={() => startEditRole(role, cfg.pages, cfg.sidebar_links)} class="text-xs text-primary-500 hover:text-primary-700">Edit</button>
            </div>
          {/if}
        {/each}
      </div>
    </div>

    <!-- User Overrides -->
    <div class="mb-4">
      <div class="flex items-center justify-between mb-2">
        <p class="text-xs font-medium text-surface-500">User Overrides</p>
        {#if editingUser !== "__new__"}
          <button onclick={startAdd} class="px-2 py-1 text-xs font-medium text-primary-600 bg-primary-50 hover:bg-primary-100 rounded-md transition-colors">Add Override</button>
        {/if}
      </div>

      {#if Object.keys(rbacConfig.user_overrides || {}).length > 0}
        <div class="space-y-2">
          {#each Object.entries(rbacConfig.user_overrides) as [username, cfg]}
            {#if editingUser === username}
              <div class="p-3 bg-surface-50 dark:bg-surface-700 rounded-lg space-y-2">
                <p class="text-xs font-medium text-surface-700 dark:text-surface-200">{username}</p>
                <div class="flex flex-wrap gap-1.5">
                  {#each allPages as p}
                    <button onclick={() => togglePage(p)} class="px-2 py-0.5 text-xs rounded-md transition-colors {editPages.includes(p) ? 'bg-primary-600 text-white' : 'bg-surface-200 dark:bg-surface-600 text-surface-500'}">{p}</button>
                  {/each}
                </div>
                <div class="flex gap-2">
                  <button onclick={saveOverride} class="px-3 py-1 text-xs font-medium text-white bg-primary-600 hover:bg-primary-700 rounded-md">Save</button>
                  <button onclick={cancelEdit} class="px-3 py-1 text-xs font-medium text-surface-500 bg-surface-200 hover:bg-surface-300 rounded-md">Cancel</button>
                </div>
              </div>
            {:else}
              <div class="flex items-center justify-between px-3 py-2 bg-surface-50 dark:bg-surface-700 rounded-lg">
                <div>
                  <span class="text-sm font-medium text-surface-700 dark:text-surface-200">{username}</span>
                  <span class="text-xs text-surface-400 ml-2">{(cfg.pages || []).join(", ")}</span>
                </div>
                <div class="flex gap-1.5">
                  <button onclick={() => startEdit(username, cfg.pages || [])} class="text-xs text-primary-500 hover:text-primary-700">Edit</button>
                  <button onclick={() => deleteOverride(username)} class="text-xs text-rose-500 hover:text-rose-700">Delete</button>
                </div>
              </div>
            {/if}
          {/each}
        </div>
      {:else if editingUser !== "__new__"}
        <p class="text-sm text-surface-400">No user overrides configured</p>
      {/if}

      {#if editingUser === "__new__"}
        <div class="p-3 bg-surface-50 dark:bg-surface-700 rounded-lg space-y-2 mt-2">
          <input type="text" bind:value={newUsername} placeholder="Username" class="w-full px-3 py-1.5 text-sm border border-surface-200 dark:border-surface-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-surface-800 dark:text-white" />
          <div class="flex flex-wrap gap-1.5">
            {#each allPages as p}
              <button onclick={() => togglePage(p)} class="px-2 py-0.5 text-xs rounded-md transition-colors {editPages.includes(p) ? 'bg-primary-600 text-white' : 'bg-surface-200 dark:bg-surface-600 text-surface-500'}">{p}</button>
            {/each}
          </div>
          <div class="flex gap-2">
            <button onclick={saveOverride} class="px-3 py-1 text-xs font-medium text-white bg-primary-600 hover:bg-primary-700 rounded-md">Save</button>
            <button onclick={cancelEdit} class="px-3 py-1 text-xs font-medium text-surface-500 bg-surface-200 hover:bg-surface-300 rounded-md">Cancel</button>
          </div>
        </div>
      {/if}
    </div>
  </div>
  {/if}

  <div class="bg-white dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-6 shadow-sm">
    <div class="flex items-center justify-between mb-4">
      <h3 class="text-sm font-semibold text-surface-700 dark:text-surface-200">Audit Log</h3>
      <button onclick={loadAuditLog} disabled={auditLoading} class="px-3 py-1.5 text-xs font-medium text-primary-600 bg-primary-50 hover:bg-primary-100 rounded-lg transition-colors disabled:opacity-50">
        {auditLoading ? "Loading..." : auditEntries.length ? "Refresh" : "Load"}
      </button>
    </div>
    {#if auditEntries.length > 0}
      <div class="flex gap-1.5 mb-3">
        {#each ["all", "high", "medium", "low", "info"] as lvl}
          <button onclick={() => auditFilter = lvl} class="px-2.5 py-1 text-xs rounded-md transition-colors {auditFilter === lvl ? 'bg-surface-800 text-white' : 'bg-surface-100 text-surface-500 hover:bg-surface-200'}">
            {lvl === "all" ? "All" : levelLabels[lvl] || lvl}
            {#if lvl !== "all"}
              <span class="ml-1 opacity-60">{auditEntries.filter(e => e.level === lvl).length}</span>
            {/if}
          </button>
        {/each}
      </div>
      <div class="max-h-80 overflow-auto rounded-lg bg-surface-950 p-3 space-y-0.5">
        {#each filteredEntries as e}
          <div class="text-xs font-mono flex gap-2 leading-5">
            <span class="{levelColors[e.level]} w-8 shrink-0">{levelLabels[e.level]}</span>
            <span class="text-surface-500 shrink-0">{e.ts.replace('T', ' ')}</span>
            <span class="text-surface-400 shrink-0 w-10">{e.status}</span>
            <span class="text-surface-300 shrink-0">{e.user}</span>
            <span class="text-surface-500">{e.method} {e.path}</span>
          </div>
        {/each}
      </div>
    {:else if !auditLoading}
      <p class="text-sm text-surface-400">Click Load to view recent audit log entries</p>
    {/if}
  </div>
</div>
