import React, { useState, useEffect, useCallback } from 'react';

export default function AdminApp() {
  const [s, setS] = useState<any>(null);
  const [stats, setStats] = useState<any>(null);
  const [models, setModels] = useState<any[]>([]);
  const [avail, setAvail] = useState<any[]>([]);
  const [users, setUsers] = useState<any[]>([]);
  const [tgLinks, setTgLinks] = useState<any[]>([]);
  const [toast, setToast] = useState<string | null>(null);
  const [orKey, setOrKey] = useState(''); const [pdPw, setPdPw] = useState(''); const [nasPw, setNasPw] = useState('');
  const [storage, setStorage] = useState<any>(null);
  const [targets, setTargets] = useState<any[]>([]);
  const [nt, setNt] = useState<any>({ protocol: 'sftp' });
  const notify = (t: string) => { setToast(t); setTimeout(() => setToast(null), 2600); };

  const load = useCallback(async () => {
    const [a, b, c, d, e, f, g] = await Promise.all([
      fetch('/api/admin/settings').then((r) => r.json()),
      fetch('/api/admin/stats').then((r) => r.json()),
      fetch('/api/admin/models?available=1').then((r) => r.json()),
      fetch('/api/admin/users').then((r) => r.json()),
      fetch('/api/admin/telegram/links').then((r) => r.json()).catch(() => ({ links: [] })),
      fetch('/api/admin/storage').then((r) => r.json()).catch(() => ({ stats: null })),
      fetch('/api/admin/delivery-targets').then((r) => r.json()).catch(() => ({ targets: [] })),
    ]);
    setS(a.settings || {}); setStats(b); setModels(c.models || []); setAvail(c.available || []);
    setUsers(d.users || []); setTgLinks(e.links || []); setStorage(f.stats || null); setTargets(g.targets || []);
  }, []);
  const addTarget = async () => {
    if (!nt.name?.trim()) { notify('Name fehlt.'); return; }
    await fetch('/api/admin/delivery-targets', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(nt) });
    setNt({ protocol: 'sftp' }); notify('Ziel angelegt.'); load();
  };
  const delTarget = async (id: string) => { if (!confirm('Ziel löschen?')) return; await fetch(`/api/admin/delivery-targets?id=${id}`, { method: 'DELETE' }); load(); };
  const toggleBackup = async (t: any) => {
    await fetch('/api/admin/delivery-targets', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: t.id, is_backup: !t.is_backup }) });
    load();
  };
  const backupAllTargets = async () => {
    if (!confirm('Alle fertigen Bilder auf alle Backup-Ziele (NAS + markierte Ziele) sichern?')) return;
    notify('Sichere … (kann dauern)');
    const r = await fetch('/api/admin/delivery-targets', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'backup_all' }) }).then((x) => x.json());
    notify(r.items != null ? `${r.mirrored} Kopien auf Backup-Ziele${r.failed ? `, ${r.failed} Fehler` : ''}.` : (r.error || 'OK'));
    load();
  };
  const genToken = async () => {
    await fetch('/api/admin/settings', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ generate_api_token: true }) });
    notify('API-Token erzeugt.'); load();
  };
  const testTarget = async (id: string) => {
    notify('Teste Ziel …');
    const r = await fetch('/api/admin/delivery-targets', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'test', id }) }).then((x) => x.json());
    notify(r.message || (r.ok ? 'OK' : 'Fehler'));
  };
  const fmtBytes = (n: number | null) => n == null ? '—'
    : n > 1e9 ? `${(n / 1e9).toFixed(2)} GB` : n > 1e6 ? `${(n / 1e6).toFixed(1)} MB` : `${Math.round(n / 1e3)} KB`;

  const pairCode = async (userId: string) => {
    const r = await fetch('/api/admin/telegram/pairing-code', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId }) }).then((x) => x.json());
    if (r.code) alert(`Kopplungscode: ${r.code}\n\n15 Minuten gültig. Im Klarbild-Telegram-Chat eingeben.`);
  };
  const unlink = async (chat_id: number) => {
    await fetch('/api/admin/telegram/links', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id }) });
    load();
  };
  useEffect(() => { load(); }, [load]);

  const field = (k: string, v: any) => setS((p: any) => ({ ...p, [k]: v }));
  const saveSettings = async () => {
    const body: any = {
      picdrop_host: s.picdrop_host, picdrop_protocol: s.picdrop_protocol, picdrop_port: s.picdrop_port,
      picdrop_user: s.picdrop_user, picdrop_base_path: s.picdrop_base_path, picdrop_default_gallery: s.picdrop_default_gallery,
      default_dpi: s.default_dpi, default_crop_mode: s.default_crop_mode, concurrency: s.concurrency,
      cricut_sheet_cm: s.cricut_sheet_cm, monthly_budget: s.monthly_budget, n8n_webhook_url: s.n8n_webhook_url,
      keep_sources: !!s.keep_sources, make_thumbnails: s.make_thumbnails !== false, retention_days: s.retention_days,
      metadata_sidecar: !!s.metadata_sidecar,
      nas_enabled: !!s.nas_enabled, nas_host: s.nas_host, nas_protocol: s.nas_protocol, nas_port: s.nas_port,
      nas_user: s.nas_user, nas_base_path: s.nas_base_path,
    };
    if (orKey.trim()) body.openrouter_key = orKey.trim();
    if (pdPw.trim()) body.picdrop_password = pdPw.trim();
    if (nasPw.trim()) body.nas_password = nasPw.trim();
    await fetch('/api/admin/settings', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
    setOrKey(''); setPdPw(''); setNasPw(''); notify('Gespeichert.'); load();
  };
  const testPicdrop = async () => {
    notify('Teste …');
    const r = await fetch('/api/admin/test-picdrop', { method: 'POST' }).then((x) => x.json());
    notify(r.message || (r.ok ? 'OK' : 'Fehler'));
  };
  const testNas = async () => {
    notify('Teste NAS …');
    const r = await fetch('/api/admin/test-nas', { method: 'POST' }).then((x) => x.json());
    notify(r.message || (r.ok ? 'OK' : 'Fehler'));
  };
  const netcheck = async () => {
    if (!s.nas_host) { notify('Erst Host eintragen und speichern.'); return; }
    notify('Prüfe Erreichbarkeit …');
    const r = await fetch('/api/admin/netcheck', { method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ host: s.nas_host, port: s.nas_port || 22 }) }).then((x) => x.json());
    const dnsMsg = r.resolve ? (r.resolve.ok ? `DNS ✓ (${r.resolve.address})` : `DNS ✕ (${r.resolve.error})`) : '';
    const tcpMsg = r.tcp ? (r.tcp.ok ? `Port erreichbar in ${r.tcp.ms}ms ✓` : `nicht erreichbar: ${r.tcp.error}`) : (r.error || 'Fehler');
    notify(`${dnsMsg}${dnsMsg ? ' · ' : ''}${tcpMsg}`);
  };
  const storageAction = async (action: string, confirmMsg?: string) => {
    if (confirmMsg && !confirm(confirmMsg)) return;
    notify('Räume auf …');
    const r = await fetch('/api/admin/storage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action }) }).then((x) => x.json());
    notify(r.deleted != null ? `${r.deleted} alte Positionen entfernt.` : r.purged != null ? `${r.purged} Quelldateien gelöscht.` : (r.error || 'OK'));
    load();
  };
  const mirrorAll = async () => {
    if (!s.nas_enabled) { notify('NAS erst aktivieren & speichern.'); return; }
    if (!confirm('Alle fertigen Bilder aufs NAS sichern?')) return;
    notify('Sichere aufs NAS … (kann dauern)');
    const r = await fetch('/api/admin/storage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'mirror_all' }) }).then((x) => x.json());
    notify(r.total != null ? `${r.mirrored}/${r.total} aufs NAS gesichert${r.failed ? `, ${r.failed} fehlgeschlagen` : ''}.` : (r.error || 'Fehler'));
    load();
  };
  const resetAll = async () => {
    const t = prompt('ACHTUNG: Löscht ALLE Bilder, Aufträge und Ordner unwiderruflich (Nutzer, Einstellungen, Modelle & Rezepte bleiben).\n\nZum Bestätigen RESET eingeben:');
    if (t !== 'RESET') { if (t != null) notify('Abgebrochen — nicht bestätigt.'); return; }
    notify('Setze zurück …');
    const r = await fetch('/api/admin/reset', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ confirm: 'RESET' }) }).then((x) => x.json());
    notify(r.ok ? `Zurückgesetzt: ${r.deleted_items} Bilder entfernt.` : (r.error || 'Fehler'));
    load();
  };
  const addModel = async (m: any) => {
    await fetch('/api/admin/models', { method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ model_id: m.model_id, label: m.name, supports_alpha: m.supports_alpha, active: true }) });
    load();
  };
  const modelAction = async (id: string, action: string) => {
    await fetch('/api/admin/models', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, action }) });
    load();
  };
  const setPw = async (id: string) => {
    const pw = prompt('Neues Passwort?'); if (!pw) return;
    await fetch('/api/admin/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, password: pw }) });
    notify('Passwort gesetzt.');
  };
  const addUser = async () => {
    const username = prompt('Benutzername?'); if (!username) return;
    const password = prompt('Passwort?'); if (!password) return;
    await fetch('/api/admin/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) });
    load();
  };

  if (!s) return <p className="fein">Lädt …</p>;
  const stored = new Set(models.map((m) => m.model_id));

  return (
    <div className="admin">
      {stats && (
        <section className="karte">
          <div className="kopfzeile"><span className="mono-label">Nutzung & Kosten</span></div>
          <div className="zahlen">
            <div><b>{stats.total}</b><span>Bilder gesamt</span></div>
            <div><b>{stats.error_rate}%</b><span>Fehlerquote</span></div>
            <div><b>${stats.cost_30?.toFixed(2)}</b><span>Kosten 30 Tage</span></div>
            <div><b>${stats.cost_all?.toFixed(2)}</b><span>Kosten gesamt</span></div>
          </div>
          <div className="fein">Heute ${stats.cost_today?.toFixed(2)} · 7 Tage ${stats.cost_7?.toFixed(2)}
            {stats.last_activity ? ` · zuletzt ${new Date(stats.last_activity).toLocaleString('de-DE')}` : ''}</div>
        </section>
      )}

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Zugang & Grenzen</span></div>
        <div className="steuer">
          <div className="feld"><label>OpenRouter-Key {s.openrouter_key_set && <em>({s.openrouter_key_masked})</em>}</label>
            <input className="input" placeholder={s.openrouter_key_set ? 'Neuen Key eingeben zum Wechseln' : 'sk-or-…'} value={orKey} onChange={(e) => setOrKey(e.target.value)} />
            <div className="fein">Verschlüsselt gespeichert, hat Vorrang vor der Umgebungsvariable.</div>
          </div>
          <div className="feld"><label>Monatliches Budget ($)</label>
            <input className="input" type="number" step="0.5" value={s.monthly_budget ?? ''} onChange={(e) => field('monthly_budget', e.target.value)} />
            <div className="fein">Bei Erreichen hält die Warteschlange an.</div>
          </div>
          <div className="zwei">
            <div className="feld"><label>Standard-dpi</label><input className="input" type="number" value={s.default_dpi ?? 300} onChange={(e) => field('default_dpi', e.target.value)} /></div>
            <div className="feld"><label>Parallelität</label><input className="input" type="number" value={s.concurrency ?? 2} onChange={(e) => field('concurrency', e.target.value)} /></div>
          </div>
          <div className="feld"><label>Cricut-Bogenmaß (cm)</label><input className="input" value={s.cricut_sheet_cm ?? ''} onChange={(e) => field('cricut_sheet_cm', e.target.value)} /></div>
        </div>
      </section>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Picdrop</span>
          <button className="mini" onClick={testPicdrop}>Verbindung testen</button></div>
        <div className="steuer">
          <div className="zwei">
            <div className="feld"><label>Host</label><input className="input" value={s.picdrop_host ?? ''} onChange={(e) => field('picdrop_host', e.target.value)} /></div>
            <div className="feld"><label>Port</label><input className="input" type="number" value={s.picdrop_port ?? 22} onChange={(e) => field('picdrop_port', e.target.value)} /></div>
          </div>
          <div className="zwei">
            <div className="feld"><label>Protokoll</label>
              <select className="input" value={s.picdrop_protocol ?? 'sftp'} onChange={(e) => field('picdrop_protocol', e.target.value)}>
                <option value="sftp">SFTP (22)</option><option value="ftps">FTPS (21)</option></select></div>
            <div className="feld"><label>Benutzer</label><input className="input" value={s.picdrop_user ?? ''} onChange={(e) => field('picdrop_user', e.target.value)} /></div>
          </div>
          <div className="feld"><label>Passwort {s.picdrop_password_set && <em>(gesetzt)</em>}</label>
            <input className="input" type="password" placeholder={s.picdrop_password_set ? '••••••' : ''} value={pdPw} onChange={(e) => setPdPw(e.target.value)} /></div>
          <div className="zwei">
            <div className="feld"><label>Basisordner (SFTP-Wurzel)</label><input className="input" value={s.picdrop_base_path ?? '/'} onChange={(e) => field('picdrop_base_path', e.target.value)} /></div>
            <div className="feld"><label>Standard-Galerie</label><input className="input" placeholder="POSTER LEA" value={s.picdrop_default_gallery ?? ''} onChange={(e) => field('picdrop_default_gallery', e.target.value)} /></div>
          </div>
          <div className="fein">Bilder landen in <b>Basisordner / Galerie</b> — z. B. <code>/ + POSTER LEA</code>. Pro Ordner lässt sich später eine eigene Galerie mappen.</div>
        </div>
      </section>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Speicher & Aufräumen</span>
          {storage && <span className="fein">{fmtBytes(storage.bytes)} · {storage.results} Ergebnisse · {storage.sources} Quellen · {storage.thumbs} Vorschau</span>}</div>
        <div className="steuer">
          <label className="schalt"><input type="checkbox" checked={s.make_thumbnails !== false} onChange={(e) => field('make_thumbnails', e.target.checked)} />
            <span><b>Vorschaubilder erzeugen</b><em>Kleine, sparsame Bilder für die Bibliotheksvorschau.</em></span></label>
          <label className="schalt"><input type="checkbox" checked={!s.keep_sources} onChange={(e) => field('keep_sources', !e.target.checked)} />
            <span><b>Quellbilder nach Bearbeitung löschen</b><em>Spart Platz — Originale werden nach dem Ergebnis entfernt.</em></span></label>
          <label className="schalt"><input type="checkbox" checked={!!s.metadata_sidecar} onChange={(e) => field('metadata_sidecar', e.target.checked)} />
            <span><b>Metadaten als .md mitschicken</b><em>Zu jedem Bild eine begleitende Textdatei (Prompt, Modell, Format …) bei Picdrop/NAS/FTP.</em></span></label>
          <div className="feld"><label>Aufbewahrung (Tage)</label>
            <input className="input" type="number" min={0} placeholder="leer = unbegrenzt" value={s.retention_days ?? ''} onChange={(e) => field('retention_days', e.target.value)} />
            <div className="fein">Ältere Bilder werden automatisch (täglich) entfernt. Leer = nichts löschen.</div></div>
          <div className="reihe">
            <button className="mini" onClick={() => storageAction('purge_sources', 'Alle Quellbilder fertiger Positionen löschen? Ergebnisse bleiben erhalten.')}>Quellen jetzt löschen</button>
            {s.retention_days ? <button className="mini" onClick={() => storageAction('retention', `Positionen älter als ${s.retention_days} Tage jetzt löschen?`)}>Retention jetzt anwenden</button> : null}
          </div>
        </div>
      </section>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Synology-NAS (zweite Sicherung)</span>
          <div className="reihe"><button className="mini" onClick={netcheck}>Erreichbarkeit</button>
          <button className="mini" onClick={testNas}>Verbindung testen</button></div></div>
        <div className="steuer">
          <label className="schalt"><input type="checkbox" checked={!!s.nas_enabled} onChange={(e) => field('nas_enabled', e.target.checked)} />
            <span><b>Ergebnisse zusätzlich aufs NAS spiegeln</b><em>Jedes fertige Bild wird nach <code>klarbild/JJJJ-MM/</code> gesichert.</em></span></label>
          <div className="zwei">
            <div className="feld"><label>Host</label><input className="input" placeholder="z. B. nas.local oder DDNS" value={s.nas_host ?? ''} onChange={(e) => field('nas_host', e.target.value)} /></div>
            <div className="feld"><label>Port</label><input className="input" type="number" value={s.nas_port ?? 22} onChange={(e) => field('nas_port', e.target.value)} /></div>
          </div>
          <div className="zwei">
            <div className="feld"><label>Protokoll</label>
              <select className="input" value={s.nas_protocol ?? 'sftp'} onChange={(e) => field('nas_protocol', e.target.value)}>
                <option value="sftp">SFTP (22)</option><option value="ftps">FTPS (21)</option></select></div>
            <div className="feld"><label>Benutzer</label><input className="input" value={s.nas_user ?? ''} onChange={(e) => field('nas_user', e.target.value)} /></div>
          </div>
          <div className="feld"><label>Passwort {s.nas_password_set && <em>(gesetzt)</em>}</label>
            <input className="input" type="password" placeholder={s.nas_password_set ? '••••••' : ''} value={nasPw} onChange={(e) => setNasPw(e.target.value)} /></div>
          <div className="feld"><label>Basisordner auf dem NAS</label><input className="input" placeholder="/Klarbild" value={s.nas_base_path ?? ''} onChange={(e) => field('nas_base_path', e.target.value)} /></div>
          <button className="mini" onClick={mirrorAll}>Alle vorhandenen Bilder aufs NAS sichern</button>
          <div className="fein">Ergebnisse landen unter <code>Basisordner / JJJJ-MM /</code>. Zugangsdaten werden verschlüsselt gespeichert.</div>
        </div>
      </section>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Weitere Ausgabeziele (FTP/SFTP)</span>
          <button className="mini" onClick={backupAllTargets}>Alle Bilder sichern</button></div>
        <div className="steuer">
          <div className="fein">Zusätzliche FTP/SFTP-Ziele — z. B. eine zweite Picdrop-Galerie oder ein anderer Server.
            Ein Preset/Ordner kann fest auf ein Ziel zeigen; im Studio unter „Wohin?" wählbar (auch NAS).
            „Backup"-Ziele erhalten automatisch <b>alle</b> Ergebnisse (wie das NAS).</div>
          <div className="liste">
            {targets.map((t) => (
              <div key={t.id} className="zeile">
                <div><b>{t.name}</b>{t.is_backup && <span className="pille">Backup</span>} <span className="fein">{t.protocol}://{t.username}@{t.host}:{t.port} · {t.base_path || '/'}{t.password_set ? '' : ' · ⚠︎ kein Passwort'}</span></div>
                <div className="reihe">
                  <button className="mini" onClick={() => toggleBackup(t)}>{t.is_backup ? 'Backup aus' : 'Als Backup'}</button>
                  <button className="mini" onClick={() => testTarget(t.id)}>Test</button>
                  <button className="mini" onClick={() => delTarget(t.id)}>✕</button>
                </div>
              </div>
            ))}
            {targets.length === 0 && <div className="fein">Noch keine zusätzlichen Ziele.</div>}
          </div>
          <div className="zwei">
            <div className="feld"><label>Name</label><input className="input" placeholder="z. B. The Frame FTP" value={nt.name ?? ''} onChange={(e) => setNt({ ...nt, name: e.target.value })} /></div>
            <div className="feld"><label>Protokoll</label>
              <select className="input" value={nt.protocol} onChange={(e) => setNt({ ...nt, protocol: e.target.value })}>
                <option value="sftp">SFTP</option><option value="ftps">FTPS</option></select></div>
          </div>
          <div className="zwei">
            <div className="feld"><label>Host</label><input className="input" value={nt.host ?? ''} onChange={(e) => setNt({ ...nt, host: e.target.value })} /></div>
            <div className="feld"><label>Port</label><input className="input" type="number" value={nt.port ?? ''} onChange={(e) => setNt({ ...nt, port: e.target.value })} /></div>
          </div>
          <div className="zwei">
            <div className="feld"><label>Benutzer</label><input className="input" value={nt.username ?? ''} onChange={(e) => setNt({ ...nt, username: e.target.value })} /></div>
            <div className="feld"><label>Passwort</label><input className="input" type="password" value={nt.password ?? ''} onChange={(e) => setNt({ ...nt, password: e.target.value })} /></div>
          </div>
          <div className="feld"><label>Basisordner</label><input className="input" placeholder="/" value={nt.base_path ?? ''} onChange={(e) => setNt({ ...nt, base_path: e.target.value })} /></div>
          <label className="schalt"><input type="checkbox" checked={!!nt.is_backup} onChange={(e) => setNt({ ...nt, is_backup: e.target.checked })} />
            <span><b>Als Backup-Ziel</b><em>Erhält automatisch alle Ergebnisse.</em></span></label>
          <button className="mini" onClick={addTarget}>Ziel hinzufügen</button>
        </div>
      </section>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Automatisierung / MCP-Zugriff</span></div>
        <div className="steuer">
          <div className="fein">API-Token für externen/programmatischen Zugriff (z. B. Klarbild-MCP: „nimm diese Bilder und verarbeite sie"). Als <code>Authorization: Bearer &lt;Token&gt;</code> an <code>/api/uploads</code> und <code>/api/jobs</code>.</div>
          {s.api_token
            ? <div className="feld"><label>API-Token</label><input className="input" readOnly value={s.api_token} onFocus={(e) => e.target.select()} /></div>
            : <div className="fein">Noch kein Token erzeugt.</div>}
          <button className="mini" onClick={genToken}>{s.api_token ? 'Neuen Token erzeugen' : 'Token erzeugen'}</button>
        </div>
      </section>

      <button className="knopf" onClick={saveSettings}>Einstellungen speichern</button>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Modelle</span></div>
        <div className="liste">
          {models.map((m) => (
            <div key={m.id} className="zeile">
              <div><b>{m.label}</b>{m.is_default && <span className="pille">Standard</span>}{m.supports_alpha && <span className="pille alpha">transparent</span>}<div className="fein">{m.model_id}</div></div>
              <div className="reihe">
                {!m.is_default && <button className="mini" onClick={() => modelAction(m.id, 'default')}>Standard</button>}
                <button className="mini" onClick={() => modelAction(m.id, 'delete')}>✕</button>
              </div>
            </div>
          ))}
          {models.length === 0 && <div className="fein">Noch keine Modelle. Aus verfügbaren hinzufügen:</div>}
          <div className="avail">
            {avail.filter((m) => !stored.has(m.model_id)).slice(0, 12).map((m) => (
              <button key={m.model_id} className="mini" onClick={() => addModel(m)}>+ {m.name}{m.supports_alpha ? ' ◇' : ''}</button>
            ))}
          </div>
        </div>
      </section>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Nutzer</span><button className="mini" onClick={addUser}>Neuer Nutzer</button></div>
        <div className="liste">
          {users.map((u) => (
            <div key={u.id} className="zeile">
              <div><b>{u.display_name || u.username}</b> <span className="fein">{u.username} · {u.role}</span></div>
              <div className="reihe">
                <button className="mini" onClick={() => pairCode(u.id)}>Telegram koppeln</button>
                <button className="mini" onClick={() => setPw(u.id)}>Passwort setzen</button>
              </div>
            </div>
          ))}
        </div>
      </section>

      <section className="karte">
        <div className="kopfzeile"><span className="mono-label">Telegram-Bot</span></div>
        <div className="liste">
          <div className="fein">Über „Telegram koppeln" bei einem Nutzer einen Code erzeugen und im Chat mit dem Bot eingeben. Danach dort Bilder weiterleiten → Rezept wählen → Ergebnisse kommen zurück.</div>
          {tgLinks.map((l) => (
            <div key={l.chat_id} className="zeile">
              <div><b>Chat {l.chat_id}</b> <span className="fein">{l.user_name || ''} · seit {new Date(l.linked_at).toLocaleDateString('de-DE')}</span></div>
              <button className="mini" onClick={() => unlink(l.chat_id)}>Trennen</button>
            </div>
          ))}
          {tgLinks.length === 0 && <div className="fein">Noch keine gekoppelten Chats.</div>}
        </div>
      </section>

      <section className="karte gefahr">
        <div className="kopfzeile"><span className="mono-label">Zurücksetzen</span></div>
        <div className="steuer">
          <div className="fein">Löscht <b>alle Bilder, Aufträge und Ordner</b> unwiderruflich, um sauber neu zu starten.
            Nutzer, Einstellungen, Modelle und Rezepte bleiben erhalten. Tipp: vorher „Alle Bilder aufs NAS sichern".</div>
          <button className="knopf warn" onClick={resetAll}>Bibliothek &amp; Aufträge zurücksetzen</button>
        </div>
      </section>

      {toast && <div className="toast">{toast}</div>}
      <AdminStyles />
    </div>
  );
}

function AdminStyles() {
  return <style>{`
    .admin{display:flex;flex-direction:column;gap:16px;max-width:640px;}
    .karte{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);}
    .kopfzeile{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--line);}
    .steuer{padding:16px;display:flex;flex-direction:column;gap:14px;}
    .zwei{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
    .feld label{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--soft);margin-bottom:6px;}
    .feld em{text-transform:none;letter-spacing:0;color:var(--accent);font-style:normal;}
    .input{width:100%;background:#fff;border:1px solid var(--line);border-radius:3px;padding:9px 11px;font-family:inherit;font-size:14px;color:var(--ink);}
    .fein{font-size:11.5px;color:var(--soft);line-height:1.5;}
    .knopf{border:none;border-radius:3px;padding:12px;cursor:pointer;background:var(--accent);color:#fff;font-family:inherit;font-weight:600;font-size:15px;}
    .knopf.warn{background:var(--err);}
    .karte.gefahr{border-color:color-mix(in oklab,var(--err) 40%,var(--line));}
    .karte.gefahr .mono-label{color:var(--err);}
    .steuer code{font-family:var(--font-mono);font-size:11px;background:var(--paper);border:1px solid var(--line);border-radius:3px;padding:1px 5px;}
    .zahlen{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;padding:16px;}
    .zahlen div{background:var(--paper);border:1px solid var(--line);border-radius:3px;padding:11px;}
    .zahlen b{display:block;font-size:22px;line-height:1;}.zahlen span{font-size:11.5px;color:var(--soft);}
    .karte>.fein{padding:0 16px 14px;}
    .liste{padding:8px 16px 16px;display:flex;flex-direction:column;}
    .zeile{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--line);}
    .zeile b{font-size:14px;}
    .pille{font-family:var(--font-mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;background:var(--accent-bg);color:var(--accent);padding:2px 7px;border-radius:20px;margin-left:6px;}
    .pille.alpha{background:var(--paper);color:var(--soft);}
    .schalt{display:flex;gap:10px;align-items:flex-start;cursor:pointer;}
    .schalt input{margin-top:3px;width:16px;height:16px;accent-color:var(--accent);flex:0 0 auto;}
    .schalt b{display:block;font-size:14px;}
    .schalt em{display:block;font-style:normal;font-size:11.5px;color:var(--soft);margin-top:1px;line-height:1.4;}
    .schalt code{font-family:var(--font-mono);font-size:11px;}
    .avail{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px;}
    .reihe{display:flex;gap:6px;}
    .mini{background:var(--card);border:1px solid var(--line);border-radius:3px;padding:6px 10px;cursor:pointer;font-family:inherit;font-size:12px;color:var(--ink);}
    .toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%);background:var(--ink);color:#FBFBF7;padding:10px 18px;border-radius:3px;font-size:13.5px;z-index:60;}
  `}</style>;
}
