import { useEffect, useState, useCallback, type ComponentType, type ReactNode } from 'react';
import { toast } from 'sonner';
import {
  User,
  Mail,
  KeyRound,
  ShieldCheck,
  Loader2,
  History,
  Monitor,
  Smartphone,
  ChevronRight,
  Bell,
  SendHorizontal,
  ScanLine,
  TriangleAlert,
  Copy,
  Check,
  Lock,
  LogOut,
} from 'lucide-react';
import { api } from '@/api';
import { errMessage } from '@/lib/utils';
import { useAuth, type User as AuthUser } from '@/hooks/useAuth';
import { useAutoSave } from '@/hooks/useAutoSave';
import { SaveStatus } from '@/components/ui/save-status';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Switch } from '@/components/ui/switch';
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
} from '@/components/ui/dialog';
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from '@/components/ui/alert-dialog';

type Settings = Record<string, unknown>;

interface Profile {
  username?: string;
  display_name?: string;
  displayName?: string;
  name?: string;
  role?: string;
  last_password_change_at?: string;
  password_changed_at?: string;
  [key: string]: unknown;
}

interface LoginEntry {
  id: number | string;
  success?: boolean;
  user_agent?: string;
  browser?: string;
  os?: string;
  device_type?: string;
  logged_in_at?: string;
  is_current_session?: boolean;
  ip_address?: string;
  location_city?: string;
  location_region?: string;
  location_country?: string;
  location_isp?: string;
  device_fingerprint?: string;
}

function asProfile(data: unknown): Profile {
  const d = data as { profile?: Profile; user?: Profile } | Profile | null | undefined;
  return ((d as { profile?: Profile })?.profile ||
    (d as { user?: Profile })?.user ||
    (d as Profile) ||
    {}) as Profile;
}

function displayNameOf(profile: Profile): string {
  return profile.display_name || profile.displayName || profile.name || '';
}

export function asSettings(data: unknown): Settings {
  const d = data as { settings?: Settings; notifications?: Settings } | Settings | null | undefined;
  return ((d as { settings?: Settings })?.settings ||
    (d as { notifications?: Settings })?.notifications ||
    (d as Settings) ||
    {}) as Settings;
}

function formatDateTime(value: unknown): string {
  if (!value) return 'Not recorded';
  const d = new Date(value as string | number);
  if (Number.isNaN(d.getTime())) return String(value);
  return (
    d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) +
    ' ' +
    d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
  );
}

function SectionCard({
  title,
  icon: Icon,
  subtitle,
  action,
  children,
}: {
  title: ReactNode;
  icon: ComponentType<{ className?: string }>;
  subtitle?: ReactNode;
  action?: ReactNode;
  children: ReactNode;
}) {
  return (
    <section className="overflow-hidden rounded-xl border border-border/70 bg-card/90 shadow-sm">
      <div className="px-6 py-4 border-b border-border/50 flex items-center gap-3">
        <div className="h-9 w-9 rounded-lg border border-primary/15 bg-primary/10 flex items-center justify-center shrink-0">
          <Icon className="h-4 w-4 text-primary" />
        </div>
        <div className="min-w-0">
          <h2 className="text-xs font-bold uppercase tracking-widest text-muted-foreground">
            {title}
          </h2>
          {subtitle && <p className="text-sm text-muted-foreground mt-0.5">{subtitle}</p>}
        </div>
        {action && <div className="ml-auto shrink-0">{action}</div>}
      </div>
      <div>{children}</div>
    </section>
  );
}

function FieldRow({ label, value }: { label: ReactNode; value?: ReactNode }) {
  return (
    <div className="rounded-lg border border-border/60 bg-muted/25 px-4 py-3">
      <p className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
        {label}
      </p>
      <p className="mt-1 text-sm font-medium text-foreground truncate">{value || 'Not set'}</p>
    </div>
  );
}

function CheckRow({
  id,
  label,
  checked,
  onChange,
  disabled,
}: {
  id: string;
  label: ReactNode;
  checked?: boolean;
  onChange?: (v: boolean) => void;
  disabled?: boolean;
}) {
  return (
    <label
      htmlFor={id}
      className="flex items-center justify-between gap-4 rounded-lg border border-border/60 bg-muted/20 px-4 py-3"
    >
      <span className="text-sm font-medium">{label}</span>
      <Switch id={id} checked={!!checked} onCheckedChange={onChange} disabled={disabled} />
    </label>
  );
}

function parseUserAgent(ua?: string | null): { browser: string; os: string; mobile: boolean } {
  if (!ua) return { browser: 'Unknown', os: 'Unknown', mobile: false };
  const s = ua;
  const mobile = /iPhone|iPad|Android|Mobile/i.test(s);
  const browser = /Edg\//i.test(s)
    ? 'Edge'
    : /OPR\//i.test(s)
      ? 'Opera'
      : /Chrome\//i.test(s)
        ? 'Chrome'
        : /Firefox\//i.test(s)
          ? 'Firefox'
          : /Safari\//i.test(s)
            ? 'Safari'
            : /curl\//i.test(s)
              ? 'curl'
              : 'Unknown';
  const os = /iPhone|iPad/i.test(s)
    ? 'iOS'
    : /Android/i.test(s)
      ? 'Android'
      : /Windows/i.test(s)
        ? 'Windows'
        : /Macintosh/i.test(s)
          ? 'macOS'
          : /Linux/i.test(s)
            ? 'Linux'
            : 'Unknown';
  return { browser, os, mobile };
}

function deviceLabel(type?: string): string {
  if (type === 'mobile') return 'Mobile';
  if (type === 'tablet') return 'Tablet';
  if (type === 'api') return 'API client';
  return 'Desktop';
}

function LoginHistoryModal({
  history: providedHistory,
  open,
  onClose,
  onLoaded,
}: {
  history?: LoginEntry[];
  open: boolean;
  onClose: () => void;
  onLoaded?: (rows: LoginEntry[]) => void;
}) {
  const [history, setHistory] = useState<LoginEntry[]>([]);
  const [loading, setLoading] = useState(false);
  const [confirmSignOut, setConfirmSignOut] = useState(false);
  const [signingOut, setSigningOut] = useState(false);

  const fetchHistory = useCallback(() => {
    setLoading(true);
    return api
      .loginHistory()
      .then((raw) => {
        const rows = (raw as { history?: LoginEntry[] }).history ?? [];
        setHistory(rows);
        onLoaded?.(rows);
      })
      .catch((err) => {
        setHistory([]);
        toast.error(errMessage(err, 'Failed to load login history.'));
      })
      .finally(() => setLoading(false));
  }, [onLoaded]);

  useEffect(() => {
    if (!open) return;
    if (providedHistory?.length) {
      setHistory(providedHistory);
      return;
    }
    fetchHistory();
  }, [open, providedHistory, fetchHistory]);

  async function handleSignOutOthers() {
    setSigningOut(true);
    try {
      const res = await api.logoutOthers();
      const n = res?.count ?? 0;
      toast.success(
        n > 0
          ? `Signed out of ${n} other device${n === 1 ? '' : 's'}.`
          : 'No other devices were signed in.',
      );
      setConfirmSignOut(false);
      await fetchHistory(); // collapse the list to just this device
    } catch (err) {
      toast.error(errMessage(err, 'Failed to sign out other devices.'));
    } finally {
      setSigningOut(false);
    }
  }

  return (
    <Dialog
      open={open}
      onOpenChange={(v) => {
        if (!v) onClose();
      }}
    >
      <DialogContent className="sm:max-w-md border-border/60 bg-card/95 backdrop-blur-xl">
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2 text-base">
            <History className="h-4 w-4 text-primary" />
            Login History
          </DialogTitle>
          <DialogDescription className="sr-only">Your last 10 sign-in events</DialogDescription>
        </DialogHeader>

        <div className="mt-1 space-y-1">
          {loading ? (
            <div className="flex items-center justify-center py-8 gap-2 text-muted-foreground text-sm">
              <Loader2 className="h-4 w-4 animate-spin" /> Loading…
            </div>
          ) : history.length === 0 ? (
            <p className="text-sm text-muted-foreground text-center py-8">
              No login history recorded.
            </p>
          ) : (
            history.map((entry, i) => {
              const isFailed = entry.success === false;
              const parsed = parseUserAgent(entry.user_agent);
              const browser = entry.browser || parsed.browser;
              const os = entry.os || parsed.os;
              const deviceType = entry.device_type || (parsed.mobile ? 'mobile' : 'desktop');
              const DeviceIcon =
                deviceType === 'mobile' || deviceType === 'tablet' ? Smartphone : Monitor;
              const isFirstSuccess =
                !isFailed && history.slice(0, i).every((e) => e.success === false);
              return (
                <div
                  key={entry.id}
                  className={`flex items-start gap-3 rounded-lg border px-4 py-3 ${
                    isFailed
                      ? 'border-destructive/30 bg-destructive/5'
                      : 'border-border/50 bg-muted/20'
                  }`}
                >
                  <DeviceIcon
                    className={`h-4 w-4 mt-0.5 shrink-0 ${isFailed ? 'text-destructive/70' : 'text-muted-foreground'}`}
                  />
                  <div className="min-w-0">
                    <p className="text-sm font-medium flex items-center flex-wrap gap-1.5">
                      {formatDateTime(entry.logged_in_at)}
                      {isFailed && (
                        <span className="text-[10px] font-semibold uppercase tracking-wide text-destructive bg-destructive/10 px-1.5 py-0.5 rounded">
                          Failed attempt
                        </span>
                      )}
                      {entry.is_current_session && (
                        <span className="text-[10px] font-semibold uppercase tracking-wide text-emerald-500 bg-emerald-500/10 px-1.5 py-0.5 rounded">
                          This session
                        </span>
                      )}
                      {!entry.is_current_session && isFirstSuccess && (
                        <span className="text-[10px] font-semibold uppercase tracking-wide text-blue-500">
                          Most recent
                        </span>
                      )}
                    </p>
                    <p className="text-xs text-muted-foreground mt-0.5">
                      {deviceLabel(deviceType)} · {browser} on {os}
                      {entry.ip_address && (
                        <span className="ml-2 font-mono">{entry.ip_address}</span>
                      )}
                      {(entry.location_city || entry.location_country) && (
                        <span className="ml-2 text-muted-foreground/80">
                          —{' '}
                          {[entry.location_city, entry.location_region, entry.location_country]
                            .filter(Boolean)
                            .join(', ')}
                        </span>
                      )}
                    </p>
                    {entry.location_isp && (
                      <p className="text-[10px] text-muted-foreground/60 mt-0.5">
                        {entry.location_isp}
                      </p>
                    )}
                    {entry.device_fingerprint && (
                      <p className="text-[10px] text-muted-foreground/70 mt-1 font-mono">
                        Device ID {entry.device_fingerprint}
                      </p>
                    )}
                  </div>
                </div>
              );
            })
          )}
        </div>

        <div className="pt-2">
          <Button
            variant="outline"
            size="sm"
            className="w-full gap-1.5 text-destructive hover:bg-destructive/10 hover:text-destructive"
            onClick={() => setConfirmSignOut(true)}
          >
            <LogOut className="h-3.5 w-3.5" />
            Sign out of other devices
          </Button>
        </div>

        <p className="text-[10px] text-muted-foreground/60 text-center pt-1">
          Showing up to 10 most recent events including failed attempts. Device ID is a short
          privacy-preserving identifier.
        </p>
        <p className="text-[10px] text-muted-foreground/60 text-center">
          This information is shown only to you and is encrypted at rest. It is not shared with
          admins.
        </p>

        <AlertDialog open={confirmSignOut} onOpenChange={setConfirmSignOut}>
          <AlertDialogContent>
            <AlertDialogHeader>
              <AlertDialogTitle>Sign out of other devices?</AlertDialogTitle>
              <AlertDialogDescription>
                This ends every other active session — anyone signed in on another browser or device
                will be logged out. This device stays signed in.
              </AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <AlertDialogCancel disabled={signingOut}>Cancel</AlertDialogCancel>
              <AlertDialogAction
                onClick={(e) => {
                  e.preventDefault();
                  handleSignOutOthers();
                }}
                disabled={signingOut}
              >
                {signingOut ? 'Signing out…' : 'Sign out others'}
              </AlertDialogAction>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialog>
      </DialogContent>
    </Dialog>
  );
}

function LoginSummaryCard({
  latestLogin,
  loading,
  onOpen,
}: {
  latestLogin: LoginEntry | null;
  loading: boolean;
  onOpen: () => void;
}) {
  const parsed = parseUserAgent(latestLogin?.user_agent);
  const browser = latestLogin?.browser || parsed.browser;
  const os = latestLogin?.os || parsed.os;
  const deviceType = latestLogin?.device_type || (parsed.mobile ? 'mobile' : 'desktop');
  const DeviceIcon = deviceType === 'mobile' || deviceType === 'tablet' ? Smartphone : Monitor;

  return (
    <button
      type="button"
      onClick={onOpen}
      className="group rounded-lg border border-border/60 bg-muted/25 px-4 py-3 text-left transition-colors hover:border-primary/35 hover:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
    >
      <div className="flex items-start gap-3">
        <div className="mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-border/60 bg-background/60 text-muted-foreground group-hover:text-primary">
          {loading ? (
            <Loader2 className="h-4 w-4 animate-spin" />
          ) : (
            <DeviceIcon className="h-4 w-4" />
          )}
        </div>
        <div className="min-w-0 flex-1">
          <div className="flex items-center justify-between gap-3">
            <p className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
              Last Login
            </p>
            <ChevronRight className="h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5 group-hover:text-primary" />
          </div>
          {latestLogin ? (
            <>
              <p className="mt-1 truncate text-sm font-semibold text-foreground">
                {formatDateTime(latestLogin.logged_in_at)}
              </p>
              <p className="mt-0.5 truncate text-xs text-muted-foreground">
                {deviceLabel(deviceType)} · {browser} on {os}
              </p>
              {(latestLogin.location_city || latestLogin.location_country) && (
                <p className="mt-0.5 truncate text-xs text-muted-foreground/70">
                  {[
                    latestLogin.location_city,
                    latestLogin.location_region,
                    latestLogin.location_country,
                  ]
                    .filter(Boolean)
                    .join(', ')}
                </p>
              )}
              {latestLogin.ip_address && (
                <p className="mt-0.5 truncate font-mono text-[11px] text-muted-foreground">
                  {latestLogin.ip_address}
                </p>
              )}
            </>
          ) : (
            <>
              <p className="mt-1 text-sm font-semibold text-foreground">
                {loading ? 'Checking login history...' : 'No login recorded yet'}
              </p>
              <p className="mt-0.5 text-xs text-muted-foreground">
                Open history to view device and IP details.
              </p>
            </>
          )}
        </div>
      </div>
    </button>
  );
}

function ProfileSummary({ profile, loading }: { profile: Profile; loading: boolean }) {
  const [historyOpen, setHistoryOpen] = useState(false);
  const [loginHistory, setLoginHistory] = useState<LoginEntry[]>([]);
  const [historyLoading, setHistoryLoading] = useState(false);

  useEffect(() => {
    if (loading) return;
    setHistoryLoading(true);
    api
      .loginHistory()
      .then((raw) => setLoginHistory((raw as { history?: LoginEntry[] }).history ?? []))
      .catch((err) => {
        setLoginHistory([]);
        toast.error(errMessage(err, 'Failed to load login history.'));
      })
      .finally(() => setHistoryLoading(false));
  }, [loading]);

  if (loading) {
    return (
      <SectionCard title="Profile Summary" icon={User}>
        <div className="px-6 py-6 text-sm text-muted-foreground">Loading profile…</div>
      </SectionCard>
    );
  }

  // Show the most recent SUCCESSFUL login in the summary card (not a failed attempt)
  const latestLogin = loginHistory.find((l) => l.success !== false) || null;

  return (
    <>
      <SectionCard title="Profile Summary" icon={User} subtitle="Your signed-in account details.">
        <div className="px-6 py-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
          <FieldRow label="Username" value={profile.username} />
          <FieldRow label="Display Name" value={displayNameOf(profile)} />
          <FieldRow label="Role" value={profile.role} />

          <LoginSummaryCard
            latestLogin={latestLogin}
            loading={historyLoading}
            onOpen={() => setHistoryOpen(true)}
          />

          <FieldRow
            label="Password Changed"
            value={formatDateTime(profile.last_password_change_at || profile.password_changed_at)}
          />
        </div>
      </SectionCard>

      <LoginHistoryModal
        history={loginHistory}
        open={historyOpen}
        onClose={() => setHistoryOpen(false)}
        onLoaded={setLoginHistory}
      />
    </>
  );
}

function EditProfile({ profile, onSaved }: { profile: Profile; onSaved: (p: Profile) => void }) {
  const [displayName, setDisplayName] = useState(displayNameOf(profile));
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    setDisplayName(displayNameOf(profile));
  }, [profile]);

  const save = async () => {
    setSaving(true);
    try {
      const data = await api.updateProfile({ display_name: displayName.trim() || null });
      toast.success('Profile saved.');
      onSaved(asProfile(data));
    } catch (err) {
      toast.error(errMessage(err, 'Failed to save profile.'));
    } finally {
      setSaving(false);
    }
  };

  return (
    <SectionCard
      title="Edit Profile"
      icon={User}
      subtitle="Choose how your name appears inside the app."
    >
      <div className="px-6 py-5 flex flex-col gap-3 sm:flex-row sm:items-end">
        <div className="space-y-1.5 flex-1 max-w-md">
          <label htmlFor="display-name" className="text-xs font-medium text-muted-foreground">
            Display name
          </label>
          <Input
            id="display-name"
            value={displayName}
            onChange={(e) => setDisplayName(e.target.value)}
            placeholder="Display name"
          />
        </div>
        <Button onClick={save} disabled={saving} className="sm:mb-0">
          {saving ? (
            <>
              <Loader2 className="h-4 w-4 mr-2 animate-spin" />
              Saving…
            </>
          ) : (
            'Save Profile'
          )}
        </Button>
      </div>
    </SectionCard>
  );
}

// Exported: rendered on the Settings page ("Notifications" section). Lives here
// because it shares asSettings/CheckRow/SectionCard with the rest of this file.
function buildNotificationPayload(form: Settings) {
  const payload = {
    email: String(form.email || form.notification_email || ''),
    notifications_enabled: !!(form.notifications_enabled ?? form.enabled),
    notify_3_day: !!(form.notify_3_day ?? form.notify_3d),
    notify_1_day: !!(form.notify_1_day ?? form.notify_1d),
    notify_due: !!(form.notify_due ?? form.notify_day_of),
    notify_overdue: !!(form.notify_overdue ?? form.notify_daily_overdue),
    notify_amount_change: !!(form.notify_amount_change ?? true),
    enabled: false,
    notify_3d: false,
    notify_1d: false,
    notify_day_of: false,
    notify_daily_overdue: false,
  };
  payload.enabled = payload.notifications_enabled;
  payload.notify_3d = payload.notify_3_day;
  payload.notify_1d = payload.notify_1_day;
  payload.notify_day_of = payload.notify_due;
  payload.notify_daily_overdue = payload.notify_overdue;
  return payload;
}

export function NotificationPreferences({ settings }: { settings: Settings }) {
  const [form, setForm] = useState<Settings>(settings);

  // Auto-save: toggles persist almost instantly, the email field debounces so
  // we never save a half-typed address. Local form stays the source of truth —
  // no parent refresh that could clobber in-flight edits.
  const { status, schedule, flush } = useAutoSave((payload) =>
    api.updateProfileSettings(payload).catch((err) => {
      toast.error(errMessage(err, 'Failed to save notification preferences.'));
      throw err;
    }),
  );

  const set = (k: string, v: unknown, delay?: number) =>
    setForm((prev) => {
      const next = { ...prev, [k]: v };
      schedule(buildNotificationPayload(next), delay);
      return next;
    });

  const payload = buildNotificationPayload(form);

  return (
    <SectionCard
      title="Notification Preferences"
      icon={Mail}
      subtitle="Manage email reminders for your bills. Changes save automatically."
      action={<SaveStatus status={status} />}
    >
      <div className="px-6 py-5 space-y-4">
        <div className="space-y-1.5 max-w-md">
          <label htmlFor="profile-email" className="text-xs font-medium text-muted-foreground">
            Email
          </label>
          <Input
            id="profile-email"
            type="email"
            value={payload.email}
            onChange={(e) => set('email', e.target.value, 900)}
            onBlur={flush}
            placeholder="you@example.com"
          />
        </div>
        <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
          <CheckRow
            id="n-enabled"
            label="Notifications enabled"
            checked={payload.notifications_enabled}
            onChange={(v) => set('notifications_enabled', v)}
          />
          <CheckRow
            id="n-3"
            label="Notify 3 days before"
            checked={payload.notify_3_day}
            onChange={(v) => set('notify_3_day', v)}
            disabled={!payload.notifications_enabled}
          />
          <CheckRow
            id="n-1"
            label="Notify 1 day before"
            checked={payload.notify_1_day}
            onChange={(v) => set('notify_1_day', v)}
            disabled={!payload.notifications_enabled}
          />
          <CheckRow
            id="n-due"
            label="Notify due date"
            checked={payload.notify_due}
            onChange={(v) => set('notify_due', v)}
            disabled={!payload.notifications_enabled}
          />
          <CheckRow
            id="n-overdue"
            label="Notify overdue"
            checked={payload.notify_overdue}
            onChange={(v) => set('notify_overdue', v)}
            disabled={!payload.notifications_enabled}
          />
          <CheckRow
            id="n-amount"
            label="Notify on price changes"
            checked={payload.notify_amount_change}
            onChange={(v) => set('notify_amount_change', v)}
            disabled={!payload.notifications_enabled}
          />
        </div>
      </div>
    </SectionCard>
  );
}

interface PushChannel {
  value: string;
  label: string;
  urlLabel: string;
  urlHint: string;
  tokenLabel: string | null;
  chatIdLabel?: string;
}

const PUSH_CHANNELS: PushChannel[] = [
  {
    value: 'ntfy',
    label: 'ntfy',
    urlLabel: 'Topic URL',
    urlHint: 'https://pulse.scheller.ltd/bills',
    tokenLabel: 'Access token (optional)',
  },
  {
    value: 'gotify',
    label: 'Gotify',
    urlLabel: 'Server URL',
    urlHint: 'http://192.168.1.11:8077',
    tokenLabel: 'App token',
  },
  {
    value: 'discord',
    label: 'Discord',
    urlLabel: 'Webhook URL',
    urlHint: 'https://discord.com/api/webhooks/…',
    tokenLabel: null,
  },
  {
    value: 'telegram',
    label: 'Telegram',
    urlLabel: 'Server URL / n/a',
    urlHint: 'Leave blank for api.telegram.org',
    tokenLabel: 'Bot token',
    chatIdLabel: 'Chat ID',
  },
];

// Exported: rendered on the Settings page ("Notifications" section).
export function PushNotifications({
  settings,
}: {
  settings: Settings;
  onSaved?: (settings?: Settings) => void;
}) {
  const [enabled, setEnabled] = useState(!!settings.notify_push_enabled);
  const [channel, setChannel] = useState(String(settings.push_channel || 'ntfy'));
  const [url, setUrl] = useState(String(settings.push_url || ''));
  const [token, setToken] = useState(''); // never pre-filled for security
  const [chatId, setChatId] = useState(String(settings.push_chat_id || ''));
  const [tokenSet, setTokenSet] = useState(!!settings.push_token_set);
  const [saving, setSaving] = useState(false);
  const [testing, setTesting] = useState(false);

  const ch = PUSH_CHANNELS.find((c) => c.value === channel) || PUSH_CHANNELS[0]!;

  // Auto-save. Toggle/channel persist immediately; URL and chat ID debounce.
  // The token is deliberately NOT auto-saved while typing — a half-typed token
  // must never overwrite a working one. It saves on blur, when complete.
  const buildPatch = (
    over: Partial<{ enabled: boolean; channel: string; url: string; chatId: string }> = {},
  ) => {
    const s = { enabled, channel, url, chatId, ...over };
    return {
      notify_push_enabled: s.enabled,
      push_channel: s.channel,
      push_url: (s.url || '').trim() || null,
      push_chat_id: (s.chatId || '').trim() || null,
    };
  };

  const { status, schedule, flush } = useAutoSave((patch) =>
    api.updateProfileSettings(patch).catch((err) => {
      toast.error(errMessage(err, 'Failed to save push settings.'));
      throw err;
    }),
  );

  const saveToken = async () => {
    const t = token.trim();
    if (!t) return;
    setSaving(true);
    try {
      await api.updateProfileSettings({ ...buildPatch(), push_token: t });
      setTokenSet(true);
      setToken('');
      toast.success('Token saved.');
    } catch (err) {
      toast.error(errMessage(err, 'Failed to save token.'));
    } finally {
      setSaving(false);
    }
  };

  const test = async () => {
    setTesting(true);
    try {
      await api.testPushNotification();
      toast.success('Test notification sent — check your device.');
    } catch (err) {
      toast.error(errMessage(err, 'Test failed. Check your channel settings.'));
    } finally {
      setTesting(false);
    }
  };

  return (
    <SectionCard
      title="Push Notifications"
      icon={Bell}
      subtitle="Get bill reminders on your phone via ntfy, Gotify, Discord, or Telegram. Changes save automatically."
      action={<SaveStatus status={status} />}
    >
      <div className="px-6 py-5 space-y-5">
        {/* Master toggle — same CheckRow pattern as the email section */}
        <CheckRow
          id="push-enabled"
          label="Enable push notifications"
          checked={enabled}
          onChange={(v) => {
            setEnabled(v);
            schedule(buildPatch({ enabled: v }), 150);
          }}
        />
        {enabled && (
          <p className="text-xs text-muted-foreground -mt-3 pl-1">
            Sent at 6 AM alongside email reminders. Use the test button below to verify immediately.
          </p>
        )}

        {enabled && (
          <>
            {/* Channel picker */}
            <div className="space-y-1.5">
              <label className="text-xs font-medium text-muted-foreground">Channel</label>
              <div className="flex flex-wrap gap-2">
                {PUSH_CHANNELS.map((c) => (
                  <button
                    key={c.value}
                    type="button"
                    onClick={() => {
                      setChannel(c.value);
                      schedule(buildPatch({ channel: c.value }), 150);
                    }}
                    className={`rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors ${
                      channel === c.value
                        ? 'border-primary bg-primary text-primary-foreground'
                        : 'border-border bg-background hover:bg-muted text-foreground'
                    }`}
                  >
                    {c.label}
                  </button>
                ))}
              </div>
            </div>

            {/* Channel-specific inputs */}
            <div className="space-y-3 max-w-md">
              <div className="space-y-1.5">
                <label className="text-xs font-medium text-muted-foreground">{ch.urlLabel}</label>
                <Input
                  value={url}
                  onChange={(e) => {
                    setUrl(e.target.value);
                    schedule(buildPatch({ url: e.target.value }), 900);
                  }}
                  onBlur={flush}
                  placeholder={ch.urlHint}
                  autoComplete="off"
                />
              </div>

              {ch.tokenLabel && (
                <div className="space-y-1.5">
                  <label className="text-xs font-medium text-muted-foreground">
                    {ch.tokenLabel}
                    {tokenSet && !token && (
                      <span className="ml-2 text-emerald-600 dark:text-emerald-400">✓ saved</span>
                    )}
                  </label>
                  <Input
                    value={token}
                    onChange={(e) => setToken(e.target.value)}
                    onBlur={saveToken}
                    placeholder={
                      tokenSet
                        ? '(leave blank to keep saved token)'
                        : 'Enter token — saves when you click away'
                    }
                    type="password"
                    autoComplete="off"
                  />
                </div>
              )}

              {ch.chatIdLabel && (
                <div className="space-y-1.5">
                  <label className="text-xs font-medium text-muted-foreground">
                    {ch.chatIdLabel}
                  </label>
                  <Input
                    value={chatId}
                    onChange={(e) => {
                      setChatId(e.target.value);
                      schedule(buildPatch({ chatId: e.target.value }), 900);
                    }}
                    onBlur={flush}
                    placeholder="e.g. 123456789"
                    autoComplete="off"
                  />
                  <p className="text-[11px] text-muted-foreground">
                    Send /start to your bot, then visit{' '}
                    <code className="rounded bg-muted px-1 py-0.5">
                      api.telegram.org/bot{'<token>'}/getUpdates
                    </code>{' '}
                    to find your chat ID.
                  </p>
                </div>
              )}
            </div>

            {/* Channel hints */}
            {channel === 'ntfy' && (
              <p className="text-[11px] text-muted-foreground">
                Your ntfy server is running at{' '}
                <code className="rounded bg-muted px-1 py-0.5">pulse.scheller.ltd</code>. Set the
                topic URL to{' '}
                <code className="rounded bg-muted px-1 py-0.5">
                  https://pulse.scheller.ltd/your-topic
                </code>
                .
              </p>
            )}
            {channel === 'gotify' && (
              <p className="text-[11px] text-muted-foreground">
                Your Gotify server is at{' '}
                <code className="rounded bg-muted px-1 py-0.5">notify.originalsinners.org</code>.
                Create an app in Gotify and paste the app token above.
              </p>
            )}
          </>
        )}
      </div>

      <div className="px-6 py-4 border-t border-border/50 flex items-center gap-3">
        <Button
          type="button"
          variant="outline"
          size="sm"
          onClick={test}
          disabled={testing || saving || !enabled || !url.trim()}
          className="gap-1.5"
        >
          {testing ? (
            <>
              <Loader2 className="h-3.5 w-3.5 animate-spin" />
              Sending…
            </>
          ) : (
            <>
              <SendHorizontal className="h-3.5 w-3.5" />
              Send test
            </>
          )}
        </Button>
        <p className="text-[11px] text-muted-foreground">Settings save as you change them.</p>
      </div>
    </SectionCard>
  );
}

function ChangePassword() {
  const [currentPassword, setCurrentPassword] = useState('');
  const [newPassword, setNewPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [saving, setSaving] = useState(false);

  const reset = () => {
    setCurrentPassword('');
    setNewPassword('');
    setConfirmPassword('');
  };

  const submit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!currentPassword || !newPassword || !confirmPassword) {
      toast.error('All password fields are required.');
      return;
    }
    if (newPassword !== confirmPassword) {
      toast.error('New passwords do not match.');
      return;
    }
    setSaving(true);
    try {
      await api.changeProfilePassword({
        current_password: currentPassword,
        new_password: newPassword,
        confirm_new_password: confirmPassword,
      });
      reset();
      toast.success('Password changed.');
    } catch (err) {
      toast.error(errMessage(err, 'Failed to change password.'));
    } finally {
      setSaving(false);
    }
  };

  return (
    <>
      <TotpSection />
      <PasskeySection />
      <SectionCard
        title="Change Password"
        icon={KeyRound}
        subtitle="Update your password without exposing it in logs or page state beyond this form."
      >
        <form onSubmit={submit} className="px-6 py-5 grid gap-4 lg:grid-cols-3">
          <div className="space-y-1.5">
            <label htmlFor="current-password" className="text-xs font-medium text-muted-foreground">
              Current password
            </label>
            <Input
              id="current-password"
              type="password"
              autoComplete="current-password"
              value={currentPassword}
              onChange={(e) => setCurrentPassword(e.target.value)}
            />
          </div>
          <div className="space-y-1.5">
            <label htmlFor="new-password" className="text-xs font-medium text-muted-foreground">
              New password
            </label>
            <Input
              id="new-password"
              type="password"
              autoComplete="new-password"
              value={newPassword}
              onChange={(e) => setNewPassword(e.target.value)}
            />
          </div>
          <div className="space-y-1.5">
            <label htmlFor="confirm-password" className="text-xs font-medium text-muted-foreground">
              Confirm new password
            </label>
            <Input
              id="confirm-password"
              type="password"
              autoComplete="new-password"
              value={confirmPassword}
              onChange={(e) => setConfirmPassword(e.target.value)}
            />
          </div>
          <Button type="submit" disabled={saving} className="lg:col-start-3">
            {saving ? (
              <>
                <Loader2 className="h-4 w-4 mr-2 animate-spin" />
                Saving…
              </>
            ) : (
              'Change Password'
            )}
          </Button>
        </form>
      </SectionCard>
    </>
  );
}

function CopyButton({ text }: { text: string }) {
  const [copied, setCopied] = useState(false);
  const copy = () => {
    navigator.clipboard.writeText(text).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  return (
    <button
      type="button"
      onClick={copy}
      className="ml-1.5 inline-flex items-center text-muted-foreground hover:text-foreground transition-colors"
    >
      {copied ? (
        <Check className="h-3.5 w-3.5 text-emerald-500" />
      ) : (
        <Copy className="h-3.5 w-3.5" />
      )}
    </button>
  );
}

function TotpSection() {
  const { singleUserMode } = useAuth();
  const [enabled, setEnabled] = useState<boolean | null>(null); // null = loading
  const [step, setStep] = useState<'idle' | 'setup' | 'confirm' | 'recovery' | 'disable'>('idle');
  const [setupData, setSetupData] = useState<{ secret?: string; qr_data_url?: string } | null>(
    null,
  );
  const [code, setCode] = useState('');
  const [recoveryCodes, setRecoveryCodes] = useState<string[]>([]);
  const [saving, setSaving] = useState(false);

  const load = useCallback(() => {
    if (singleUserMode) return;
    api
      .totpStatus()
      .then((raw) => setEnabled(!!(raw as { enabled?: boolean }).enabled))
      .catch(() => setEnabled(false));
  }, [singleUserMode]);

  useEffect(() => {
    load();
  }, [load]);

  if (singleUserMode) return null;
  if (enabled === null) return null;

  const startSetup = async () => {
    setSaving(true);
    try {
      const d = (await api.totpSetup()) as { secret?: string; qr_data_url?: string };
      setSetupData(d);
      setCode('');
      setStep('setup');
    } catch (err) {
      toast.error(errMessage(err, 'Failed to generate setup data.'));
    } finally {
      setSaving(false);
    }
  };

  const confirmEnable = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!setupData) return;
    setSaving(true);
    try {
      const d = (await api.totpEnable({ secret: setupData.secret, code })) as {
        recovery_codes?: string[];
      };
      setRecoveryCodes(d.recovery_codes ?? []);
      setEnabled(true);
      setStep('recovery');
    } catch (err) {
      toast.error(errMessage(err, 'Invalid code. Try again.'));
    } finally {
      setSaving(false);
    }
  };

  const confirmDisable = async (e: React.FormEvent) => {
    e.preventDefault();
    setSaving(true);
    try {
      await api.totpDisable({ code });
      setEnabled(false);
      setStep('idle');
      setCode('');
      toast.success('Authenticator app removed.');
    } catch (err) {
      toast.error(errMessage(err, 'Invalid code.'));
    } finally {
      setSaving(false);
    }
  };

  return (
    <SectionCard
      title="Two-Factor Authentication"
      icon={ScanLine}
      subtitle="Require an authenticator app code on every sign-in."
    >
      <div className="px-6 py-5 space-y-5">
        {/* Idle — enabled status */}
        {step === 'idle' && (
          <div className="flex items-center justify-between gap-4">
            <div className="flex items-center gap-3">
              <div
                className={`h-2.5 w-2.5 rounded-full ${enabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'}`}
              />
              <span className="text-sm">
                {enabled ? 'Authenticator app is active' : 'Not configured'}
              </span>
            </div>
            {enabled ? (
              <Button
                variant="outline"
                size="sm"
                onClick={() => {
                  setCode('');
                  setStep('disable');
                }}
              >
                Remove
              </Button>
            ) : (
              <Button size="sm" onClick={startSetup} disabled={saving}>
                {saving ? 'Loading…' : 'Set up'}
              </Button>
            )}
          </div>
        )}

        {/* Setup — show QR code */}
        {step === 'setup' && setupData && (
          <div className="space-y-5">
            <p className="text-sm text-muted-foreground">
              Scan the QR code with Google Authenticator, Authy, 1Password, Bitwarden, or any TOTP
              app. Then enter the 6-digit code to confirm.
            </p>
            <div className="flex flex-col sm:flex-row gap-6 items-start">
              <img
                src={setupData.qr_data_url}
                alt="TOTP QR code"
                className="rounded-lg border border-border/60 w-40 h-40 shrink-0"
              />
              <div className="space-y-3 min-w-0">
                <p className="text-xs text-muted-foreground">
                  Can't scan? Enter this key manually:
                </p>
                <div className="flex items-center gap-1 font-mono text-sm bg-muted/30 border border-border/60 rounded px-3 py-2 break-all">
                  {setupData.secret}
                  <CopyButton text={setupData.secret || ''} />
                </div>
                <form onSubmit={confirmEnable} className="space-y-3 pt-1">
                  <Input
                    value={code}
                    onChange={(e) => setCode(e.target.value)}
                    placeholder="000 000"
                    autoComplete="one-time-code"
                    maxLength={7}
                    className="text-center tracking-widest font-mono text-lg max-w-[140px]"
                    autoFocus
                    required
                  />
                  <div className="flex gap-2">
                    <Button type="submit" size="sm" disabled={saving || !code.trim()}>
                      {saving ? 'Verifying…' : 'Confirm & Enable'}
                    </Button>
                    <Button
                      type="button"
                      variant="ghost"
                      size="sm"
                      onClick={() => {
                        setStep('idle');
                        setSetupData(null);
                      }}
                    >
                      Cancel
                    </Button>
                  </div>
                </form>
              </div>
            </div>
          </div>
        )}

        {/* Recovery codes — shown once after enabling */}
        {step === 'recovery' && (
          <div className="space-y-4">
            <div className="flex items-start gap-3 rounded-lg border border-amber-500/30 bg-amber-500/10 px-4 py-3">
              <TriangleAlert className="h-4 w-4 text-amber-500 shrink-0 mt-0.5" />
              <p className="text-sm text-amber-600 dark:text-amber-400">
                Save these recovery codes somewhere safe. Each code works once. If you lose your
                phone, use one of these to sign in.
              </p>
            </div>
            <div className="grid grid-cols-2 sm:grid-cols-4 gap-2">
              {recoveryCodes.map((c) => (
                <div
                  key={c}
                  className="flex items-center justify-between gap-1 font-mono text-xs bg-muted/30 border border-border/60 rounded px-2.5 py-1.5"
                >
                  {c} <CopyButton text={c} />
                </div>
              ))}
            </div>
            <Button
              size="sm"
              onClick={() => {
                setStep('idle');
                setRecoveryCodes([]);
              }}
            >
              Done — I've saved these
            </Button>
          </div>
        )}

        {/* Disable — requires TOTP code */}
        {step === 'disable' && (
          <div className="space-y-4">
            <p className="text-sm text-muted-foreground">
              Enter the current code from your authenticator app to remove 2FA.
            </p>
            <form onSubmit={confirmDisable} className="flex items-end gap-3">
              <div className="space-y-1.5">
                <label className="text-xs font-medium text-muted-foreground">
                  Authenticator code
                </label>
                <Input
                  value={code}
                  onChange={(e) => setCode(e.target.value)}
                  placeholder="000 000"
                  autoComplete="one-time-code"
                  maxLength={7}
                  className="text-center tracking-widest font-mono text-lg max-w-[140px]"
                  autoFocus
                  required
                />
              </div>
              <Button
                type="submit"
                variant="destructive"
                size="sm"
                disabled={saving || !code.trim()}
              >
                {saving ? 'Removing…' : 'Remove 2FA'}
              </Button>
              <Button
                type="button"
                variant="ghost"
                size="sm"
                onClick={() => {
                  setStep('idle');
                  setCode('');
                }}
              >
                Cancel
              </Button>
            </form>
          </div>
        )}
      </div>
    </SectionCard>
  );
}

interface PasskeyCredential {
  id: number;
  credential_id: string;
  credential_name: string;
  created_at?: string;
}

function PasskeySection() {
  const { singleUserMode } = useAuth();
  const [enabled, setEnabled] = useState<boolean | null>(null); // null = loading
  const [credentials, setCredentials] = useState<PasskeyCredential[]>([]);
  const [keyName, setKeyName] = useState('');
  const [naming, setNaming] = useState(false); // show the name-your-key form
  const [password, setPassword] = useState('');
  const [removeTarget, setRemoveTarget] = useState<'all' | string | null>(null); // credential_id or 'all'
  const [saving, setSaving] = useState(false);

  const load = useCallback(() => {
    if (singleUserMode) return;
    api
      .webauthnStatus()
      .then((raw) => setEnabled(!!(raw as { enabled?: boolean }).enabled))
      .catch(() => setEnabled(false));
    api
      .webauthnCredentials()
      .then((raw) =>
        setCredentials((raw as { credentials?: PasskeyCredential[] }).credentials ?? []),
      )
      .catch(() => setCredentials([]));
  }, [singleUserMode]);

  useEffect(() => {
    load();
  }, [load]);

  if (singleUserMode) return null;
  if (enabled === null) return null;

  const enroll = async (e: React.FormEvent) => {
    e.preventDefault();
    setSaving(true);
    try {
      const { startRegistration } = await import('@simplewebauthn/browser');
      const setup = (await api.webauthnSetup()) as { options?: unknown; challengeId?: string };
      const response = await startRegistration({
        optionsJSON: setup.options as Parameters<typeof startRegistration>[0]['optionsJSON'],
      });
      await api.webauthnEnable({
        challengeId: setup.challengeId,
        response,
        credential_name: keyName.trim() || 'Security Key',
      });
      toast.success('Security key added.');
      setNaming(false);
      setKeyName('');
      load();
    } catch (err) {
      const cancelled = (err as Error)?.name === 'NotAllowedError';
      toast.error(
        cancelled
          ? 'Security key prompt was cancelled.'
          : errMessage(err, 'Failed to add security key.'),
      );
    } finally {
      setSaving(false);
    }
  };

  const confirmRemove = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!removeTarget) return;
    setSaving(true);
    try {
      if (removeTarget === 'all') {
        await api.webauthnDisable({ password });
        toast.success('Security keys removed.');
      } else {
        await api.webauthnDeleteCred(removeTarget, { password });
        toast.success('Security key removed.');
      }
      setRemoveTarget(null);
      setPassword('');
      load();
    } catch (err) {
      toast.error(errMessage(err, 'Failed to remove security key.'));
    } finally {
      setSaving(false);
    }
  };

  return (
    <SectionCard
      title="Passkeys & Security Keys"
      icon={KeyRound}
      subtitle="Phishing-resistant sign-in with a passkey, fingerprint, or hardware key. If an authenticator app is also enabled, the app code is used at sign-in."
    >
      <div className="px-6 py-5 space-y-5">
        <div className="flex items-center justify-between gap-4">
          <div className="flex items-center gap-3">
            <div
              className={`h-2.5 w-2.5 rounded-full ${enabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'}`}
            />
            <span className="text-sm">
              {enabled
                ? `${credentials.length} security ${credentials.length === 1 ? 'key' : 'keys'} registered`
                : 'Not configured'}
            </span>
          </div>
          <div className="flex gap-2">
            {enabled && credentials.length > 0 && (
              <Button
                variant="outline"
                size="sm"
                onClick={() => {
                  setPassword('');
                  setRemoveTarget('all');
                }}
              >
                Remove all
              </Button>
            )}
            <Button
              size="sm"
              onClick={() => {
                setKeyName('');
                setNaming(true);
              }}
              disabled={saving}
            >
              Add key
            </Button>
          </div>
        </div>

        {credentials.length > 0 && (
          <ul className="divide-y divide-border rounded-md border border-border">
            {credentials.map((cred) => (
              <li
                key={cred.credential_id}
                className="flex items-center justify-between px-4 py-2.5"
              >
                <div>
                  <div className="text-sm">{cred.credential_name || 'Security Key'}</div>
                  {cred.created_at && (
                    <div className="text-xs text-muted-foreground">
                      Added {new Date(cred.created_at).toLocaleDateString()}
                    </div>
                  )}
                </div>
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={() => {
                    setPassword('');
                    setRemoveTarget(cred.credential_id);
                  }}
                >
                  Remove
                </Button>
              </li>
            ))}
          </ul>
        )}

        {naming && (
          <form onSubmit={enroll} className="flex flex-wrap items-end gap-3">
            <div className="space-y-1.5">
              <label htmlFor="passkey-name" className="text-xs font-medium text-muted-foreground">
                Key name
              </label>
              <Input
                id="passkey-name"
                value={keyName}
                onChange={(e) => setKeyName(e.target.value)}
                placeholder="e.g. YubiKey, This laptop"
                maxLength={60}
                autoFocus
              />
            </div>
            <Button type="submit" size="sm" disabled={saving}>
              {saving ? 'Waiting for your key…' : 'Register key'}
            </Button>
            <Button type="button" variant="ghost" size="sm" onClick={() => setNaming(false)}>
              Cancel
            </Button>
          </form>
        )}

        {removeTarget !== null && (
          <form onSubmit={confirmRemove} className="flex flex-wrap items-end gap-3">
            <div className="space-y-1.5">
              <label
                htmlFor="passkey-remove-password"
                className="text-xs font-medium text-muted-foreground"
              >
                Confirm with your password to{' '}
                {removeTarget === 'all' ? 'remove all keys' : 'remove this key'}
              </label>
              <Input
                id="passkey-remove-password"
                type="password"
                autoComplete="current-password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                required
                autoFocus
              />
            </div>
            <Button type="submit" variant="destructive" size="sm" disabled={saving || !password}>
              {saving ? 'Removing…' : 'Remove'}
            </Button>
            <Button
              type="button"
              variant="ghost"
              size="sm"
              onClick={() => {
                setRemoveTarget(null);
                setPassword('');
              }}
            >
              Cancel
            </Button>
          </form>
        )}
      </div>
    </SectionCard>
  );
}

function PrivacySettings({
  settings,
  onSaved,
}: {
  settings: Settings;
  onSaved: (settings: Settings) => void;
}) {
  const [geoEnabled, setGeoEnabled] = useState(!!settings.geolocation_enabled);
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    setGeoEnabled(!!settings.geolocation_enabled);
  }, [settings.geolocation_enabled]);

  const save = async () => {
    setSaving(true);
    try {
      await api.updateProfileSettings({ geolocation_enabled: geoEnabled });
      toast.success('Privacy settings saved.');
      onSaved({ ...settings, geolocation_enabled: geoEnabled });
    } catch (err) {
      toast.error(errMessage(err, 'Failed to save privacy settings.'));
    } finally {
      setSaving(false);
    }
  };

  const changed = geoEnabled !== !!settings.geolocation_enabled;

  return (
    <SectionCard
      title="Privacy"
      icon={Lock}
      subtitle="Control what login data is collected about you."
    >
      <div className="px-6 py-5 space-y-4">
        <CheckRow
          id="geo-enabled"
          label="Login geolocation"
          checked={geoEnabled}
          onChange={setGeoEnabled}
        />
        <p className="text-xs text-muted-foreground">
          When on, your login IP is resolved to a city/region via{' '}
          <span className="font-mono text-[11px]">ip-api.com</span> over plain HTTP. Location data
          is encrypted at rest and visible only to you. Turn off to keep all login data on-device.
        </p>
      </div>
      <div className="px-6 py-4 border-t border-border/50 flex justify-end">
        <Button onClick={save} disabled={saving || !changed}>
          {saving ? (
            <>
              <Loader2 className="h-4 w-4 mr-2 animate-spin" />
              Saving…
            </>
          ) : (
            'Save Privacy Settings'
          )}
        </Button>
      </div>
    </SectionCard>
  );
}

function ProfileNav() {
  const items: [string, string][] = [
    ['#account', 'Account'],
    ['#security', 'Security'],
    ['#privacy', 'Privacy'],
  ];
  return (
    <div className="mb-6 flex flex-wrap gap-2">
      {items.map(([href, label]) => (
        <a
          key={href}
          href={href}
          className="rounded-md border border-border bg-card px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
        >
          {label}
        </a>
      ))}
    </div>
  );
}

export default function ProfilePage() {
  const { setUser, refresh } = useAuth();
  const [profile, setProfile] = useState<Profile>({});
  const [settings, setSettings] = useState<Settings>({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let mounted = true;
    Promise.all([api.profile(), api.profileSettings()])
      .then(([profileData, settingsData]) => {
        if (!mounted) return;
        setProfile(asProfile(profileData));
        setSettings(asSettings(settingsData));
      })
      .catch((err) => toast.error(errMessage(err, 'Failed to load profile.')))
      .finally(() => {
        if (mounted) setLoading(false);
      });

    return () => {
      mounted = false;
    };
  }, []);

  const handleProfileSaved = (nextProfile: Profile) => {
    setProfile((prev) => ({ ...prev, ...nextProfile }));
    setUser((prev) => (prev ? ({ ...prev, ...nextProfile } as AuthUser) : prev));
    refresh();
  };

  return (
    <div className="mx-auto w-full max-w-5xl">
      <div className="mb-6 flex items-start justify-between gap-4">
        <div>
          <h1 className="text-2xl font-bold tracking-tight">Profile</h1>
          <p className="text-sm text-muted-foreground mt-0.5">
            Manage your account, security, and privacy. Notification preferences live in Settings.
          </p>
        </div>
        <div className="hidden sm:flex items-center gap-2 rounded-full border border-border bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground">
          <ShieldCheck className="h-3.5 w-3.5 text-emerald-500" />
          User-owned data only
        </div>
      </div>

      <ProfileNav />

      <div className="space-y-5">
        <div id="account" className="scroll-mt-6 space-y-6">
          <ProfileSummary profile={profile} loading={loading} />
          {!loading && <EditProfile profile={profile} onSaved={handleProfileSaved} />}
        </div>
        <div id="security" className="scroll-mt-6">
          <ChangePassword />
        </div>
        {/* Notification preferences moved to Settings → Notifications */}
        <div id="privacy" className="scroll-mt-6">
          {!loading && <PrivacySettings settings={settings} onSaved={setSettings} />}
        </div>
      </div>
    </div>
  );
}
