// storage.jsx — Cloud-Anbindung an hausgut-server (Hetzner Object Storage)
// Stellt useAuth, useDocuments, uploadDocument, deleteDocument, downloadDocument
// als globale Helfer bereit. Greift via fetch + Cookie-Auth auf das Backend zu.

const API_BASE = (window.HAUSGUT_API_URL || 'http://localhost:8080').replace(/\/$/, '');

async function apiFetch(path, opts = {}) {
  const res = await fetch(`${API_BASE}${path}`, { credentials: 'include', ...opts });
  if (res.status === 401) {
    window.dispatchEvent(new CustomEvent('hausgut:unauth'));
    throw new Error('unauthenticated');
  }
  return res;
}

async function apiJson(path, opts = {}) {
  const res = await apiFetch(path, opts);
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    throw new Error(`API ${res.status}: ${text || res.statusText}`);
  }
  return res.json();
}

// ── Auth ──────────────────────────────────────────────────────────────
async function checkAuth() {
  try {
    const res = await fetch(`${API_BASE}/api/auth/me`, { credentials: 'include' });
    if (!res.ok) return false;
    const data = await res.json();
    return !!data.authenticated;
  } catch { return false; }
}

async function login(password) {
  const res = await fetch(`${API_BASE}/api/auth/login`, {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ password }),
  });
  if (!res.ok) throw new Error(res.status === 401 ? 'invalid_password' : `login_failed_${res.status}`);
  return true;
}

async function logout() {
  await apiFetch('/api/auth/logout', { method: 'POST' }).catch(() => {});
}

function useAuth() {
  const [state, setState] = React.useState({ loading: true, authenticated: false });
  const refresh = React.useCallback(async () => {
    const authed = await checkAuth();
    setState({ loading: false, authenticated: authed });
  }, []);
  React.useEffect(() => {
    refresh();
    const onUnauth = () => setState({ loading: false, authenticated: false });
    window.addEventListener('hausgut:unauth', onUnauth);
    return () => window.removeEventListener('hausgut:unauth', onUnauth);
  }, [refresh]);
  return {
    ...state,
    login: async (pw) => { await login(pw); setState({ loading: false, authenticated: true }); },
    logout: async () => { await logout(); setState({ loading: false, authenticated: false }); },
  };
}

// ── Documents ─────────────────────────────────────────────────────────
async function listDocuments({ propertyId, unitId, scope } = {}) {
  const qs = new URLSearchParams();
  if (propertyId) qs.set('propertyId', propertyId);
  if (unitId)     qs.set('unitId', unitId);
  if (scope)      qs.set('scope', scope);
  const suffix = qs.toString() ? `?${qs}` : '';
  return apiJson(`/api/documents${suffix}`);
}

async function uploadDocument(file, meta = {}) {
  const fd = new FormData();
  fd.append('file', file);
  if (meta.category)      fd.append('category', meta.category);
  if (meta.propertyId)    fd.append('propertyId', meta.propertyId);
  if (meta.unitId)        fd.append('unitId', meta.unitId);
  if (meta.scope)         fd.append('scope', meta.scope);
  if (meta.transactionId) fd.append('transactionId', meta.transactionId);
  const res = await apiFetch('/api/uploads', { method: 'POST', body: fd });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    throw new Error(`upload_failed: ${text || res.statusText}`);
  }
  return res.json();
}

async function deleteDocument(id) {
  const res = await apiFetch(`/api/documents/${id}`, { method: 'DELETE' });
  if (!res.ok) throw new Error(`delete_failed_${res.status}`);
  return true;
}

function downloadDocumentUrl(id) {
  return `${API_BASE}/api/documents/${id}/download`;
}

function viewDocumentUrl(id) {
  return `${API_BASE}/api/documents/${id}/view`;
}

// Link / unlink a document to/from a transaction
async function linkDocumentToTransaction(documentId, transactionId) {
  const res = await apiFetch(`/api/documents/${documentId}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ transactionId: transactionId || null }),
  });
  if (!res.ok) throw new Error(`link_failed_${res.status}`);
  return res.json();
}

// List documents linked to a transaction
async function listDocumentsForTransaction(transactionId) {
  return apiJson(`/api/transactions/${transactionId}/documents`);
}

// Build URL for monthly Steuerberater-ZIP (browser navigates to download)
function monthlyExportZipUrl({ year, month, propertyId }) {
  const qs = new URLSearchParams({ year: String(year), month: String(month) });
  if (propertyId) qs.set('propertyId', propertyId);
  return `${API_BASE}/api/exports/monthly-zip?${qs}`;
}

function datevExportUrl({ year, propertyId }) {
  const qs = new URLSearchParams({ year: String(year) });
  if (propertyId) qs.set('propertyId', propertyId);
  return `${API_BASE}/api/exports/datev-csv?${qs}`;
}

async function getAfaSummary({ year, propertyId } = {}) {
  const qs = new URLSearchParams();
  if (year) qs.set('year', String(year));
  if (propertyId) qs.set('propertyId', propertyId);
  return apiJson(`/api/afa-summary${qs.toString() ? '?' + qs : ''}`);
}

async function getAnlageV({ year, propertyId } = {}) {
  const qs = new URLSearchParams();
  if (year) qs.set('year', String(year));
  if (propertyId) qs.set('propertyId', propertyId);
  return apiJson(`/api/anlage-v${qs.toString() ? '?' + qs : ''}`);
}

async function getRentStatus({ year } = {}) {
  const qs = new URLSearchParams();
  if (year) qs.set('year', String(year));
  return apiJson(`/api/rent-status${qs.toString() ? '?' + qs : ''}`);
}

// ── Stammdaten CRUD (properties / units / tenants / transactions) ────

async function listResource(path) {
  const res = await apiFetch(path);
  if (!res.ok) throw new Error(`${path} (${res.status})`);
  return res.json();
}

async function createResource(path, body) {
  const res = await apiFetch(path, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const t = await res.text().catch(() => '');
    throw new Error(`${path} POST ${res.status}: ${t || res.statusText}`);
  }
  return res.json();
}

async function updateResource(path, body) {
  const res = await apiFetch(path, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const t = await res.text().catch(() => '');
    throw new Error(`${path} PUT ${res.status}: ${t || res.statusText}`);
  }
  return res.json();
}

async function deleteResource(path) {
  const res = await apiFetch(path, { method: 'DELETE' });
  if (!res.ok) throw new Error(`${path} DELETE ${res.status}`);
  return true;
}

// Single store hook that fetches all stammdaten + provides CRUD operations.
// Replaces window.DEMO_DATA. Returns a `data`-shaped object compatible with
// the existing pages (properties[].units[], tenants{}, transactions[]).
function useDataStore() {
  const [state, setState] = React.useState({
    loading: true, error: null,
    properties: [], unitsRaw: [], tenantsRaw: [], transactions: [], documents: [],
    contacts: [], maintenance: [], reminders: [], settings: {}, assets: [], tasks: [],
  });

  // Only the very first load shows the full-screen "Lade Daten…" — every
  // subsequent reload (after add/update/delete) updates state silently in the
  // background. Without this, every save flashed the loading screen and felt
  // like a manual refresh.
  const reload = React.useCallback(async () => {
    setState(s => ({ ...s, error: null }));
    try {
      const [properties, units, tenants, transactions, contacts, maintenance, reminders, settings, assets, tasks] = await Promise.all([
        listResource('/api/properties'),
        listResource('/api/units'),
        listResource('/api/tenants'),
        listResource('/api/transactions'),
        listResource('/api/contacts'),
        listResource('/api/maintenance'),
        listResource('/api/reminders'),
        listResource('/api/settings'),
        listResource('/api/assets'),
        listResource('/api/tasks'),
      ]);
      setState({ loading: false, error: null, properties, unitsRaw: units, tenantsRaw: tenants, transactions, documents: [], contacts, maintenance, reminders, settings, assets, tasks });
    } catch (err) {
      setState(s => ({ ...s, loading: false, error: err.message }));
    }
  }, []);

  React.useEffect(() => { reload(); }, [reload]);

  // Re-shape into the same structure pages already expect:
  // properties[i].units[] (nested), tenants as { id: tenant } map
  const data = React.useMemo(() => {
    const tenantsMap = {};
    state.tenantsRaw.forEach(t => { tenantsMap[t.id] = t; });
    const propertiesNested = state.properties.map(p => ({
      ...p,
      units: state.unitsRaw.filter(u => u.propertyId === p.id).map(u => ({ ...u, tenant: u.tenantId })),
    }));
    return {
      properties: propertiesNested,
      tenants: tenantsMap,
      transactions: state.transactions,
      documents: state.documents,
      contacts: state.contacts,
      maintenance: state.maintenance,
      reminders: state.reminders,
      settings: state.settings || {},
      assets: state.assets || [],
      tasks: state.tasks || [],
      meterReadings: [],     // not implemented yet
    };
  }, [state]);

  return {
    loading: state.loading,
    error: state.error,
    data,
    reload,
    // Properties
    addProperty: async (body) => { const p = await createResource('/api/properties', body); await reload(); return p; },
    updateProperty: async (id, body) => { const p = await updateResource(`/api/properties/${id}`, body); await reload(); return p; },
    deleteProperty: async (id) => { await deleteResource(`/api/properties/${id}`); await reload(); },
    // Units
    addUnit: async (body) => { const u = await createResource('/api/units', body); await reload(); return u; },
    updateUnit: async (id, body) => { const u = await updateResource(`/api/units/${id}`, body); await reload(); return u; },
    deleteUnit: async (id) => { await deleteResource(`/api/units/${id}`); await reload(); },
    // Tenants
    addTenant: async (body) => { const t = await createResource('/api/tenants', body); await reload(); return t; },
    updateTenant: async (id, body) => { const t = await updateResource(`/api/tenants/${id}`, body); await reload(); return t; },
    deleteTenant: async (id) => { await deleteResource(`/api/tenants/${id}`); await reload(); },
    // Transactions
    addTransaction: async (body) => { const t = await createResource('/api/transactions', body); await reload(); return t; },
    updateTransaction: async (id, body) => { const t = await updateResource(`/api/transactions/${id}`, body); await reload(); return t; },
    deleteTransaction: async (id) => { await deleteResource(`/api/transactions/${id}`); await reload(); },
    // Contacts
    addContact: async (body) => { const c = await createResource('/api/contacts', body); await reload(); return c; },
    updateContact: async (id, body) => { const c = await updateResource(`/api/contacts/${id}`, body); await reload(); return c; },
    deleteContact: async (id) => { await deleteResource(`/api/contacts/${id}`); await reload(); },
    // Maintenance
    addMaintenance: async (body) => { const m = await createResource('/api/maintenance', body); await reload(); return m; },
    updateMaintenance: async (id, body) => { const m = await updateResource(`/api/maintenance/${id}`, body); await reload(); return m; },
    deleteMaintenance: async (id) => { await deleteResource(`/api/maintenance/${id}`); await reload(); },
    // Reminders
    addReminder: async (body) => { const r = await createResource('/api/reminders', body); await reload(); return r; },
    updateReminder: async (id, body) => { const r = await updateResource(`/api/reminders/${id}`, body); await reload(); return r; },
    deleteReminder: async (id) => { await deleteResource(`/api/reminders/${id}`); await reload(); },
    // Settings
    updateSettings: async (body) => { const s = await updateResource('/api/settings', body); await reload(); return s; },
    // Assets (AfA)
    addAsset:    async (body) => { const a = await createResource('/api/assets', body); await reload(); return a; },
    updateAsset: async (id, body) => { const a = await updateResource(`/api/assets/${id}`, body); await reload(); return a; },
    deleteAsset: async (id) => { await deleteResource(`/api/assets/${id}`); await reload(); },
    // Tasks
    addTask:    async (body) => { const t = await createResource('/api/tasks', body); await reload(); return t; },
    updateTask: async (id, body) => { const t = await updateResource(`/api/tasks/${id}`, body); await reload(); return t; },
    deleteTask: async (id) => { await deleteResource(`/api/tasks/${id}`); await reload(); },
  };
}

// ── CSV Import for bank transactions ──────────────────────────────────
async function importTransactionsCsv(file, { propertyId, unitId, dryRun, autoCategorize } = {}) {
  const fd = new FormData();
  fd.append('file', file);
  if (propertyId) fd.append('propertyId', propertyId);
  if (unitId) fd.append('unitId', unitId);
  if (dryRun) fd.append('dryRun', 'true');
  if (autoCategorize === false) fd.append('autoCategorize', 'false');
  const res = await apiFetch('/api/transactions/import', { method: 'POST', body: fd });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    let err;
    try { err = JSON.parse(text); } catch { err = { error: 'import_failed', detail: text }; }
    throw Object.assign(new Error(err.detail || err.error || 'import_failed'), err);
  }
  return res.json();
}

// Start an async categorize job. Returns { jobId, total, done, ... }.
// Use getCategorizeStatus(jobId) to poll progress.
async function categorizeTransactions(transactionIds) {
  const res = await apiFetch('/api/transactions/categorize', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ transactionIds: transactionIds || null }),
  });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    throw new Error(`categorize_failed: ${text || res.statusText}`);
  }
  return res.json();
}

// ── KI-Berater: Insights + Chat ──────────────────────────────────────
async function getAiInsights(force) {
  const res = await apiFetch('/api/ai/insights', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ force: !!force }),
  });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    let err; try { err = JSON.parse(text); } catch { err = { error: text || 'failed' }; }
    throw Object.assign(new Error(err.detail || err.error || 'insights_failed'), err);
  }
  return res.json();
}

async function aiChat(message, history) {
  const res = await apiFetch('/api/ai/chat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message, history }),
  });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    let err; try { err = JSON.parse(text); } catch { err = { error: text || 'failed' }; }
    throw Object.assign(new Error(err.detail || err.error || 'chat_failed'), err);
  }
  return res.json();
}

// Get backend health (incl. ocr.enabled) — used to display KI connection status
async function getHealth() {
  const res = await fetch(`${API_BASE}/api/health`);
  if (!res.ok) throw new Error('health_failed');
  return res.json();
}

async function getCategorizeStatus(jobId) {
  const res = await apiFetch(`/api/transactions/categorize/status/${jobId}`);
  if (!res.ok) {
    if (res.status === 404) return { error: 'job_not_found', finished: true };
    throw new Error(`status_failed: ${res.statusText}`);
  }
  return res.json();
}

// ── OCR: send file to /api/ocr → structured suggestion ────────────────
async function analyzeFile(file) {
  const fd = new FormData();
  fd.append('file', file);
  const res = await apiFetch('/api/ocr', { method: 'POST', body: fd });
  if (!res.ok) {
    const text = await res.text().catch(() => '');
    throw new Error(`ocr_failed (${res.status}): ${text || res.statusText}`);
  }
  return res.json();
}

// React hook: list + auto-refresh after upload/delete
function useDocuments(filter = {}) {
  const filterKey = JSON.stringify(filter);
  const [state, setState] = React.useState({ loading: true, error: null, items: [] });
  const reload = React.useCallback(async () => {
    setState(s => ({ ...s, loading: true, error: null }));
    try {
      const items = await listDocuments(filter);
      setState({ loading: false, error: null, items });
    } catch (err) {
      setState({ loading: false, error: err.message, items: [] });
    }
  // filterKey serializes the filter into a stable dep so the callback isn't recreated each render
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [filterKey]);
  React.useEffect(() => { reload(); }, [reload]);
  return {
    ...state,
    reload,
    upload: async (file, meta) => { const doc = await uploadDocument(file, { ...filter, ...meta }); await reload(); return doc; },
    remove: async (id) => { await deleteDocument(id); await reload(); },
  };
}

// ── Login screen ──────────────────────────────────────────────────────
function LoginGate({ onSuccess }) {
  const [pw, setPw] = React.useState('');
  const [err, setErr] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const submit = async (e) => {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      await login(pw);
      onSuccess?.();
    } catch (e) {
      setErr(e.message === 'invalid_password' ? 'Falsches Passwort.' : 'Anmeldung fehlgeschlagen. Backend erreichbar?');
    } finally {
      setBusy(false);
    }
  };
  return (
    <div style={{ minHeight: '100vh', display: 'grid', placeItems: 'center', background: 'var(--bg, #fafaf7)' }}>
      <form onSubmit={submit} style={{ width: 320, padding: 32, background: 'var(--bg-elev, #fff)', border: '1px solid var(--line, #e6e3da)', borderRadius: 8 }}>
        <div style={{ fontFamily: 'var(--font-serif, Georgia, serif)', fontSize: 28, marginBottom: 8 }}>Hausgut</div>
        <div style={{ color: 'var(--ink-3, #6b6a64)', fontSize: 13, marginBottom: 24 }}>Anmelden, um Dokumente zu verwalten.</div>
        <input
          type="password" autoFocus value={pw} onChange={e => setPw(e.target.value)}
          placeholder="Passwort"
          style={{ width: '100%', padding: '10px 12px', border: '1px solid var(--line, #e6e3da)', borderRadius: 6, fontSize: 14, marginBottom: 12 }}
        />
        {err && <div style={{ color: 'var(--neg, #8b3a2f)', fontSize: 12, marginBottom: 12 }}>{err}</div>}
        <button type="submit" disabled={busy || !pw}
          style={{ width: '100%', padding: '10px 12px', border: 0, borderRadius: 6, background: 'var(--accent, #2d4a3e)', color: 'var(--accent-ink, #fff)', fontSize: 14, fontWeight: 500, cursor: 'pointer', opacity: busy ? 0.6 : 1 }}>
          {busy ? 'Anmelden…' : 'Anmelden'}
        </button>
        <div style={{ marginTop: 16, fontSize: 11, color: 'var(--ink-4, #9a9890)', textAlign: 'center' }}>
          Backend: {API_BASE}
        </div>
      </form>
    </div>
  );
}

// ── Document categories & scopes ──────────────────────────────────────
const DOC_CATEGORIES = [
  'Mietvertrag', 'Kündigung', 'Übergabeprotokoll',
  'Rechnung', 'Beleg', 'Nebenkostenabrechnung',
  'Versicherung', 'Eigentum / Grundbuch',
  'Mahnung', 'Modernisierung', 'Sonstiges',
];

// ── Upload modal: ask for kategorie + scope + kommentar before upload ─
function UploadModal({ files, defaultMeta = {}, propertyContext, unitContext, onConfirm, onCancel, properties }) {
  const [category, setCategory] = React.useState(defaultMeta.category || '');
  const [scope, setScope] = React.useState(defaultMeta.scope || (unitContext ? 'einheit' : 'allgemein'));
  // When no propertyContext is fixed AND a properties list is provided,
  // the modal shows a property/unit picker so the user assigns the upload.
  const showPicker = !propertyContext && Array.isArray(properties);
  const [pickerPropertyId, setPickerPropertyId] = React.useState(defaultMeta.propertyId || '');
  const [pickerUnitId, setPickerUnitId] = React.useState(defaultMeta.unitId || '');
  const pickerProp = (properties || []).find(p => p.id === pickerPropertyId);
  const pickerUnits = pickerProp?.units || [];
  const [comment, setComment] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [progress, setProgress] = React.useState({ done: 0, total: files.length });
  const [ocrBusy, setOcrBusy] = React.useState(false);
  const [ocrResult, setOcrResult] = React.useState(null);
  const [ocrError, setOcrError] = React.useState(null);

  const runOcr = async () => {
    if (!files[0]) return;
    setOcrBusy(true); setOcrError(null); setOcrResult(null);
    try {
      const r = await analyzeFile(files[0]);
      setOcrResult(r);
      // Auto-apply suggestions (user can still adjust)
      if (r.category && DOC_CATEGORIES.includes(r.category)) setCategory(r.category);
      if (r.scope_suggestion === 'allgemein' || r.scope_suggestion === 'einheit') {
        // only apply scope='einheit' if the unit context allows it
        if (r.scope_suggestion === 'einheit' && !unitContext) setScope('allgemein');
        else setScope(r.scope_suggestion);
      }
      const noteParts = [];
      if (r.summary) noteParts.push(r.summary);
      if (r.supplier) noteParts.push(`Aussteller: ${r.supplier}`);
      if (r.date) noteParts.push(`Datum: ${r.date}`);
      if (typeof r.amount_eur === 'number') noteParts.push(`Betrag: ${r.amount_eur.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })}`);
      setComment(noteParts.join(' · '));
    } catch (err) {
      setOcrError(err.message.includes('ocr_disabled') ? 'OpenAI-Key fehlt in der .env (OPENAI_API_KEY).' : err.message);
    } finally {
      setOcrBusy(false);
    }
  };

  const submit = async (e) => {
    e?.preventDefault();
    if (!category) return;
    setBusy(true); setProgress({ done: 0, total: files.length });
    try {
      for (let i = 0; i < files.length; i++) {
        await uploadDocument(files[i], {
          ...defaultMeta,
          category,
          scope,
          // Picker values win over defaultMeta when picker is shown
          ...(showPicker ? { propertyId: pickerPropertyId || null, unitId: pickerUnitId || null } : {}),
        });
        setProgress({ done: i + 1, total: files.length });
      }
      onConfirm?.();
    } catch (err) {
      alert('Upload fehlgeschlagen: ' + err.message);
      setBusy(false);
    }
  };

  return (
    <div className="upload-modal-backdrop" onClick={busy ? null : onCancel}>
      <form className="upload-modal" onClick={e => e.stopPropagation()} onSubmit={submit}>
        <div className="upload-modal-head">
          <h3>{files.length} {files.length === 1 ? 'Datei' : 'Dateien'} hochladen</h3>
          {!busy && <button type="button" className="upload-modal-close" onClick={onCancel} aria-label="Schließen">✕</button>}
        </div>

        <div className="upload-modal-files">
          {files.map((f, i) => (
            <div key={i} className="upload-modal-file">
              <span className="upload-modal-file-icon">📄</span>
              <span className="upload-modal-file-name">{f.name}</span>
              <span className="upload-modal-file-size">{(f.size / 1024).toFixed(0)} KB</span>
            </div>
          ))}
        </div>

        {(propertyContext || unitContext) && (
          <div className="upload-modal-context">
            {propertyContext && <span>📍 {propertyContext}</span>}
            {unitContext && <span> · {unitContext}</span>}
          </div>
        )}

        {showPicker && (
          <div className="upload-modal-field">
            <label>Objekt zuordnen</label>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
              <select
                value={pickerPropertyId}
                onChange={e => { setPickerPropertyId(e.target.value); setPickerUnitId(''); }}
                disabled={busy}
              >
                <option value="">— kein Objekt —</option>
                {properties.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
              </select>
              <select
                value={pickerUnitId}
                onChange={e => setPickerUnitId(e.target.value)}
                disabled={busy || !pickerPropertyId}
              >
                <option value="">{pickerPropertyId ? '— Gesamtobjekt / allgemein —' : '— erst Objekt wählen —'}</option>
                {pickerUnits.map(u => <option key={u.id} value={u.id}>{u.label}</option>)}
              </select>
            </div>
          </div>
        )}

        <div className="upload-modal-ocr">
          {!ocrResult && !ocrError && (
            <button type="button" className="upload-modal-ocr-btn" onClick={runOcr} disabled={busy || ocrBusy}>
              {ocrBusy ? '🔍 Analysiere…' : `🔍 Mit KI vorschlagen${files.length > 1 ? ` (erste Datei)` : ''}`}
            </button>
          )}
          {ocrResult && (
            <div className="upload-modal-ocr-result">
              <div className="upload-modal-ocr-head">
                ✨ KI-Vorschlag <span className="upload-modal-ocr-conf">({Math.round(ocrResult.confidence * 100)} % sicher)</span>
              </div>
              <div className="upload-modal-ocr-body">{ocrResult.summary}</div>
              <button type="button" className="upload-modal-ocr-redo" onClick={runOcr} disabled={ocrBusy}>↻ erneut</button>
            </div>
          )}
          {ocrError && (
            <div className="upload-modal-ocr-error">⚠ {ocrError}</div>
          )}
        </div>

        <div className="upload-modal-field">
          <label>Kategorie *</label>
          <select value={category} onChange={e => setCategory(e.target.value)} required disabled={busy}>
            <option value="" disabled>— bitte wählen —</option>
            {DOC_CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
          </select>
        </div>

        <div className="upload-modal-field">
          <label>Zuordnung</label>
          <div className="upload-modal-radios">
            <label className={scope === 'allgemein' ? 'active' : ''}>
              <input type="radio" name="scope" value="allgemein" checked={scope === 'allgemein'} onChange={e => setScope(e.target.value)} disabled={busy} />
              <div>
                <div className="upload-modal-radio-title">Allgemein</div>
                <div className="upload-modal-radio-sub">Gilt fürs ganze Objekt (z.B. Versicherung, Grundbuch)</div>
              </div>
            </label>
            <label className={scope === 'einheit' ? 'active' : ''}>
              <input type="radio" name="scope" value="einheit" checked={scope === 'einheit'} onChange={e => setScope(e.target.value)} disabled={busy || !unitContext} />
              <div>
                <div className="upload-modal-radio-title">Einheit{!unitContext ? ' (nur in Wohnungs-Ansicht)' : ''}</div>
                <div className="upload-modal-radio-sub">Direkt einer Wohnung/Mieter zugeordnet</div>
              </div>
            </label>
          </div>
        </div>

        <div className="upload-modal-field">
          <label>Notiz <span style={{ color: 'var(--ink-4)', fontWeight: 'normal', fontSize: 11 }}>(optional, für dich)</span></label>
          <input type="text" value={comment} onChange={e => setComment(e.target.value)} placeholder="z.B. Original liegt im Aktenordner 2026/Q2" disabled={busy} />
        </div>

        <div className="upload-modal-actions">
          {busy ? (
            <div className="upload-modal-progress">Hochladen… {progress.done} / {progress.total}</div>
          ) : (
            <>
              <button type="button" className="btn btn-ghost" onClick={onCancel}>Abbrechen</button>
              <button type="submit" className="btn btn-primary" disabled={!category}>
                {files.length > 1 ? `${files.length} Dateien hochladen` : 'Hochladen'}
              </button>
            </>
          )}
        </div>
      </form>
    </div>
  );
}

// ── Drop zone with click-to-pick + drag-and-drop, opens UploadModal ──
function DropZone({ accept = '.pdf,.jpg,.jpeg,.png,.heic,.docx,.xlsx', meta = {}, propertyContext, unitContext, onUpload, label = 'Dateien hierher ziehen oder klicken zum Auswählen', subLabel = 'PDF, JPG, PNG, DOCX, XLSX', compact = false, properties }) {
  const inputRef = React.useRef(null);
  const [files, setFiles] = React.useState(null);
  const [hover, setHover] = React.useState(false);

  const onPick = (selected) => {
    const arr = Array.from(selected || []).filter(Boolean);
    if (arr.length) setFiles(arr);
  };

  const onDrop = (e) => {
    e.preventDefault();
    setHover(false);
    onPick(e.dataTransfer?.files);
  };

  return (
    <>
      <div
        className={`drop-zone ${hover ? 'hover' : ''} ${compact ? 'compact' : ''}`}
        onClick={() => inputRef.current?.click()}
        onDragOver={e => { e.preventDefault(); setHover(true); }}
        onDragLeave={() => setHover(false)}
        onDrop={onDrop}
      >
        <div className="drop-zone-icon">↑</div>
        <div className="drop-zone-label">{label}</div>
        {subLabel && <div className="drop-zone-sub">{subLabel}</div>}
        <input ref={inputRef} type="file" multiple accept={accept} style={{ display: 'none' }} onChange={e => onPick(e.target.files)} />
      </div>
      {files && (
        <UploadModal
          files={files}
          defaultMeta={meta}
          properties={properties}
          propertyContext={propertyContext}
          unitContext={unitContext}
          onConfirm={() => { setFiles(null); onUpload?.(); if (inputRef.current) inputRef.current.value = ''; }}
          onCancel={() => { setFiles(null); if (inputRef.current) inputRef.current.value = ''; }}
        />
      )}
    </>
  );
}

// ── Reusable button that opens the same modal (for header/inline use) ──
function UploadButton({ onUpload, accept = '.pdf,.jpg,.jpeg,.png,.heic,.docx,.xlsx', label = 'Hochladen', meta = {}, propertyContext, unitContext, className = 'btn', properties }) {
  const inputRef = React.useRef(null);
  const [files, setFiles] = React.useState(null);
  const onPick = (selected) => {
    const arr = Array.from(selected || []).filter(Boolean);
    if (arr.length) setFiles(arr);
  };
  return (
    <>
      <button type="button" className={className} onClick={() => inputRef.current?.click()}>{label}</button>
      <input ref={inputRef} type="file" multiple accept={accept} style={{ display: 'none' }} onChange={e => onPick(e.target.files)} />
      {files && (
        <UploadModal
          files={files}
          defaultMeta={meta}
          properties={properties}
          propertyContext={propertyContext}
          unitContext={unitContext}
          onConfirm={() => { setFiles(null); onUpload?.(); if (inputRef.current) inputRef.current.value = ''; }}
          onCancel={() => { setFiles(null); if (inputRef.current) inputRef.current.value = ''; }}
        />
      )}
    </>
  );
}

Object.assign(window, {
  HAUSGUT_API_BASE: API_BASE,
  useAuth, useDocuments,
  uploadDocument, deleteDocument, listDocuments, downloadDocumentUrl, viewDocumentUrl,
  useDataStore, importTransactionsCsv, categorizeTransactions, getCategorizeStatus,
  getAiInsights, aiChat, getHealth,
  linkDocumentToTransaction, listDocumentsForTransaction, monthlyExportZipUrl,
  datevExportUrl, getAfaSummary, getAnlageV, getRentStatus,
  LoginGate, UploadButton, DropZone, UploadModal,
  DOC_CATEGORIES,
});
