import React, { useState, useEffect, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from 'sonner';
import {
  ChevronLeft, ChevronRight, Database, Download, Eye, EyeOff,
  Play, RefreshCw, RotateCcw, Trash2, Upload, Wrench,
} from 'lucide-react';
import { api } from '@/api';
import { cn, fmtBytes } from '@/lib/utils';
import { Button, buttonVariants } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import {
  Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
} from '@/components/ui/select';
import {
  AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle,
  AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction,
} from '@/components/ui/alert-dialog';
import AppNavigation from '@/components/layout/Sidebar';

// ─── Helpers ──────────────────────────────────────────────────────────────────

const AUTHENTIK_ICON_URL = '/img/auth.png';

function SectionHeading({ children }) {
  return <h2 className="text-base font-semibold text-foreground">{children}</h2>;
}

function FieldRow({ label, children }) {
  return (
    <div className="grid gap-2 lg:grid-cols-[200px_1fr] lg:items-center lg:gap-4">
      <Label className="text-muted-foreground lg:text-right">{label}</Label>
      {children}
    </div>
  );
}

function Toggle({ checked, onChange, label, disabled = false }) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={checked}
      disabled={disabled}
      onClick={() => !disabled && onChange(!checked)}
      className={`relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'} ${checked ? 'bg-primary' : 'bg-input'}`}
    >
      <span className={`pointer-events-none inline-block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${checked ? 'translate-x-4' : 'translate-x-0'}`} />
      {label && <span className="sr-only">{label}</span>}
    </button>
  );
}

function defaultOidcRedirectUri() {
  if (typeof window === 'undefined') return '';
  return `${window.location.origin}/api/auth/oidc/callback`;
}

function looksLikeOidcEndpoint(url) {
  const value = String(url || '').toLowerCase();
  return /\/(?:authorize|token|userinfo|jwks|certs)\/?$/.test(value);
}

function formatDateTime(value) {
  if (!value) return '—';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return date.toLocaleString();
}

function BackupTypeBadge({ type }) {
  const cls = {
    manual: 'bg-blue-500/15 text-blue-400 border-blue-500/20',
    scheduled: 'bg-emerald-500/15 text-emerald-400 border-emerald-500/20',
    imported: 'bg-violet-500/15 text-violet-400 border-violet-500/20',
    'pre-restore': 'bg-amber-500/15 text-amber-400 border-amber-500/20',
  }[type] || 'bg-muted text-muted-foreground border-border';

  return <Badge className={cls}>{type || 'backup'}</Badge>;
}

// ─── Onboarding Wizard ────────────────────────────────────────────────────────

function OnboardingWizard({ onComplete }) {
  const [step, setStep]         = useState(0);
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [confirm, setConfirm]   = useState('');
  const [loading, setLoading]   = useState(false);
  const [error, setError]       = useState('');

  const handleCreate = async (e) => {
    e.preventDefault();
    setError('');
    
    let validationError = '';
    if (password !== confirm) {
      validationError = 'Passwords do not match.';
    } else if (password.length < 8) {
      validationError = 'Password must be at least 8 characters.';
    }
    
    if (validationError) {
      setError(validationError);
      toast.error(validationError);
      return;
    }
    
    setLoading(true);
    try {
      await api.createUser({ username, password });
      toast.success('User created successfully.');
      onComplete();
    } catch (err) {
      const errorMessage = err.message || 'Failed to create user.';
      setError(errorMessage);
      toast.error(errorMessage);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="flex flex-col items-center justify-center min-h-[calc(100vh-64px)] px-4 py-12">
      <div className="w-full max-w-md">
        {/* Step dots */}
        <div className="flex justify-center gap-2 mb-8">
          {[0, 1].map(i => (
            <span
              key={i}
              className={`h-2 rounded-full transition-all ${i === step ? 'w-6 bg-primary' : 'w-2 bg-border'}`}
            />
          ))}
        </div>

        {step === 0 && (
          <Card>
            <CardHeader>
              <CardTitle className="text-xl">Welcome, Administrator</CardTitle>
              <p className="text-sm text-muted-foreground mt-1">
                Before creating your first user, please understand what your admin account can and cannot do.
              </p>
            </CardHeader>
            <CardContent className="space-y-3">
              <div className="space-y-2.5">
                {[
                  { can: true,  text: 'Create and manage user accounts' },
                  { can: true,  text: 'Reset user passwords' },
                  { can: true,  text: 'Configure email notifications' },
                  { can: true,  text: 'Toggle single-user / multi-user mode' },
                  { can: false, text: 'Cannot view bills or financial data' },
                  { can: false, text: 'Cannot access user settings or history' },
                ].map(({ can, text }) => (
                  <div key={text} className="flex items-center gap-3 text-sm">
                    <span className={`shrink-0 w-5 h-5 rounded-full flex items-center justify-center text-xs font-bold ${can ? 'bg-emerald-500/15 text-emerald-400' : 'bg-red-500/15 text-red-400'}`}>
                      {can ? '✓' : '✗'}
                    </span>
                    <span className={can ? 'text-foreground' : 'text-muted-foreground'}>{text}</span>
                  </div>
                ))}
              </div>
              <div className="pt-4">
                <Button className="w-full" onClick={() => setStep(1)}>
                  Got it, create my user account
                  <ChevronRight className="h-4 w-4" />
                </Button>
              </div>
            </CardContent>
          </Card>
        )}

        {step === 1 && (
          <Card>
            <CardHeader>
              <CardTitle className="text-xl">Create first user</CardTitle>
              <p className="text-sm text-muted-foreground mt-1">
                This account will be used to access the bill tracker.
              </p>
            </CardHeader>
            <CardContent>
              <form onSubmit={handleCreate} className="space-y-4">
                <div className="space-y-1.5">
                  <Label htmlFor="ob-username">Username</Label>
                  <Input
                    id="ob-username"
                    placeholder="username"
                    value={username}
                    onChange={e => setUsername(e.target.value)}
                    required
                  />
                </div>
                <div className="space-y-1.5">
                  <Label htmlFor="ob-password">Password</Label>
                  <Input
                    id="ob-password"
                    type="password"
                    placeholder="Password"
                    value={password}
                    onChange={e => setPassword(e.target.value)}
                    required
                  />
                </div>
                <div className="space-y-1.5">
                  <Label htmlFor="ob-confirm">Confirm password</Label>
                  <Input
                    id="ob-confirm"
                    type="password"
                    placeholder="Confirm password"
                    value={confirm}
                    onChange={e => setConfirm(e.target.value)}
                    required
                  />
                </div>
                {error && (
                  <div className="rounded-lg border border-destructive/25 bg-destructive/10 px-3 py-2 text-sm text-destructive">
                    {error}
                  </div>
                )}
                <div className="flex gap-2 pt-2">
                  <Button type="button" variant="outline" onClick={() => setStep(0)}>
                    <ChevronLeft className="h-4 w-4" />
                    Back
                  </Button>
                  <Button type="submit" className="flex-1" disabled={loading} aria-busy={loading}>
                    {loading ? 'Creating…' : 'Create User'}
                  </Button>
                </div>
              </form>
            </CardContent>
          </Card>
        )}
      </div>
    </div>
  );
}

// ─── Email Notifications Card ─────────────────────────────────────────────────

function EmailNotifCard() {
  const DEFAULTS = {
    enabled: false,
    sender_name: '', sender_address: '',
    smtp_host: '', smtp_port: '587', smtp_encryption: 'starttls',
    smtp_self_signed: false,
    smtp_username: '', smtp_password: '',
    allow_user_config: false,
    global_recipient: '',
  };

  const [cfg, setCfg]             = useState(DEFAULTS);
  const [loading, setLoading]     = useState(true);
  const [saving, setSaving]       = useState(false);
  const [showPw, setShowPw]       = useState(false);
  const [testEmail, setTestEmail] = useState('');
  const [testing, setTesting]     = useState(false);

  useEffect(() => {
    api.notifAdmin()
      .then(d => setCfg({ ...DEFAULTS, ...d }))
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  const set = (k, v) => setCfg(p => ({ ...p, [k]: v }));

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.saveNotifAdmin(cfg);
      toast.success('Email settings saved.');
    } catch (err) {
      toast.error(err.message || 'Failed to save.');
    } finally {
      setSaving(false);
    }
  };

  const handleTest = async () => {
    if (!testEmail) { toast.error('Enter a recipient email.'); return; }
    setTesting(true);
    try {
      await api.testEmail({ to: testEmail });
      toast.success('Test email sent.');
    } catch (err) {
      toast.error(err.message || 'Failed to send test email.');
    } finally {
      setTesting(false);
    }
  };

  if (loading) return <Card><CardContent className="py-8 text-center text-muted-foreground text-sm">Loading…</CardContent></Card>;

  return (
    <Card>
      <CardHeader className="pb-4">
        <CardTitle>Email Notifications</CardTitle>
      </CardHeader>
      <CardContent className="space-y-5">
        {/* Enable toggle */}
        <div className="flex items-center justify-between">
          <div>
            <p className="text-sm font-medium">Enable email notifications</p>
            <p className="text-xs text-muted-foreground">Configure SMTP to send bill reminders</p>
          </div>
          <Toggle checked={cfg.enabled} onChange={v => set('enabled', v)} label="Enable email notifications" />
        </div>

        <div className="border-t border-border" />

        <div className="space-y-4">
          <SectionHeading>Sender</SectionHeading>
          <FieldRow label="Sender name">
            <Input value={cfg.sender_name} onChange={e => set('sender_name', e.target.value)} placeholder="BillTracker" />
          </FieldRow>
          <FieldRow label="Sender address">
            <Input value={cfg.sender_address} onChange={e => set('sender_address', e.target.value)} placeholder="no-reply@example.com" type="email" />
          </FieldRow>
        </div>

        <div className="border-t border-border" />

        <div className="space-y-4">
          <SectionHeading>SMTP Server</SectionHeading>
          <FieldRow label="SMTP host">
            <Input value={cfg.smtp_host} onChange={e => set('smtp_host', e.target.value)} placeholder="smtp.example.com" />
          </FieldRow>
          <FieldRow label="Port">
            <Input value={cfg.smtp_port} onChange={e => set('smtp_port', e.target.value)} placeholder="587" type="number" className="w-28" />
          </FieldRow>
          <FieldRow label="Encryption">
            <Select value={cfg.smtp_encryption} onValueChange={v => set('smtp_encryption', v)}>
              <SelectTrigger className="w-40">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="starttls">STARTTLS</SelectItem>
                <SelectItem value="ssl">SSL/TLS</SelectItem>
                <SelectItem value="none">None</SelectItem>
              </SelectContent>
            </Select>
          </FieldRow>
          <FieldRow label="Allow self-signed cert">
            <div className="flex items-center h-9">
              <input
                type="checkbox"
                id="self-signed"
                checked={cfg.smtp_self_signed}
                onChange={e => set('smtp_self_signed', e.target.checked)}
                className="h-4 w-4 rounded border-input bg-input accent-primary"
              />
              <label htmlFor="self-signed" className="ml-2 text-sm text-muted-foreground">
                Accept self-signed certificates
              </label>
            </div>
          </FieldRow>
          <FieldRow label="SMTP username">
            <Input value={cfg.smtp_username} onChange={e => set('smtp_username', e.target.value)} placeholder="user@example.com" />
          </FieldRow>
          <FieldRow label="SMTP password">
            <div className="relative">
              <Input
                type={showPw ? 'text' : 'password'}
                value={cfg.smtp_password}
                onChange={e => set('smtp_password', e.target.value)}
                placeholder="••••••••"
                className="pr-9"
              />
              <button
                type="button"
                onClick={() => setShowPw(p => !p)}
                className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
              >
                {showPw ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
              </button>
            </div>
          </FieldRow>
        </div>

        <div className="border-t border-border" />

        <div className="space-y-4">
          <SectionHeading>User Access</SectionHeading>
          <FieldRow label="Allow user config">
            <div className="flex items-center h-9">
              <input
                type="checkbox"
                id="allow-user"
                checked={cfg.allow_user_config}
                onChange={e => set('allow_user_config', e.target.checked)}
                className="h-4 w-4 rounded border-input bg-input accent-primary"
              />
              <label htmlFor="allow-user" className="ml-2 text-sm text-muted-foreground">
                Let users configure their own notification preferences
              </label>
            </div>
          </FieldRow>
          <FieldRow label="Global recipient">
            <Input
              value={cfg.global_recipient}
              onChange={e => set('global_recipient', e.target.value)}
              placeholder="recipient@example.com"
              type="email"
            />
          </FieldRow>
        </div>

        <div className="border-t border-border" />

        <div className="space-y-4">
          <SectionHeading>Test Email</SectionHeading>
          <FieldRow label="Send test to">
            <div className="flex gap-2">
              <Input
                value={testEmail}
                onChange={e => setTestEmail(e.target.value)}
                placeholder="you@example.com"
                type="email"
              />
              <Button variant="outline" onClick={handleTest} disabled={testing} className="shrink-0">
                {testing ? 'Sending…' : 'Send Test Email'}
              </Button>
            </div>
          </FieldRow>
        </div>

        <div className="flex justify-end pt-2">
          <Button onClick={handleSave} disabled={saving}>
            {saving ? 'Saving…' : 'Save Settings'}
          </Button>
        </div>
      </CardContent>
    </Card>
  );
}

// ─── Login Mode Card ──────────────────────────────────────────────────────────

function LoginModeCard({ users }) {
  const [modeData, setModeData]         = useState(null);
  const [loading, setLoading]           = useState(true);
  const [saving, setSaving]             = useState(false);
  const [selectedUser, setSelectedUser] = useState('');

  // Single-user mode confirmation dialog
  const [confirmSingle, setConfirmSingle] = useState(false);
  const [pendingUserId, setPendingUserId] = useState(null);

  useEffect(() => {
    api.authModeConfig()
      .then(d => { setModeData(d); setSelectedUser(d.default_user_id?.toString() || ''); })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []);

  const doSetMode = async (mode, userId) => {
    setSaving(true);
    try {
      await api.setAuthMode({
        auth_mode: mode,
        default_user_id: mode === 'single' ? parseInt(userId, 10) : null,
      });
      const d = await api.authModeConfig();
      setModeData(d);
      toast.success(mode === 'single' ? 'Single-user mode enabled.' : 'Login requirement restored.');
    } catch (err) {
      toast.error(err.message || 'Failed to update auth mode.');
    } finally {
      setSaving(false);
    }
  };

  const handleRequestSingle = () => {
    if (!selectedUser) { toast.error('Select a user first.'); return; }
    setPendingUserId(selectedUser);
    setConfirmSingle(true);
  };

  const handleConfirmSingle = () => {
    setConfirmSingle(false);
    doSetMode('single', pendingUserId);
  };

  if (loading) return <Card><CardContent className="py-8 text-center text-muted-foreground text-sm">Loading…</CardContent></Card>;

  const isMulti      = !modeData || modeData.auth_mode === 'multi';
  const activeUser   = users?.find(u => u.id === modeData?.default_user_id);
  const selectedUsername = users?.find(u => u.id.toString() === selectedUser)?.username ?? selectedUser;

  return (
    <>
      <Card>
        <CardHeader className="pb-4">
          <div className="flex items-center justify-between">
            <CardTitle>Login Mode</CardTitle>
            <Badge className={isMulti ? 'bg-sky-500/15 text-sky-400 border-sky-500/20' : 'bg-amber-500/15 text-amber-400 border-amber-500/20'}>
              {isMulti ? 'Multi-user' : 'Single-user'}
            </Badge>
          </div>
        </CardHeader>
        <CardContent className="space-y-4">
          {isMulti ? (
            <>
              <p className="text-sm text-muted-foreground">
                Single-user mode bypasses the login screen and automatically signs in as the selected user.
              </p>
              <div className="space-y-1.5">
                <Label>Default user</Label>
                <Select value={selectedUser} onValueChange={setSelectedUser}>
                  <SelectTrigger>
                    <SelectValue placeholder="Select a user…" />
                  </SelectTrigger>
                  <SelectContent>
                    {(users || []).filter(u => u.role === 'user').map(u => (
                      <SelectItem key={u.id} value={u.id.toString()}>{u.username}</SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
              <Button onClick={handleRequestSingle} disabled={saving} className="w-full">
                {saving ? 'Enabling…' : 'Enable Single-User Mode'}
              </Button>
            </>
          ) : (
            <>
              <p className="text-sm text-muted-foreground">
                Currently auto-signing in as{' '}
                <span className="font-medium text-foreground">{activeUser?.username ?? '—'}</span>.
                Restoring login requirement will require all users to sign in manually.
              </p>
              <Button variant="outline" onClick={() => doSetMode('multi', null)} disabled={saving} className="w-full">
                {saving ? 'Restoring…' : 'Restore Login Requirement'}
              </Button>
            </>
          )}
        </CardContent>
      </Card>

      {/* Single-user mode confirmation */}
      <AlertDialog open={confirmSingle} onOpenChange={setConfirmSingle}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Enable Single-User Mode?</AlertDialogTitle>
            <AlertDialogDescription>
              Anyone who opens the app will be automatically signed in as{' '}
              <span className="font-medium text-foreground">{selectedUsername}</span>.
              The admin login still requires a password.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Cancel</AlertDialogCancel>
            <AlertDialogAction onClick={handleConfirmSingle}>
              Enable Single-User Mode
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </>
  );
}

// ─── AuthMethodsCard ─────────────────────────────────────────────────────────
// Controls login methods and DB-backed authentik/OIDC provider settings.
// Client secret is write-only; the API returns only a "set" marker.

function AuthMethodsCard() {
  const [data, setData]     = useState(null);
  const [form, setForm]     = useState(null);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [testingOidc, setTestingOidc] = useState(false);
  const [oidcTest, setOidcTest] = useState(null);

  const load = useCallback(async () => {
    try {
      const d = await api.authModeConfig();
      setData(d);
      setForm({
        local_login_enabled: d.local_login_enabled !== false,
        oidc_login_enabled:  !!d.oidc_login_enabled,
        oidc_provider_name:  d.oidc_provider_name || 'authentik',
        oidc_issuer_url:     d.oidc_issuer_url || '',
        oidc_client_id:      d.oidc_client_id || '',
        oidc_client_secret:  '',
        oidc_client_secret_clear: false,
        oidc_token_auth_method: d.oidc_token_auth_method || 'client_secret_basic',
        oidc_redirect_uri:   d.oidc_redirect_uri || defaultOidcRedirectUri(),
        oidc_scopes:         d.oidc_scopes || 'openid email profile groups',
        oidc_auto_provision: d.oidc_auto_provision !== false,
        oidc_admin_group:    d.oidc_admin_group || '',
        oidc_default_role:   d.oidc_default_role || 'user',
      });
    } catch (err) {
      toast.error(err.message || 'Failed to load auth settings.');
    } finally {
      setLoading(false);
    }
  }, []);

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

  const set = (k, v) => setForm(prev => ({ ...prev, [k]: v }));

  async function handleSave() {
    setSaving(true);
    try {
      const d = await api.setAuthMode(form);
      setData(d);
      setForm({
        local_login_enabled: d.local_login_enabled !== false,
        oidc_login_enabled:  !!d.oidc_login_enabled,
        oidc_provider_name:  d.oidc_provider_name || 'authentik',
        oidc_issuer_url:     d.oidc_issuer_url || '',
        oidc_client_id:      d.oidc_client_id || '',
        oidc_client_secret:  '',
        oidc_client_secret_clear: false,
        oidc_token_auth_method: d.oidc_token_auth_method || 'client_secret_basic',
        oidc_redirect_uri:   d.oidc_redirect_uri || defaultOidcRedirectUri(),
        oidc_scopes:         d.oidc_scopes || 'openid email profile groups',
        oidc_auto_provision: d.oidc_auto_provision !== false,
        oidc_admin_group:    d.oidc_admin_group || '',
        oidc_default_role:   d.oidc_default_role || 'user',
      });
      toast.success('Auth method settings saved.');
    } catch (err) {
      toast.error(err.message || 'Failed to save auth method settings.');
    } finally {
      setSaving(false);
    }
  }

  async function handleTestOidc() {
    setTestingOidc(true);
    setOidcTest(null);
    try {
      const result = await api.testOidcConfig(form);
      setOidcTest(result);
      toast.success('authentik configuration test passed.');
    } catch (err) {
      const result = err.data || { ok: false, error: err.message || 'OIDC configuration test failed.' };
      setOidcTest(result);
      toast.error(result.error || 'OIDC configuration test failed.');
    } finally {
      setTestingOidc(false);
    }
  }

  if (loading || !form) {
    return (
      <Card>
        <CardContent className="py-8 text-center text-muted-foreground text-sm">
          Loading auth settings…
        </CardContent>
      </Card>
    );
  }

  const secretAvailable = form.oidc_client_secret.trim()
    ? true
    : form.oidc_client_secret_clear
    ? false
    : !!data?.oidc_client_secret_set;
  const oidcConfigured = !!(
    form.oidc_issuer_url.trim() &&
    form.oidc_client_id.trim() &&
    secretAvailable &&
    form.oidc_redirect_uri.trim()
  );
  const adminGroupConfigured = !!form.oidc_admin_group.trim();
  const wouldLockOut   = !form.local_login_enabled && !form.oidc_login_enabled;
  const cantDisableLocal = !form.local_login_enabled && (!oidcConfigured || !form.oidc_login_enabled || !adminGroupConfigured);
  const oidcEnabledButIncomplete = form.oidc_login_enabled && !oidcConfigured;
  const canSave = !wouldLockOut && !cantDisableLocal && !oidcEnabledButIncomplete && !saving;
  const canTestOidc = oidcConfigured && !testingOidc;
  const missingFields = [
    !form.oidc_issuer_url.trim() && 'Issuer URL',
    !form.oidc_client_id.trim() && 'Client ID',
    !secretAvailable && 'Client Secret',
    !form.oidc_redirect_uri.trim() && 'Redirect URI',
  ].filter(Boolean);
  const issuerEndpointWarning = looksLikeOidcEndpoint(form.oidc_issuer_url);

  return (
    <Card>
      <CardHeader className="pb-4">
        <div className="flex items-center justify-between gap-3">
          <div>
            <CardTitle>Authentication Methods</CardTitle>
            <p className="text-sm text-muted-foreground mt-1">
              Control local login and authentik/OIDC. Settings are saved in the database;
              environment variables only fill blank fields as bootstrap defaults.
            </p>
          </div>
        </div>
      </CardHeader>

      <CardContent className="space-y-5">

        {/* Warnings */}
        {(data?.warnings?.length > 0 || wouldLockOut || cantDisableLocal) && (
          <div className="rounded-lg border border-amber-500/25 bg-amber-500/10 px-4 py-3 space-y-1">
            {wouldLockOut && (
              <p className="text-sm text-amber-600 dark:text-amber-400">
                Cannot disable all login methods; at least one must remain enabled.
              </p>
            )}
            {cantDisableLocal && !wouldLockOut && (
              <p className="text-sm text-amber-600 dark:text-amber-400">
                Cannot disable local login without authentik/OIDC configured, enabled, and mapped to an admin group.
              </p>
            )}
            {oidcEnabledButIncomplete && (
              <p className="text-sm text-amber-600 dark:text-amber-400">
                authentik/OIDC needs {missingFields.join(', ')} before it can be enabled.
              </p>
            )}
            {data?.warnings?.map((w, i) => (
              <p key={i} className="text-sm text-amber-600 dark:text-amber-400">{w}</p>
            ))}
          </div>
        )}

        {/* Local login toggle */}
        <FieldRow label="Local username/password login">
          <div className="flex items-center gap-3">
            <Toggle
              checked={form.local_login_enabled}
              onChange={v => set('local_login_enabled', v)}
              label="Enable local login"
            />
            <span className="text-xs text-muted-foreground">
              {form.local_login_enabled ? 'Enabled' : 'Disabled'}
            </span>
          </div>
        </FieldRow>

        {/* OIDC / authentik login toggle */}
        <FieldRow label="authentik / OIDC login">
          <div className="flex items-center gap-3">
            <Toggle
              checked={form.oidc_login_enabled}
              onChange={v => set('oidc_login_enabled', v)}
              label="Enable OIDC login"
            />
            <span className={`text-xs ${oidcConfigured ? 'text-muted-foreground' : 'text-amber-500'}`}>
              {!oidcConfigured
                ? 'Not fully configured'
                : form.oidc_login_enabled ? 'Enabled' : 'Disabled'}
            </span>
          </div>
        </FieldRow>

        <div className="space-y-4 pt-2 border-t border-border">
          <div className="flex items-center gap-2 pt-1 text-sm font-medium text-muted-foreground">
            <img
              src={AUTHENTIK_ICON_URL}
              alt=""
              aria-hidden="true"
              className="h-5 w-5 shrink-0 object-contain"
            />
            <span>authentik / OIDC configuration</span>
          </div>

          <FieldRow label="Provider name">
            <Input
              value={form.oidc_provider_name}
              onChange={e => set('oidc_provider_name', e.target.value)}
              placeholder="authentik"
              className="max-w-xs h-8 text-sm"
            />
          </FieldRow>

          <FieldRow label="Issuer / discovery URL">
            <div className="space-y-1">
              <Input
                value={form.oidc_issuer_url}
                onChange={e => set('oidc_issuer_url', e.target.value)}
                placeholder="https://yourURL.com/application/o/bills/.well-known/openid-configuration"
                className="max-w-xl h-8 text-sm"
              />
              <p className={issuerEndpointWarning ? 'text-xs text-amber-500' : 'text-xs text-muted-foreground'}>
                Use the authentik provider issuer URL or full discovery URL, for example https://yourURL.com/application/o/bills/.well-known/openid-configuration.
              </p>
              {issuerEndpointWarning && (
                <p className="text-xs text-amber-500">
                  This looks like an authorization endpoint. In authentik, copy the provider issuer or OpenID Configuration URL.
                </p>
              )}
            </div>
          </FieldRow>

          <FieldRow label="Client ID">
            <Input
              value={form.oidc_client_id}
              onChange={e => set('oidc_client_id', e.target.value)}
              placeholder="authentik client ID"
              className="max-w-xl h-8 text-sm"
            />
          </FieldRow>

          <FieldRow label="Client Secret">
            <div className="space-y-2">
              <div className="flex max-w-xl items-center gap-3">
                <Input
                  type="password"
                  value={form.oidc_client_secret}
                  onChange={e => setForm(prev => ({
                    ...prev,
                    oidc_client_secret: e.target.value,
                    oidc_client_secret_clear: e.target.value ? false : prev.oidc_client_secret_clear,
                  }))}
                  placeholder="Leave blank to keep existing secret"
                  className="h-8 text-sm"
                />
                <span className={`shrink-0 text-xs ${data?.oidc_client_secret_set && !form.oidc_client_secret_clear ? 'text-emerald-500' : 'text-muted-foreground'}`}>
                  {data?.oidc_client_secret_set && !form.oidc_client_secret_clear ? 'Secret is set' : 'No secret saved'}
                </span>
              </div>
              <label className="flex items-center gap-2 text-xs text-muted-foreground">
                <input
                  type="checkbox"
                  checked={form.oidc_client_secret_clear}
                  onChange={e => set('oidc_client_secret_clear', e.target.checked)}
                />
                Clear saved secret on save
              </label>
            </div>
          </FieldRow>

          <FieldRow label="Client auth method">
            <div className="space-y-1">
              <select
                value={form.oidc_token_auth_method}
                onChange={e => set('oidc_token_auth_method', e.target.value)}
                className="h-8 rounded-md border border-input bg-background px-3 text-sm"
              >
                <option value="client_secret_basic">client_secret_basic</option>
                <option value="client_secret_post">client_secret_post</option>
              </select>
              <p className="text-xs text-muted-foreground">
                Advanced. Keep client_secret_basic unless your authentik provider explicitly requires client_secret_post.
              </p>
            </div>
          </FieldRow>

          <FieldRow label="Redirect URI">
            <div className="space-y-1">
              <div className="flex max-w-xl gap-2">
                <Input
                  value={form.oidc_redirect_uri}
                  onChange={e => set('oidc_redirect_uri', e.target.value)}
                  placeholder={defaultOidcRedirectUri()}
                  className="h-8 text-sm"
                />
                <Button
                  type="button"
                  variant="outline"
                  size="sm"
                  onClick={() => set('oidc_redirect_uri', defaultOidcRedirectUri())}
                >
                  Use Current
                </Button>
              </div>
              <p className="text-xs text-muted-foreground">
                Add this exact URL to the Redirect URIs allowed by authentik.
              </p>
            </div>
          </FieldRow>

          <FieldRow label="Scopes">
            <Input
              value={form.oidc_scopes}
              onChange={e => set('oidc_scopes', e.target.value)}
              placeholder="openid email profile groups"
              className="max-w-xl h-8 text-sm"
            />
          </FieldRow>

          <FieldRow label="Admin group">
            <div className="space-y-1">
              <Input
                value={form.oidc_admin_group}
                onChange={e => set('oidc_admin_group', e.target.value)}
                placeholder="e.g. bill-tracker-admins"
                className="max-w-sm h-8 text-sm"
              />
              <p className="text-xs text-muted-foreground">
                Only users in this authentik group become app admins. Admin is never granted by default.
              </p>
            </div>
          </FieldRow>

          <FieldRow label="Auto-provision users">
            <div className="space-y-1">
              <div className="flex items-center gap-3">
                <Toggle
                  checked={form.oidc_auto_provision}
                  onChange={v => set('oidc_auto_provision', v)}
                  label="Auto-provision users"
                />
                <span className="text-xs text-muted-foreground">
                  {form.oidc_auto_provision ? 'Enabled' : 'Disabled'}
                </span>
              </div>
              <p className="text-xs text-muted-foreground">
                When enabled, valid authentik users are created in this app on first login.
              </p>
            </div>
          </FieldRow>

          <FieldRow label="Default role">
            <div className="flex items-center gap-2">
              <Input
                value="user"
                readOnly
                className="max-w-[120px] h-8 text-sm"
              />
              <span className="text-xs text-muted-foreground">
                Admin role only via admin group.
              </span>
            </div>
          </FieldRow>

          {data?.oidc_env_fallback_used && (
            <div className="rounded-lg border border-sky-500/25 bg-sky-500/10 px-4 py-3 text-xs text-sky-700 dark:text-sky-400">
              One or more blank database fields are currently using environment fallback values. Saving values here takes precedence.
            </div>
          )}

          {oidcTest && (
            <div className={`rounded-lg border px-4 py-3 text-xs ${
              oidcTest.ok
                ? 'border-emerald-500/25 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400'
                : 'border-destructive/25 bg-destructive/10 text-destructive'
            }`}>
              {oidcTest.ok
                ? `Configuration test passed for ${oidcTest.issuer || form.oidc_issuer_url}.`
                : oidcTest.error || 'Configuration test failed.'}
            </div>
          )}
        </div>

        <div className="pt-2 border-t border-border flex flex-wrap items-center gap-2">
          <Button variant="outline" onClick={handleTestOidc} disabled={!canTestOidc}>
            {testingOidc ? 'Testing…' : 'Test Configuration'}
          </Button>
          <Button
            variant="outline"
            disabled={!data?.oidc_login_enabled || !data?.oidc_configured}
            onClick={() => { window.location.href = '/api/auth/oidc/login?redirect_to=/admin'; }}
          >
            <img
              src={AUTHENTIK_ICON_URL}
              alt=""
              aria-hidden="true"
              className="mr-2 h-4 w-4 shrink-0 object-contain"
            />
            Test authentik Login
          </Button>
          <Button onClick={handleSave} disabled={!canSave}>
            {saving ? 'Saving…' : 'Save Auth Settings'}
          </Button>
        </div>

      </CardContent>
    </Card>
  );
}

// ─── Users Table ──────────────────────────────────────────────────────────────

function UsersTable({ users, onRefresh, currentUser }) {
  const [resetForms, setResetForms] = useState({});
  const [deleting, setDeleting]     = useState(null);
  const [resetting, setResetting]   = useState(null);
  const [roleUpdating, setRoleUpdating] = useState(null);
  const [activeUpdating, setActiveUpdating] = useState(null);

  // Delete confirmation dialog
  const [deleteTarget, setDeleteTarget] = useState(null); // user object

  const setReset = (id, v) => setResetForms(p => ({ ...p, [id]: { ...(p[id] || {}), ...v } }));
  const getForm  = (id)    => resetForms[id] || { pw: '', open: false };

  const handleReset = async (user) => {
    const form = getForm(user.id);
    if (!form.pw || form.pw.length < 8) { toast.error('Password must be at least 8 characters.'); return; }
    setResetting(user.id);
    try {
      await api.resetPassword(user.id, { password: form.pw });
      toast.success(`Password reset for ${user.username}.`);
      setReset(user.id, { pw: '', open: false });
    } catch (err) {
      toast.error(err.message || 'Failed to reset password.');
    } finally {
      setResetting(null);
    }
  };

  const handleDelete = async () => {
    if (!deleteTarget) return;
    setDeleting(deleteTarget.id);
    try {
      await api.deleteUser(deleteTarget.id);
      toast.success(`User ${deleteTarget.username} deleted.`);
      setDeleteTarget(null);
      onRefresh();
    } catch (err) {
      toast.error(err.message || 'Failed to delete user.');
    } finally {
      setDeleting(null);
    }
  };

  const handleRoleChange = async (user, role) => {
    if (user.role === role) return;
    setRoleUpdating(user.id);
    try {
      await api.updateUserRole(user.id, { role });
      toast.success(`${user.username} is now ${role === 'admin' ? 'an admin' : 'a user'}.`);
      onRefresh();
    } catch (err) {
      toast.error(err.message || 'Failed to update user role.');
    } finally {
      setRoleUpdating(null);
    }
  };

  const handleActiveChange = async (user, active) => {
    setActiveUpdating(user.id);
    try {
      await api.updateUserActive(user.id, { active });
      toast.success(`${user.username} is now ${active ? 'active' : 'inactive'}.`);
      onRefresh();
    } catch (err) {
      toast.error(err.message || 'Failed to update user status.');
    } finally {
      setActiveUpdating(null);
    }
  };

  return (
    <>
      <Card>
        <CardHeader className="pb-4">
          <CardTitle>Users</CardTitle>
        </CardHeader>
        <CardContent className="p-0">
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="border-b border-border">
                  <th className="text-left px-6 py-3 text-muted-foreground font-medium">Username</th>
                  <th className="text-left px-6 py-3 text-muted-foreground font-medium">Role</th>
                  <th className="text-left px-6 py-3 text-muted-foreground font-medium">Status</th>
                  <th className="text-left px-6 py-3 text-muted-foreground font-medium">Password</th>
                  <th className="text-left px-6 py-3 text-muted-foreground font-medium">Reset Password</th>
                  <th className="px-6 py-3" />
                </tr>
              </thead>
              <tbody>
                {(users || []).map(user => {
                  const form = getForm(user.id);
                  const isSelf = currentUser?.id === user.id;
                  return (
                    <tr key={user.id} className="group border-b border-border last:border-0 hover:bg-muted/30 transition-colors">
                      <td className="px-6 py-3 font-medium">
                        <div className="flex items-center gap-2">
                          <span>{user.username}</span>
                          {user.is_default_admin && <Badge variant="secondary">default admin</Badge>}
                        </div>
                      </td>
                      <td className="px-6 py-3">
                        <div className="flex items-center gap-2">
                          <Badge variant={user.role === 'admin' ? 'autodraft' : 'secondary'}>
                            {user.role}
                          </Badge>
                          <select
                            value={user.role}
                            disabled={isSelf || roleUpdating === user.id}
                            onChange={e => handleRoleChange(user, e.target.value)}
                            className="h-8 rounded-md border border-input bg-background px-2 text-xs disabled:cursor-not-allowed disabled:opacity-50"
                            title={isSelf ? 'You cannot change your own role' : 'Change user role'}
                          >
                            <option value="user">user</option>
                            <option value="admin">admin</option>
                          </select>
                        </div>
                      </td>
                      <td className="px-6 py-3">
                        <select
                          value={user.active === false || user.active === 0 ? 'inactive' : 'active'}
                          disabled={isSelf || activeUpdating === user.id}
                          onChange={e => handleActiveChange(user, e.target.value === 'active')}
                          className="h-8 rounded-md border border-input bg-background px-2 text-xs disabled:cursor-not-allowed disabled:opacity-50"
                          title={isSelf ? 'You cannot deactivate your own account' : 'Change user status'}
                        >
                          <option value="active">active</option>
                          <option value="inactive">inactive</option>
                        </select>
                      </td>
                      <td className="px-6 py-3">
                        {user.must_change_password
                          ? <Badge variant="due_soon">Temporary</Badge>
                          : <span className="text-muted-foreground">Set</span>
                        }
                      </td>
                      <td className="px-6 py-3">
                        {form.open ? (
                          <div className="flex items-center gap-2">
                            <Input
                              type="password"
                              placeholder="New password"
                              value={form.pw || ''}
                              onChange={e => setReset(user.id, { pw: e.target.value })}
                              className="h-8 text-sm w-36"
                            />
                            <Button
                              size="sm"
                              onClick={() => handleReset(user)}
                              disabled={resetting === user.id}
                            >
                              {resetting === user.id ? '…' : 'Save'}
                            </Button>
                            <Button
                              size="sm"
                              variant="ghost"
                              onClick={() => setReset(user.id, { open: false, pw: '' })}
                            >
                              Cancel
                            </Button>
                          </div>
                        ) : (
                          <Button size="sm" variant="outline" onClick={() => setReset(user.id, { open: true })}>
                            Reset
                          </Button>
                        )}
                      </td>
                      <td className="px-6 py-3 text-right">
                        {!isSelf && (
                          <Button
                            size="sm"
                            variant="destructive"
                            onClick={() => setDeleteTarget(user)}
                            disabled={deleting === user.id}
                          >
                            {deleting === user.id ? '…' : 'Delete'}
                          </Button>
                        )}
                      </td>
                    </tr>
                  );
                })}
                {!users?.length && (
                  <tr>
                    <td colSpan={6} className="px-6 py-8 text-center text-muted-foreground">No users found.</td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>
        </CardContent>
      </Card>

      {/* Delete user confirmation */}
      <AlertDialog open={!!deleteTarget} onOpenChange={(open) => { if (!open) setDeleteTarget(null); }}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Delete {deleteTarget?.username}?</AlertDialogTitle>
            <AlertDialogDescription>
              This is permanent in 2026. The user account and all user-owned data will be deleted, including bills,
              payments, categories, monthly state, monthly starting amounts, imports, import history, and sessions.
              This cannot be undone from BillTracker.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel disabled={!!deleting}>Cancel</AlertDialogCancel>
            <AlertDialogAction
              className={cn(buttonVariants({ variant: 'destructive' }))}
              onClick={handleDelete}
              disabled={!!deleting}
            >
              {deleting ? 'Deleting…' : 'Delete User'}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </>
  );
}

// ─── Add User Card ────────────────────────────────────────────────────────────

function AddUserCard({ onCreated }) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading]   = useState(false);
  const [error, setError]       = useState('');

  const handleCreate = async (e) => {
    e.preventDefault();
    setError('');
    
    if (password.length < 8) {
      const msg = 'Password must be at least 8 characters.';
      setError(msg);
      toast.error(msg);
      return;
    }
    
    setLoading(true);
    try {
      await api.createUser({ username, password });
      toast.success(`User "${username}" created.`);
      setUsername('');
      setPassword('');
      setError('');
      onCreated();
    } catch (err) {
      const errorMessage = err.message || 'Failed to create user.';
      setError(errorMessage);
      toast.error(errorMessage);
    } finally {
      setLoading(false);
    }
  };

  return (
    <Card>
      <CardHeader className="pb-4">
        <CardTitle>Add User</CardTitle>
      </CardHeader>
      <CardContent>
        <form onSubmit={handleCreate} className="flex flex-col gap-3 lg:flex-row lg:items-end">
          <div className="space-y-1.5 flex-1">
            <Label htmlFor="new-uname">Username</Label>
            <Input
              id="new-uname"
              value={username}
              onChange={e => setUsername(e.target.value)}
              placeholder="username"
              required
            />
          </div>
          <div className="space-y-1.5 flex-1">
            <Label htmlFor="new-upw">Password</Label>
            <Input
              id="new-upw"
              type="password"
              value={password}
              onChange={e => setPassword(e.target.value)}
              placeholder="Password"
              required
            />
          </div>
          <div className="lg:flex-1" />
          {error && (
            <div className="w-full lg:w-auto rounded-lg border border-destructive/25 bg-destructive/10 px-3 py-2 text-sm text-destructive lg:col-start-1 lg:col-span-3">
              {error}
            </div>
          )}
          <Button type="submit" disabled={loading} className="shrink-0" aria-busy={loading}>
            {loading ? 'Creating…' : 'Create User'}
          </Button>
        </form>
      </CardContent>
    </Card>
  );
}

// ─── Backup Management Card ──────────────────────────────────────────────────

function BackupManagementCard() {
  const DEFAULT_SETTINGS = {
    enabled: false,
    frequency: 'daily',
    time: '02:00',
    retention_count: 14,
    last_run_at: null,
    next_run_at: null,
    last_error: null,
  };

  const [backups, setBackups] = useState([]);
  const [settings, setSettings] = useState(DEFAULT_SETTINGS);
  const [loading, setLoading] = useState(true);
  const [busy, setBusy] = useState('');
  const [restoreTarget, setRestoreTarget] = useState(null);
  const [deleteTarget, setDeleteTarget] = useState(null);

  const load = useCallback(async () => {
    setLoading(true);
    try {
      const [backupData, settingsData] = await Promise.all([
        api.adminBackups(),
        api.adminBackupSettings(),
      ]);
      setBackups(backupData.backups || []);
      setSettings({ ...DEFAULT_SETTINGS, ...settingsData });
    } catch (err) {
      toast.error(err.message || 'Failed to load backups.');
    } finally {
      setLoading(false);
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

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

  const latest = backups[0];
  const setSchedule = (key, value) => setSettings(prev => ({ ...prev, [key]: value }));

  async function handleCreate() {
    setBusy('create');
    try {
      await api.createAdminBackup();
      toast.success('Backup created.');
      await load();
    } catch (err) {
      toast.error(err.message || 'Failed to create backup.');
    } finally {
      setBusy('');
    }
  }

  async function handleDownload(backup) {
    setBusy(`download:${backup.id}`);
    try {
      const { blob, filename } = await api.downloadAdminBackup(backup.id);
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = filename || backup.id;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(url);
    } catch (err) {
      toast.error(err.message || 'Failed to download backup.');
    } finally {
      setBusy('');
    }
  }

  async function handleImport(e) {
    const file = e.target.files?.[0];
    e.target.value = '';
    if (!file) return;

    setBusy('import');
    try {
      await api.importAdminBackup(file);
      toast.success('Backup imported.');
      await load();
    } catch (err) {
      toast.error(err.message || 'Failed to import backup.');
    } finally {
      setBusy('');
    }
  }

  async function handleRestore() {
    if (!restoreTarget) return;
    setBusy(`restore:${restoreTarget.id}`);
    try {
      const result = await api.restoreAdminBackup(restoreTarget.id);
      toast.success(`Database restored. Pre-restore backup: ${result.pre_restore_backup}`);
      setRestoreTarget(null);
      await load();
    } catch (err) {
      toast.error(err.message || 'Failed to restore backup.');
    } finally {
      setBusy('');
    }
  }

  async function handleDelete() {
    if (!deleteTarget) return;
    setBusy(`delete:${deleteTarget.id}`);
    try {
      await api.deleteAdminBackup(deleteTarget.id);
      toast.success('Backup deleted.');
      setDeleteTarget(null);
      await load();
    } catch (err) {
      toast.error(err.message || 'Failed to delete backup.');
    } finally {
      setBusy('');
    }
  }

  async function handleSaveSettings() {
    setBusy('settings');
    try {
      const saved = await api.saveAdminBackupSettings({
        enabled: !!settings.enabled,
        frequency: settings.frequency,
        time: settings.time,
        retention_count: parseInt(settings.retention_count, 10) || 14,
      });
      setSettings({ ...DEFAULT_SETTINGS, ...saved });
      toast.success('Backup schedule saved.');
    } catch (err) {
      toast.error(err.message || 'Failed to save backup schedule.');
    } finally {
      setBusy('');
    }
  }

  async function handleRunScheduledNow() {
    setBusy('run-scheduled');
    try {
      await api.runScheduledBackupNow();
      toast.success('Scheduled backup created.');
      await load();
    } catch (err) {
      toast.error(err.message || 'Failed to run scheduled backup.');
    } finally {
      setBusy('');
    }
  }

  if (loading) {
    return <Card><CardContent className="py-8 text-center text-muted-foreground text-sm">Loading backups…</CardContent></Card>;
  }

  return (
    <>
      <Card>
        <CardHeader className="pb-4">
          <div className="flex items-center justify-between gap-3">
            <div>
              <CardTitle>Database Backups</CardTitle>
              <p className="text-sm text-muted-foreground mt-1">
                Admin-only SQLite backup, import, download, restore, and schedule controls.
              </p>
            </div>
            <Badge className={settings.last_error ? 'bg-red-500/15 text-red-400 border-red-500/20' : 'bg-emerald-500/15 text-emerald-400 border-emerald-500/20'}>
              {settings.last_error ? 'Attention' : 'Ready'}
            </Badge>
          </div>
        </CardHeader>
        <CardContent className="space-y-6">
          <div className="grid gap-3 md:grid-cols-4">
            {[
              ['Managed backups', backups.length],
              ['Latest backup', latest ? formatDateTime(latest.modified_at) : '—'],
              ['Latest size', latest ? fmtBytes(latest.size_bytes) : '—'],
              ['Scheduled', settings.enabled ? `${settings.frequency} ${settings.time}` : 'Disabled'],
            ].map(([label, value]) => (
              <div key={label} className="rounded-lg border border-border bg-muted/20 p-3">
                <p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">{label}</p>
                <p className="text-sm font-medium mt-1 truncate">{value}</p>
              </div>
            ))}
          </div>

          {settings.last_error && (
            <div className="rounded-lg border border-red-500/25 bg-red-500/10 px-3 py-2 text-sm text-red-400">
              {settings.last_error}
            </div>
          )}

          <div className="flex flex-wrap items-center gap-2">
            <Button onClick={handleCreate} disabled={!!busy}>
              <Database className="h-4 w-4" />
              {busy === 'create' ? 'Creating…' : 'Create Backup'}
            </Button>
            <Button asChild variant="outline" disabled={!!busy}>
              <label className={cn('cursor-pointer', busy && 'pointer-events-none opacity-50')}>
                <Upload className="h-4 w-4" />
                {busy === 'import' ? 'Importing…' : 'Import Backup'}
                <input
                  type="file"
                  accept=".sqlite,.db,application/octet-stream,application/x-sqlite3,application/vnd.sqlite3"
                  onChange={handleImport}
                  className="sr-only"
                  disabled={!!busy}
                />
              </label>
            </Button>
            <Button variant="outline" onClick={load} disabled={!!busy}>
              <RefreshCw className="h-4 w-4" />
              Refresh
            </Button>
          </div>

          <div className="overflow-x-auto rounded-lg border border-border">
            <table className="min-w-[860px] w-full text-sm">
              <thead className="bg-muted/40">
                <tr className="border-b border-border">
                  <th className="text-left px-4 py-3 text-muted-foreground font-medium">Backup</th>
                  <th className="text-left px-4 py-3 text-muted-foreground font-medium">Type</th>
                  <th className="text-left px-4 py-3 text-muted-foreground font-medium">Modified</th>
                  <th className="text-left px-4 py-3 text-muted-foreground font-medium">Size</th>
                  <th className="text-left px-4 py-3 text-muted-foreground font-medium">Checksum</th>
                  <th className="px-4 py-3" />
                </tr>
              </thead>
              <tbody>
                {backups.map(backup => (
                  <tr key={backup.id} className="border-b border-border last:border-0 hover:bg-muted/25">
                    <td className="px-4 py-3 font-mono text-xs max-w-[260px] truncate" title={backup.id}>{backup.id}</td>
                    <td className="px-4 py-3"><BackupTypeBadge type={backup.type} /></td>
                    <td className="px-4 py-3 text-muted-foreground">{formatDateTime(backup.modified_at)}</td>
                    <td className="px-4 py-3 font-mono">{fmtBytes(backup.size_bytes)}</td>
                    <td className="px-4 py-3 font-mono text-xs text-muted-foreground max-w-[120px] truncate" title={backup.checksum}>
                      {backup.checksum || '—'}
                    </td>
                    <td className="px-4 py-3">
                      <div className="flex items-center justify-end gap-1">
                        <Button size="icon" variant="ghost" title="Download backup" onClick={() => handleDownload(backup)} disabled={!!busy}>
                          <Download className="h-4 w-4" />
                        </Button>
                        <Button size="icon" variant="ghost" title="Restore backup" onClick={() => setRestoreTarget(backup)} disabled={!!busy}>
                          <RotateCcw className="h-4 w-4" />
                        </Button>
                        <Button size="icon" variant="ghost" title="Delete backup" onClick={() => setDeleteTarget(backup)} disabled={!!busy}>
                          <Trash2 className="h-4 w-4" />
                        </Button>
                      </div>
                    </td>
                  </tr>
                ))}
                {!backups.length && (
                  <tr>
                    <td colSpan={6} className="px-4 py-8 text-center text-muted-foreground">
                      No managed backups yet.
                    </td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>

          <div className="border-t border-border pt-5 space-y-4">
            <div className="flex items-center justify-between">
              <div>
                <SectionHeading>Scheduled Backups</SectionHeading>
                <p className="text-xs text-muted-foreground mt-1">
                  Scheduled runs create managed backups and only apply retention to scheduled backups.
                </p>
              </div>
              <Toggle checked={!!settings.enabled} onChange={v => setSchedule('enabled', v)} label="Enable scheduled backups" />
            </div>

            <div className="grid gap-4 md:grid-cols-4">
              <div className="space-y-1.5">
                <Label>Frequency</Label>
                <Select value={settings.frequency} onValueChange={v => setSchedule('frequency', v)}>
                  <SelectTrigger><SelectValue /></SelectTrigger>
                  <SelectContent>
                    <SelectItem value="daily">Daily</SelectItem>
                    <SelectItem value="weekly">Weekly</SelectItem>
                  </SelectContent>
                </Select>
              </div>
              <div className="space-y-1.5">
                <Label>Time</Label>
                <Input type="time" value={settings.time} onChange={e => setSchedule('time', e.target.value)} />
              </div>
              <div className="space-y-1.5">
                <Label>Keep scheduled</Label>
                <Input
                  type="number"
                  min="1"
                  max="365"
                  value={settings.retention_count}
                  onChange={e => setSchedule('retention_count', e.target.value)}
                />
              </div>
              <div className="space-y-1.5">
                <Label>Next run</Label>
                <div className="h-9 rounded-md border border-input bg-muted/30 px-3 py-2 text-sm text-muted-foreground truncate">
                  {formatDateTime(settings.next_run_at)}
                </div>
              </div>
            </div>

            <div className="grid gap-3 md:grid-cols-2">
              <div className="text-sm text-muted-foreground">
                Last run: <span className="text-foreground">{formatDateTime(settings.last_run_at)}</span>
              </div>
              <div className="text-sm text-muted-foreground">
                Last error: <span className="text-foreground">{settings.last_error || '—'}</span>
              </div>
            </div>

            <div className="flex flex-wrap justify-end gap-2">
              <Button variant="outline" onClick={handleRunScheduledNow} disabled={!!busy}>
                <Play className="h-4 w-4" />
                {busy === 'run-scheduled' ? 'Running…' : 'Run Scheduled Now'}
              </Button>
              <Button onClick={handleSaveSettings} disabled={!!busy}>
                {busy === 'settings' ? 'Saving…' : 'Save Schedule'}
              </Button>
            </div>
          </div>
        </CardContent>
      </Card>

      <AlertDialog open={!!restoreTarget} onOpenChange={(open) => { if (!open) setRestoreTarget(null); }}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Restore this database backup?</AlertDialogTitle>
            <AlertDialogDescription>
              This replaces the live database with <span className="font-mono">{restoreTarget?.id}</span>.
              A pre-restore backup will be created first. Run this during a quiet maintenance window.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel disabled={busy.startsWith('restore:')}>Cancel</AlertDialogCancel>
            <AlertDialogAction
              className={cn(buttonVariants({ variant: 'destructive' }))}
              onClick={handleRestore}
              disabled={busy.startsWith('restore:')}
            >
              {busy.startsWith('restore:') ? 'Restoring…' : 'Restore Database'}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>

      <AlertDialog open={!!deleteTarget} onOpenChange={(open) => { if (!open) setDeleteTarget(null); }}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Delete this backup?</AlertDialogTitle>
            <AlertDialogDescription>
              This permanently deletes <span className="font-mono">{deleteTarget?.id}</span>.
              The live database is not affected.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel disabled={busy.startsWith('delete:')}>Cancel</AlertDialogCancel>
            <AlertDialogAction
              className={cn(buttonVariants({ variant: 'destructive' }))}
              onClick={handleDelete}
              disabled={busy.startsWith('delete:')}
            >
              {busy.startsWith('delete:') ? 'Deleting…' : 'Delete Backup'}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </>
  );
}

// ─── CleanupPanel ─────────────────────────────────────────────────────────────

function CleanupPanel() {
  const [status, setStatus]   = useState(null);
  const [form, setForm]       = useState({
    import_sessions_enabled:     true,
    temp_exports_enabled:        true,
    temp_export_max_age_hours:   2,
    backup_partials_enabled:     true,
    import_history_enabled:      false,
    import_history_max_age_days: 365,
  });
  const [saving,  setSaving]  = useState(false);
  const [running, setRunning] = useState(false);
  const [loading, setLoading] = useState(true);

  const load = useCallback(async () => {
    try {
      const data = await api.adminCleanup();
      setStatus(data);
      if (data.settings) setForm(data.settings);
    } catch (err) {
      toast.error(err.message || 'Failed to load cleanup settings.');
    } finally {
      setLoading(false);
    }
  }, []);

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

  const set = (k, v) => setForm(prev => ({ ...prev, [k]: v }));

  async function handleSave() {
    setSaving(true);
    try {
      const next = await api.saveAdminCleanup(form);
      if (next) setForm(next);
      toast.success('Cleanup settings saved.');
    } catch (err) {
      toast.error(err.message || 'Failed to save cleanup settings.');
    } finally {
      setSaving(false);
    }
  }

  async function handleRunNow() {
    setRunning(true);
    try {
      const result = await api.runAdminCleanup();
      setStatus(prev => ({
        ...prev,
        last_run_at:  result.ran_at,
        last_result:  result.tasks,
      }));
      toast.success('Cleanup tasks completed.');
    } catch (err) {
      toast.error(err.message || 'Cleanup run failed.');
    } finally {
      setRunning(false);
    }
  }

  function resultLine(label, task, countKey) {
    if (!task || task[countKey] == null) return null;
    return `${label}: ${task[countKey]}`;
  }

  const resultLines = status?.last_result ? [
    resultLine('Import sessions pruned', status.last_result.import_sessions, 'pruned'),
    resultLine('Temp export files removed', status.last_result.temp_export_files, 'removed'),
    resultLine('Backup partials removed', status.last_result.backup_partials, 'removed'),
    resultLine('Import history rows pruned', status.last_result.import_history, 'pruned'),
  ].filter(Boolean) : [];

  if (loading) {
    return (
      <Card>
        <CardContent className="py-8 text-center text-muted-foreground text-sm">
          Loading cleanup settings…
        </CardContent>
      </Card>
    );
  }

  return (
    <Card>
      <CardHeader className="pb-4">
        <div className="flex items-center justify-between gap-3">
          <div className="flex items-center gap-3">
            <Wrench className="h-5 w-5 text-muted-foreground" />
            <div>
              <CardTitle>Cleanup / Maintenance</CardTitle>
              <p className="text-sm text-muted-foreground mt-1">
                Automatic daily cleanup: expired import sessions, stale export temp files, orphaned backup partials. Runs at 6:00 AM.
              </p>
            </div>
          </div>
          <Badge className="bg-emerald-500/15 text-emerald-400 border-emerald-500/20 shrink-0">Auto</Badge>
        </div>
      </CardHeader>

      <CardContent className="space-y-6">

        {/* Last run summary */}
        <div className="grid gap-3 md:grid-cols-2">
          <div className="rounded-lg border border-border bg-muted/20 p-3">
            <p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">Last Run</p>
            <p className="text-sm font-medium mt-1">{status?.last_run_at ? formatDateTime(status.last_run_at) : '—'}</p>
          </div>
          <div className="rounded-lg border border-border bg-muted/20 p-3">
            <p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">Last Result</p>
            {resultLines.length > 0 ? (
              <ul className="mt-1 space-y-0.5">
                {resultLines.map(line => (
                  <li key={line} className="text-xs text-muted-foreground">{line}</li>
                ))}
              </ul>
            ) : (
              <p className="text-sm font-medium mt-1 text-muted-foreground/60">No runs recorded yet</p>
            )}
          </div>
        </div>

        {/* Settings */}
        <div className="space-y-3">
          <p className="text-sm font-medium">Task Settings</p>

          {[
            ['import_sessions_enabled',  'Prune expired import sessions (24h TTL)'],
            ['temp_exports_enabled',     'Remove stale SQLite export temp files'],
            ['backup_partials_enabled',  'Remove orphaned backup .partial / .upload files'],
          ].map(([key, label]) => (
            <FieldRow key={key} label={label}>
              <Toggle checked={!!form[key]} onChange={v => set(key, v)} label={label} />
            </FieldRow>
          ))}

          <FieldRow label="Temp file max age (hours, 1–72)">
            <Input
              type="number" min="1" max="72"
              value={form.temp_export_max_age_hours}
              onChange={e => set('temp_export_max_age_hours', parseInt(e.target.value, 10) || 2)}
              disabled={!form.temp_exports_enabled}
              className="max-w-[120px] h-8 text-sm"
            />
          </FieldRow>

          <FieldRow label="Trim import history rows (off by default)">
            <Toggle checked={!!form.import_history_enabled} onChange={v => set('import_history_enabled', v)} label="Trim import history rows" />
          </FieldRow>

          {form.import_history_enabled && (
            <>
              <FieldRow label="Import history max age (days, 30–3650)">
                <Input
                  type="number" min="30" max="3650"
                  value={form.import_history_max_age_days}
                  onChange={e => set('import_history_max_age_days', parseInt(e.target.value, 10) || 365)}
                  className="max-w-[120px] h-8 text-sm"
                />
              </FieldRow>
              <div className="rounded-lg border border-amber-500/25 bg-amber-500/10 px-4 py-3 text-sm text-amber-600 dark:text-amber-400">
                <strong>Warning:</strong> Import history trimming permanently deletes audit records older than {form.import_history_max_age_days} days. This cannot be undone.
              </div>
            </>
          )}
        </div>

        {/* Action buttons */}
        <div className="flex flex-wrap items-center gap-2 pt-2 border-t border-border">
          <Button onClick={handleSave} disabled={saving || running}>
            {saving ? 'Saving…' : 'Save Settings'}
          </Button>
          <Button variant="outline" onClick={handleRunNow} disabled={saving || running}>
            <Play className="h-4 w-4" />
            {running ? 'Running…' : 'Run Cleanup Now'}
          </Button>
        </div>

      </CardContent>
    </Card>
  );
}

// ─── AdminPage ────────────────────────────────────────────────────────────────

export default function AdminPage() {
  const navigate = useNavigate();

  const [me, setMe]             = useState(null);
  const [hasUsers, setHasUsers] = useState(null); // null = loading
  const [users, setUsers]       = useState([]);

  const loadMe = useCallback(async () => {
    try {
      const d = await api.me();
      setMe(d.user);
    } catch {
      navigate('/login', { replace: true });
    }
  }, [navigate]);

  const loadHasUsers = useCallback(async () => {
    try {
      const d = await api.hasUsers();
      setHasUsers(d.has_users);
      if (d.has_users) loadUsers();
    } catch {}
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  const loadUsers = useCallback(async () => {
    try {
      const d = await api.adminUsers();
      setUsers(d.users || d);
    } catch {}
  }, []);

  useEffect(() => {
    loadMe();
    loadHasUsers();
  }, [loadMe, loadHasUsers]);

  const handleOnboardingComplete = () => {
    setHasUsers(true);
    loadUsers();
  };

  // Loading state
  if (hasUsers === null) {
    return (
      <div className="flex items-center justify-center min-h-screen text-muted-foreground text-sm">
        Loading…
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-[radial-gradient(circle_at_top_left,oklch(var(--primary)/0.06),transparent_34rem),linear-gradient(180deg,oklch(var(--background)),oklch(var(--muted)/0.18))] text-foreground">
      <AppNavigation adminMode />

      {/* Content */}
      {!hasUsers ? (
        <OnboardingWizard onComplete={handleOnboardingComplete} />
      ) : (
        <main className="mx-auto max-w-5xl px-4 py-6 sm:px-6 lg:px-8 lg:py-8 space-y-6">
          <EmailNotifCard />
          <BackupManagementCard />
          <CleanupPanel />
          <LoginModeCard users={users} />
          <AuthMethodsCard />
          <AddUserCard onCreated={loadUsers} />
          <UsersTable users={users} onRefresh={loadUsers} currentUser={me} />
        </main>
      )}
    </div>
  );
}
