import { useEffect, useMemo, useState } from "react";
import { createPortal } from "react-dom";
import QRCode from "qrcode";
import { listBlockedUsers, updateMyProfile } from "../api/users";
import { requestUploadUrl, uploadToPresignedUrl } from "../api/chats";
import {
  disableTwoFactor,
  enableTwoFactor,
  getTwoFactorRecoveryStatus,
  listSessions,
  regenerateTwoFactorRecoveryCodes,
  revokeAllSessions,
  revokeSession,
  setupTwoFactor
} from "../api/auth";
import { getNotifications, type NotificationItem } from "../api/notifications";
import type { AuthSession, AuthUser } from "../chat/types";
import { useAuthStore } from "../store/authStore";
import { useUiStore } from "../store/uiStore";
import { AppPreferences, getAppPreferences, updateAppPreferences } from "../utils/preferences";
import { AvatarCropModal } from "./AvatarCropModal";

type SettingsPage = "main" | "general" | "notifications" | "privacy";

interface Props {
  open: boolean;
  onClose: () => void;
}

export function SettingsPanel({ open, onClose }: Props) {
  const me = useAuthStore((s) => s.me);
  const logout = useAuthStore((s) => s.logout);
  const showToast = useUiStore((s) => s.showToast);
  const [page, setPage] = useState<SettingsPage>("main");
  const [prefs, setPrefs] = useState<AppPreferences>(() => getAppPreferences());
  const [blockedCount, setBlockedCount] = useState(0);
  const [savingPrivacy, setSavingPrivacy] = useState(false);
  const [privacyPrivateMessages, setPrivacyPrivateMessages] = useState<"everyone" | "contacts" | "nobody">("everyone");
  const [sessions, setSessions] = useState<AuthSession[]>([]);
  const [sessionsLoading, setSessionsLoading] = useState(false);
  const [sessionsError, setSessionsError] = useState<string | null>(null);
  const [revokeAllLoading, setRevokeAllLoading] = useState(false);
  const [revokingSessionIds, setRevokingSessionIds] = useState<string[]>([]);
  const [twofaCode, setTwofaCode] = useState("");
  const [twofaSecret, setTwofaSecret] = useState<string | null>(null);
  const [twofaUrl, setTwofaUrl] = useState<string | null>(null);
  const [twofaQrUrl, setTwofaQrUrl] = useState<string | null>(null);
  const [twofaError, setTwofaError] = useState<string | null>(null);
  const [recoveryRemaining, setRecoveryRemaining] = useState<number>(0);
  const [recoveryCodes, setRecoveryCodes] = useState<string[]>([]);
  const [privacyLastSeen, setPrivacyLastSeen] = useState<"everyone" | "contacts" | "nobody">("everyone");
  const [privacyAvatar, setPrivacyAvatar] = useState<"everyone" | "contacts" | "nobody">("everyone");
  const [privacyGroupInvites, setPrivacyGroupInvites] = useState<"everyone" | "contacts" | "nobody">("everyone");
  const [notificationItems, setNotificationItems] = useState<NotificationItem[]>([]);
  const [notificationItemsLoading, setNotificationItemsLoading] = useState(false);
  const [profileDraft, setProfileDraft] = useState({
    name: "",
    username: "",
    bio: "",
    avatarUrl: "",
  });
  const [profileAvatarUploading, setProfileAvatarUploading] = useState(false);
  const [avatarCropFile, setAvatarCropFile] = useState<File | null>(null);

  useEffect(() => {
    if (!me) {
      return;
    }
    setPrivacyPrivateMessages(me.privacy_private_messages || (me.allow_private_messages ? "everyone" : "nobody"));
    setPrivacyLastSeen(me.privacy_last_seen || "everyone");
    setPrivacyAvatar(me.privacy_avatar || "everyone");
    setPrivacyGroupInvites(me.privacy_group_invites || "everyone");
    setProfileDraft({
      name: me.name || "",
      username: me.username || "",
      bio: me.bio || "",
      avatarUrl: me.avatar_url || "",
    });
  }, [me]);

  useEffect(() => {
    if (!twofaUrl) {
      setTwofaQrUrl(null);
      return;
    }
    let cancelled = false;
    void (async () => {
      try {
        const url = await QRCode.toDataURL(twofaUrl, { margin: 1, width: 192 });
        if (!cancelled) {
          setTwofaQrUrl(url);
        }
      } catch {
        if (!cancelled) {
          setTwofaQrUrl(null);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [twofaUrl]);

  useEffect(() => {
    if (!open) {
      return;
    }
    setPrefs(getAppPreferences());
  }, [open]);

  useEffect(() => {
    if (!open || page !== "privacy") {
      return;
    }
    let cancelled = false;
    void (async () => {
      try {
        const blocked = await listBlockedUsers();
        if (!cancelled) {
          setBlockedCount(blocked.length);
        }
      } catch {
        if (!cancelled) {
          setBlockedCount(0);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, page]);

  useEffect(() => {
    if (!open || page !== "privacy" || !me?.twofa_enabled) {
      setRecoveryRemaining(0);
      return;
    }
    let cancelled = false;
    void (async () => {
      try {
        const status = await getTwoFactorRecoveryStatus();
        if (!cancelled) {
          setRecoveryRemaining(status.remaining_codes);
        }
      } catch {
        if (!cancelled) {
          setRecoveryRemaining(0);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, page, me?.twofa_enabled]);

  useEffect(() => {
    if (!open || page !== "notifications") {
      return;
    }
    let cancelled = false;
    setNotificationItemsLoading(true);
    void (async () => {
      try {
        const rows = await getNotifications(30);
        if (!cancelled) {
          setNotificationItems(rows);
        }
      } catch {
        if (!cancelled) {
          setNotificationItems([]);
        }
      } finally {
        if (!cancelled) {
          setNotificationItemsLoading(false);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, page]);

  useEffect(() => {
    if (!open || page !== "privacy") {
      return;
    }
    let cancelled = false;
    setSessionsLoading(true);
    setSessionsError(null);
    void (async () => {
      try {
        const rows = await listSessions();
        if (!cancelled) {
          setSessions(rows);
        }
      } catch {
        if (!cancelled) {
          setSessions([]);
          setSessionsError("Failed to load sessions");
        }
      } finally {
        if (!cancelled) {
          setSessionsLoading(false);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [open, page]);

  const title = useMemo(() => {
    if (page === "general") return "General";
    if (page === "notifications") return "Notifications";
    if (page === "privacy") return "Privacy and Security";
    return "Settings";
  }, [page]);

  if (!open || !me) {
    return null;
  }

  function updatePrefs(patch: Partial<AppPreferences>) {
    setPrefs(updateAppPreferences(patch));
  }

  const notificationStatus = "Notification" in window ? Notification.permission : "denied";

  async function uploadAvatar(file: File) {
    setProfileAvatarUploading(true);
    try {
      const upload = await requestUploadUrl(file);
      await uploadToPresignedUrl(upload.upload_url, upload.required_headers, file);
      setProfileDraft((prev) => ({ ...prev, avatarUrl: upload.file_url }));
      showToast("Avatar uploaded");
    } catch {
      showToast("Avatar upload failed");
    } finally {
      setProfileAvatarUploading(false);
    }
  }

  async function copyRecoveryCodes(codes: string[]) {
    if (!codes.length) {
      showToast("No recovery codes to copy");
      return;
    }
    try {
      await navigator.clipboard.writeText(codes.join("\n"));
      showToast("Recovery codes copied");
    } catch {
      showToast("Failed to copy recovery codes");
    }
  }

  function downloadRecoveryCodes(codes: string[]) {
    if (!codes.length) {
      showToast("No recovery codes to download");
      return;
    }
    const content = `BenyaMessenger 2FA Recovery Codes\n\n${codes.join("\n")}\n`;
    const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = "benya-recovery-codes.txt";
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
    showToast("Recovery codes downloaded");
  }

  return createPortal(
    <div className="fixed inset-0 z-[150] bg-slate-950/60" onClick={onClose}>
      <aside
        className="absolute left-0 top-0 flex h-full w-full max-w-md flex-col border-r border-slate-700/70 bg-slate-900 shadow-2xl"
        onClick={(event) => event.stopPropagation()}
      >
        <div className="flex items-center justify-between border-b border-slate-700/60 px-4 py-3">
          <div className="flex items-center gap-2">
            {page !== "main" ? (
              <button className="rounded bg-slate-700 px-2 py-1 text-xs" onClick={() => setPage("main")} type="button">
                Back
              </button>
            ) : null}
            <p className="text-lg font-semibold">{title}</p>
          </div>
          <button className="rounded bg-slate-700 px-2 py-1 text-xs" onClick={onClose} type="button">
            Close
          </button>
        </div>

        <div className="tg-scrollbar min-h-0 flex-1 overflow-y-auto">
          {page === "main" ? (
            <>
              <section className="border-b border-slate-700/60 px-4 py-4">
                <p className="mb-2 text-xs uppercase tracking-wide text-slate-400">Profile</p>
                <div className="space-y-2">
                  <div className="flex items-center gap-3 rounded border border-slate-700/70 bg-slate-800/40 p-2">
                    {profileDraft.avatarUrl.trim() ? (
                      <img alt="avatar preview" className="h-12 w-12 rounded-full object-cover" src={profileDraft.avatarUrl.trim()} />
                    ) : (
                      <div className="flex h-12 w-12 flex-col items-center justify-center rounded-full bg-slate-700 text-[10px] leading-[1.05] text-slate-300">
                        <span>No</span>
                        <span>avatar</span>
                      </div>
                    )}
                    <div className="flex flex-wrap items-center gap-2">
                      <label className="cursor-pointer rounded bg-sky-500 px-3 py-1.5 text-xs font-semibold text-slate-950">
                        {profileAvatarUploading ? "Uploading..." : "Upload avatar"}
                        <input
                          accept="image/*"
                          className="hidden"
                          disabled={profileAvatarUploading}
                          onChange={(e) => {
                            const file = e.target.files?.[0];
                            e.currentTarget.value = "";
                            if (!file) {
                              return;
                            }
                            setAvatarCropFile(file);
                          }}
                          type="file"
                        />
                      </label>
                      {profileDraft.avatarUrl.trim() ? (
                        <button
                          className="rounded bg-slate-700 px-3 py-1.5 text-xs"
                          onClick={() => setProfileDraft((prev) => ({ ...prev, avatarUrl: "" }))}
                          type="button"
                        >
                          Remove
                        </button>
                      ) : null}
                    </div>
                  </div>
                  <input
                    className="w-full rounded bg-slate-800 px-3 py-2 text-sm"
                    placeholder="Name"
                    value={profileDraft.name}
                    onChange={(e) => setProfileDraft((prev) => ({ ...prev, name: e.target.value }))}
                  />
                  <input
                    className="w-full rounded bg-slate-800 px-3 py-2 text-sm"
                    placeholder="Username"
                    value={profileDraft.username}
                    onChange={(e) => setProfileDraft((prev) => ({ ...prev, username: e.target.value.replace("@", "") }))}
                  />
                  <input
                    className="w-full rounded bg-slate-800 px-3 py-2 text-sm"
                    placeholder="Bio"
                    value={profileDraft.bio}
                    onChange={(e) => setProfileDraft((prev) => ({ ...prev, bio: e.target.value }))}
                  />
                  <input
                    className="w-full rounded bg-slate-800 px-3 py-2 text-sm"
                    placeholder="Avatar URL (optional)"
                    value={profileDraft.avatarUrl}
                    onChange={(e) => setProfileDraft((prev) => ({ ...prev, avatarUrl: e.target.value }))}
                  />
                  <button
                    className="w-full rounded bg-sky-500 px-3 py-2 text-sm font-semibold text-slate-950 disabled:opacity-60"
                    disabled={profileAvatarUploading}
                    onClick={async () => {
                      const updated = await updateMyProfile({
                        name: profileDraft.name.trim() || undefined,
                        username: profileDraft.username.trim() || undefined,
                        bio: profileDraft.bio.trim() || null,
                        avatar_url: profileDraft.avatarUrl.trim() || null,
                      });
                      useAuthStore.setState({ me: updated as AuthUser });
                    }}
                    type="button"
                  >
                    Save profile
                  </button>
                </div>
              </section>

              <section className="border-b border-slate-700/60 py-1">
                <SettingsRow label="General Settings" value={`${prefs.messageFontSize}px, ${prefs.theme}`} onClick={() => setPage("general")} />
                <SettingsRow
                  label="Notifications"
                  value={prefs.webNotifications ? "Enabled" : "Disabled"}
                  onClick={() => setPage("notifications")}
                />
                <SettingsRow
                  label="Privacy and Security"
                  value={
                    privacyPrivateMessages === "everyone"
                      ? "Everybody can message"
                      : privacyPrivateMessages === "contacts"
                        ? "Only contacts can message"
                        : "Messages disabled"
                  }
                  onClick={() => setPage("privacy")}
                />
              </section>
            </>
          ) : null}

          {page === "general" ? (
            <div className="space-y-4 px-4 py-3">
              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <p className="mb-2 text-xs uppercase tracking-wide text-slate-400">Settings</p>
                <p className="mb-2 text-sm">Message Font Size</p>
                <div className="flex items-center gap-3">
                  <input
                    className="w-full"
                    min={12}
                    max={24}
                    step={1}
                    type="range"
                    value={prefs.messageFontSize}
                    onChange={(e) => updatePrefs({ messageFontSize: Number(e.target.value) })}
                  />
                  <span className="text-sm">{prefs.messageFontSize}</span>
                </div>
              </section>

              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <p className="mb-2 text-xs uppercase tracking-wide text-slate-400">Theme</p>
                <RadioOption checked={prefs.theme === "light"} label="Light" onChange={() => updatePrefs({ theme: "light" })} />
                <RadioOption checked={prefs.theme === "dark"} label="Dark" onChange={() => updatePrefs({ theme: "dark" })} />
                <RadioOption checked={prefs.theme === "system"} label="System" onChange={() => updatePrefs({ theme: "system" })} />
              </section>

              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <p className="mb-2 text-xs uppercase tracking-wide text-slate-400">Keyboard</p>
                <RadioOption checked={prefs.sendMode === "enter"} label="Send with Enter (Shift+Enter new line)" onChange={() => updatePrefs({ sendMode: "enter" })} />
                <RadioOption checked={prefs.sendMode === "ctrl_enter"} label="Send with Ctrl+Enter (Enter new line)" onChange={() => updatePrefs({ sendMode: "ctrl_enter" })} />
              </section>
            </div>
          ) : null}

          {page === "notifications" ? (
            <div className="space-y-4 px-4 py-3">
              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <p className="mb-2 text-xs uppercase tracking-wide text-slate-400">Web Notifications</p>
                <CheckboxOption
                  checked={prefs.webNotifications}
                  label={`Web Notifications (${notificationStatus})`}
                  onChange={async (checked) => {
                    updatePrefs({ webNotifications: checked });
                    if (checked && "Notification" in window && Notification.permission === "default") {
                      await Notification.requestPermission();
                    }
                  }}
                />
                <CheckboxOption checked={prefs.notificationSound} label="Notification Sound" onChange={(checked) => updatePrefs({ notificationSound: checked })} />
                <CheckboxOption checked={prefs.messagePreview} label="Message Preview" onChange={(checked) => updatePrefs({ messagePreview: checked })} />
              </section>

              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <p className="mb-2 text-xs uppercase tracking-wide text-slate-400">Chats</p>
                <CheckboxOption checked={prefs.privateNotifications} label="Notifications for private chats" onChange={(checked) => updatePrefs({ privateNotifications: checked })} />
                <CheckboxOption checked={prefs.groupNotifications} label="Notifications for groups" onChange={(checked) => updatePrefs({ groupNotifications: checked })} />
                <CheckboxOption checked={prefs.channelNotifications} label="Notifications for channels" onChange={(checked) => updatePrefs({ channelNotifications: checked })} />
              </section>

              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <div className="mb-2 flex items-center justify-between">
                  <p className="text-xs uppercase tracking-wide text-slate-400">Recent Notifications</p>
                  <button
                    className="rounded bg-slate-700 px-2 py-1 text-[11px]"
                    onClick={async () => {
                      setNotificationItemsLoading(true);
                      try {
                        setNotificationItems(await getNotifications(30));
                      } finally {
                        setNotificationItemsLoading(false);
                      }
                    }}
                    type="button"
                  >
                    Refresh
                  </button>
                </div>
                {notificationItemsLoading ? <p className="text-xs text-slate-400">Loading...</p> : null}
                {!notificationItemsLoading && notificationItems.length === 0 ? <p className="text-xs text-slate-400">No notifications yet</p> : null}
                <div className="space-y-2">
                  {notificationItems.slice(0, 20).map((item) => (
                    <div className="rounded bg-slate-900/50 px-2 py-2" key={item.id}>
                      <p className="text-xs font-semibold text-slate-200">{item.event_type}</p>
                      <p className="mt-0.5 line-clamp-2 text-[11px] text-slate-400">{renderNotificationPayload(item.payload)}</p>
                      <p className="mt-0.5 text-[11px] text-slate-500">{new Date(item.created_at).toLocaleString()}</p>
                    </div>
                  ))}
                </div>
              </section>
            </div>
          ) : null}

          {page === "privacy" ? (
            <div className="space-y-4 px-4 py-3">
              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <p className="mb-2 text-xs uppercase tracking-wide text-slate-400">Privacy</p>
                <SettingsTextRow label="Blocked Users" value={String(blockedCount)} />
                <div className="mb-2 rounded bg-slate-900/50 px-2 py-2">
                  <p className="mb-1 text-xs text-slate-300">Who can send me private messages?</p>
                  <select
                    className="w-full rounded bg-slate-800 px-2 py-1 text-xs"
                    value={privacyPrivateMessages}
                    onChange={(e) => setPrivacyPrivateMessages(e.target.value as "everyone" | "contacts" | "nobody")}
                  >
                    <option value="everyone">Everybody</option>
                    <option value="contacts">My contacts</option>
                    <option value="nobody">Nobody</option>
                  </select>
                </div>
                <div className="mb-2 rounded bg-slate-900/50 px-2 py-2">
                  <p className="mb-1 text-xs text-slate-300">Who can see my profile photo?</p>
                  <select
                    className="w-full rounded bg-slate-800 px-2 py-1 text-xs"
                    value={privacyAvatar}
                    onChange={(e) => setPrivacyAvatar(e.target.value as "everyone" | "contacts" | "nobody")}
                  >
                    <option value="everyone">Everybody</option>
                    <option value="contacts">My contacts</option>
                    <option value="nobody">Nobody</option>
                  </select>
                </div>
                <div className="mb-2 rounded bg-slate-900/50 px-2 py-2">
                  <p className="mb-1 text-xs text-slate-300">Who can see my last seen?</p>
                  <select
                    className="w-full rounded bg-slate-800 px-2 py-1 text-xs"
                    value={privacyLastSeen}
                    onChange={(e) => setPrivacyLastSeen(e.target.value as "everyone" | "contacts" | "nobody")}
                  >
                    <option value="everyone">Everybody</option>
                    <option value="contacts">My contacts</option>
                    <option value="nobody">Nobody</option>
                  </select>
                </div>
                <div className="mb-2 rounded bg-slate-900/50 px-2 py-2">
                  <p className="mb-1 text-xs text-slate-300">Who can add me to groups?</p>
                  <select
                    className="w-full rounded bg-slate-800 px-2 py-1 text-xs"
                    value={privacyGroupInvites}
                    onChange={(e) => setPrivacyGroupInvites(e.target.value as "everyone" | "contacts" | "nobody")}
                  >
                    <option value="everyone">Everybody</option>
                    <option value="contacts">My contacts</option>
                    <option value="nobody">Nobody</option>
                  </select>
                </div>
                <button
                  className="w-full rounded bg-sky-500 px-3 py-2 text-sm font-semibold text-slate-950 disabled:opacity-60"
                  onClick={async () => {
                    setSavingPrivacy(true);
                    try {
                      const updated = await updateMyProfile({
                        allow_private_messages: privacyPrivateMessages !== "nobody",
                        privacy_private_messages: privacyPrivateMessages,
                        privacy_last_seen: privacyLastSeen,
                        privacy_avatar: privacyAvatar,
                        privacy_group_invites: privacyGroupInvites,
                      });
                      useAuthStore.setState({ me: updated as AuthUser });
                      showToast("Privacy settings saved");
                    } catch {
                      showToast("Failed to save privacy settings");
                    } finally {
                      setSavingPrivacy(false);
                    }
                  }}
                  disabled={savingPrivacy}
                  type="button"
                >
                  {savingPrivacy ? "Saving..." : "Save privacy settings"}
                </button>
              </section>
              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <div className="mb-2 flex items-center justify-between">
                  <p className="text-xs uppercase tracking-wide text-slate-400">Two-Factor Authentication</p>
                  <span className="text-[11px] text-slate-400">{me.twofa_enabled ? "Enabled" : "Disabled"}</span>
                </div>
                {!me.twofa_enabled ? (
                  <>
                    <button
                      className="mb-2 rounded bg-slate-700 px-2 py-1 text-xs"
                      onClick={async () => {
                        setTwofaError(null);
                        try {
                          const setup = await setupTwoFactor();
                          setTwofaSecret(setup.secret);
                          setTwofaUrl(setup.otpauth_url);
                        } catch {
                          setTwofaError("Failed to setup 2FA");
                        }
                      }}
                      type="button"
                    >
                      Generate secret
                    </button>
                    {twofaSecret ? (
                      <div className="mb-2 rounded bg-slate-900/50 px-2 py-2">
                        <p className="text-[11px] text-slate-400">Secret</p>
                        <p className="break-all text-xs text-slate-200">{twofaSecret}</p>
                        {twofaQrUrl ? (
                          <div className="mt-2 rounded bg-white p-2">
                            <img alt="TOTP QR" className="mx-auto h-44 w-44" src={twofaQrUrl} />
                          </div>
                        ) : null}
                        {twofaUrl ? <p className="mt-1 break-all text-[11px] text-slate-500">{twofaUrl}</p> : null}
                      </div>
                    ) : null}
                    <div className="flex gap-2">
                      <input
                        className="w-full rounded bg-slate-800 px-2 py-1.5 text-xs"
                        placeholder="Enter app code"
                        value={twofaCode}
                        onChange={(e) => setTwofaCode(e.target.value.replace(/\D/g, "").slice(0, 8))}
                      />
                      <button
                        className="rounded bg-sky-500 px-2 py-1 text-xs font-semibold text-slate-950"
                        onClick={async () => {
                          setTwofaError(null);
                          try {
                            await enableTwoFactor(twofaCode);
                            await useAuthStore.getState().loadMe();
                            setTwofaCode("");
                          } catch {
                            setTwofaError("Invalid 2FA code");
                          }
                        }}
                        type="button"
                      >
                        Enable
                      </button>
                    </div>
                  </>
                ) : (
                  <div className="flex gap-2">
                    <input
                      className="w-full rounded bg-slate-800 px-2 py-1.5 text-xs"
                      placeholder="Code to disable"
                      value={twofaCode}
                      onChange={(e) => setTwofaCode(e.target.value.replace(/\D/g, "").slice(0, 8))}
                    />
                    <button
                      className="rounded bg-red-500 px-2 py-1 text-xs font-semibold text-white"
                      onClick={async () => {
                        setTwofaError(null);
                        try {
                          await disableTwoFactor(twofaCode);
                          await useAuthStore.getState().loadMe();
                          setTwofaCode("");
                          setTwofaSecret(null);
                          setTwofaUrl(null);
                          setTwofaQrUrl(null);
                          setRecoveryCodes([]);
                          setRecoveryRemaining(0);
                        } catch {
                          setTwofaError("Invalid 2FA code");
                        }
                      }}
                      type="button"
                    >
                      Disable
                    </button>
                  </div>
                )}
                {twofaError ? <p className="mt-2 text-xs text-red-400">{twofaError}</p> : null}
                {me.twofa_enabled ? (
                  <div className="mt-3 rounded bg-slate-900/50 px-2 py-2">
                    {recoveryRemaining === 0 ? (
                      <p className="mb-2 rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] text-amber-200">
                        You have no recovery codes. Generate and save them to avoid lockout.
                      </p>
                    ) : null}
                    <div className="mb-2 flex items-center justify-between gap-2">
                      <p className="text-xs text-slate-300">Recovery codes remaining: {recoveryRemaining}</p>
                      <button
                        className="rounded bg-slate-700 px-2 py-1 text-[11px]"
                        onClick={async () => {
                          setTwofaError(null);
                          try {
                            const result = await regenerateTwoFactorRecoveryCodes(twofaCode);
                            setRecoveryCodes(result.codes);
                            setRecoveryRemaining(result.codes.length);
                            setTwofaCode("");
                            showToast("New recovery codes generated");
                          } catch {
                            setTwofaError("Invalid 2FA code for recovery codes");
                          }
                        }}
                        type="button"
                      >
                        Generate recovery codes
                      </button>
                    </div>
                    {recoveryCodes.length > 0 ? (
                      <div className="rounded border border-amber-500/40 bg-amber-500/10 p-2">
                        <p className="mb-1 text-[11px] text-amber-200">Save these codes now. They are shown only once.</p>
                        <div className="mb-2 flex items-center gap-2">
                          <button
                            className="rounded bg-slate-700 px-2 py-1 text-[11px]"
                            onClick={() => {
                              void copyRecoveryCodes(recoveryCodes);
                            }}
                            type="button"
                          >
                            Copy
                          </button>
                          <button
                            className="rounded bg-slate-700 px-2 py-1 text-[11px]"
                            onClick={() => downloadRecoveryCodes(recoveryCodes)}
                            type="button"
                          >
                            Download .txt
                          </button>
                        </div>
                        <div className="grid grid-cols-2 gap-1">
                          {recoveryCodes.map((item) => (
                            <code className="rounded bg-slate-900 px-1.5 py-1 text-[11px]" key={item}>
                              {item}
                            </code>
                          ))}
                        </div>
                      </div>
                    ) : null}
                  </div>
                ) : null}
              </section>
              <section className="rounded border border-slate-700/70 bg-slate-800/50 p-3">
                <div className="mb-2 flex items-center justify-between">
                  <p className="text-xs uppercase tracking-wide text-slate-400">Active Sessions</p>
                  <button
                    className="rounded bg-slate-700 px-2 py-1 text-xs disabled:opacity-60"
                    onClick={async () => {
                      if (revokeAllLoading) {
                        return;
                      }
                      if (!window.confirm("Revoke all sessions and log out on this device?")) {
                        return;
                      }
                      setRevokeAllLoading(true);
                      try {
                        await revokeAllSessions();
                        setSessions([]);
                        logout();
                        onClose();
                      } catch {
                        showToast("Failed to revoke sessions");
                      } finally {
                        setRevokeAllLoading(false);
                      }
                    }}
                    disabled={revokeAllLoading}
                    type="button"
                  >
                    {revokeAllLoading ? "Revoking..." : "Revoke all"}
                  </button>
                </div>
                {sessionsLoading ? <p className="text-xs text-slate-400">Loading sessions...</p> : null}
                {!sessionsLoading && sessionsError ? <p className="text-xs text-red-400">{sessionsError}</p> : null}
                {!sessionsLoading && sessions.length === 0 ? <p className="text-xs text-slate-400">No active sessions</p> : null}
                <div className="space-y-2">
                  {sessions.map((session) => (
                    <div className="rounded bg-slate-900/50 px-2 py-2" key={session.jti}>
                      <p className="truncate text-xs text-slate-300">
                        {session.user_agent || "Unknown device"}
                        {session.current ? " (current)" : ""}
                      </p>
                      <p className="truncate text-[11px] text-slate-500">Token: {session.token_type || "refresh"}</p>
                      <p className="truncate text-[11px] text-slate-400">{session.ip_address || "Unknown IP"}</p>
                      <p className="text-[11px] text-slate-500">{new Date(session.created_at).toLocaleString()}</p>
                      {session.token_type === "refresh" ? (
                        <button
                          className="mt-1 rounded bg-slate-700 px-2 py-1 text-[11px] disabled:opacity-60"
                          onClick={async () => {
                            const token = session.jti;
                            setRevokingSessionIds((prev) => (prev.includes(token) ? prev : [...prev, token]));
                            try {
                              await revokeSession(session.jti);
                              setSessions((prev) => prev.filter((item) => item.jti !== session.jti));
                            } catch {
                              showToast("Failed to revoke session");
                            } finally {
                              setRevokingSessionIds((prev) => prev.filter((item) => item !== token));
                            }
                          }}
                          disabled={revokingSessionIds.includes(session.jti)}
                          type="button"
                        >
                          {revokingSessionIds.includes(session.jti) ? "Revoking..." : "Revoke"}
                        </button>
                      ) : (
                        <p className="mt-1 text-[11px] text-slate-500">Use Revoke all to invalidate active access token.</p>
                      )}
                    </div>
                  ))}
                </div>
              </section>
            </div>
          ) : null}
        </div>
      </aside>
      <AvatarCropModal
        file={avatarCropFile}
        onApply={(processedFile) => {
          setAvatarCropFile(null);
          void uploadAvatar(processedFile);
        }}
        onCancel={() => setAvatarCropFile(null)}
        open={Boolean(avatarCropFile)}
      />
    </div>,
    document.body
  );
}

function renderNotificationPayload(payload: string): string {
  try {
    const parsed = JSON.parse(payload) as { text?: string; title?: string; body?: string; chat_id?: number };
    return parsed.text || parsed.body || parsed.title || (parsed.chat_id ? `Chat #${parsed.chat_id}` : payload);
  } catch {
    return payload;
  }
}

function SettingsRow({ label, value, onClick }: { label: string; value: string; onClick: () => void }) {
  return (
    <button className="flex w-full items-center justify-between px-4 py-3 text-left hover:bg-slate-800/60" onClick={onClick} type="button">
      <div className="min-w-0">
        <p className="truncate text-sm font-semibold">{label}</p>
        <p className="truncate text-xs text-slate-400">{value}</p>
      </div>
      <span className="text-slate-400">›</span>
    </button>
  );
}

function SettingsTextRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="mb-2 flex items-center justify-between gap-2 rounded bg-slate-900/50 px-2 py-2">
      <p className="text-sm">{label}</p>
      <p className="text-xs text-slate-400">{value}</p>
    </div>
  );
}

function RadioOption({ checked, label, onChange }: { checked: boolean; label: string; onChange: () => void }) {
  return (
    <label className="mb-2 flex items-center gap-2 text-sm">
      <input checked={checked} onChange={onChange} type="radio" />
      <span>{label}</span>
    </label>
  );
}

function CheckboxOption({
  checked,
  label,
  onChange,
  disabled,
}: {
  checked: boolean;
  label: string;
  onChange: (checked: boolean) => void;
  disabled?: boolean;
}) {
  return (
    <label className="mb-2 flex items-center gap-2 text-sm">
      <input checked={checked} disabled={disabled} onChange={(e) => onChange(e.target.checked)} type="checkbox" />
      <span>{label}</span>
    </label>
  );
}
