/**
 * AdminDatabaseHealthPage
 *
 * SysAdmin-only page with three tabs:
 *   1. Table Statistics — pg_stat data for every table across all databases
 *   2. Orphan Cleanup   — FK orphan detection with per-relation + batch cleanup
 *   3. Migration        — Database backup (export) and restore (import)
 *
 * Both Stats/Orphan tabs use FormGeneratorTable with a client-side pagination/sort/filter
 * adapter (the backend returns all rows at once; the dataset is small enough).
 */

import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import { FaSync, FaTrashAlt, FaBroom, FaExclamationTriangle, FaDownload, FaUpload, FaDatabase, FaInfoCircle, FaCheckCircle } from 'react-icons/fa';
import api from '../../api';
import styles from './Admin.module.css';
import { useLanguage } from '../../providers/language/LanguageContext';
import { useToast } from '../../contexts/ToastContext';
import { useConfirm } from '../../hooks/useConfirm';
import { Tabs } from '../../components/UiComponents/Tabs/Tabs';
import { FormGeneratorTable, type ColumnConfig } from '../../components/FormGenerator/FormGeneratorTable';


// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

interface TableStat {
  id: string;
  db: string;
  table: string;
  estimatedRows: number;
  totalSizeBytes: number;
  indexSizeBytes: number;
  lastVacuum: string | null;
  lastAnalyze: string | null;
}

interface OrphanEntry {
  id: string;
  sourceDb: string;
  sourceTable: string;
  sourceColumn: string;
  targetDb: string;
  targetTable: string;
  targetColumn: string;
  orphanCount: number;
  sourceRowCount?: number;
  targetRowCount?: number;
  targetEmpty?: boolean;
  wouldDeleteAll?: boolean;
}

interface CleanResult {
  db: string;
  table: string;
  column: string;
  deleted: number;
  error?: string;
  skipped?: string;
}

interface PaginationParams {
  page?: number;
  pageSize?: number;
  search?: string;
  filters?: Record<string, any>;
  sort?: Array<{ field: string; direction: 'asc' | 'desc' }>;
}

interface PaginationMeta {
  currentPage: number;
  pageSize: number;
  totalItems: number;
  totalPages: number;
}


// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function _formatBytes(bytes: number): string {
  if (bytes === 0) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(1024));
  const value = bytes / Math.pow(1024, i);
  return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
}

function _formatNumber(n: number): string {
  return n.toLocaleString('de-CH');
}


// ---------------------------------------------------------------------------
// useClientPagination — adapts a static array to FormGeneratorTable's
// hookData.refetch / hookData.pagination contract.
// ---------------------------------------------------------------------------

function _useClientPagination<T extends Record<string, any>>(allData: T[]) {
  const [visibleData, setVisibleData] = useState<T[]>([]);
  const [pagination, setPagination] = useState<PaginationMeta>({
    currentPage: 1, pageSize: 50, totalItems: 0, totalPages: 1,
  });

  const allDataRef = useRef(allData);
  allDataRef.current = allData;

  const lastParamsRef = useRef<PaginationParams>({});

  const fetchFilterValues = useCallback(async (columnKey: string, crossFilters?: Record<string, any>) => {
    let source = allDataRef.current;
    if (crossFilters && Object.keys(crossFilters).length > 0) {
      source = source.filter(row => {
        for (const [key, val] of Object.entries(crossFilters)) {
          if (val === undefined || val === null || val === '') continue;
          const cell = String(row[key] ?? '');
          if (Array.isArray(val)) {
            if (val.length > 0 && !val.includes(cell)) return false;
          } else {
            if (cell !== String(val)) return false;
          }
        }
        return true;
      });
    }
    const seen = new Set<string>();
    for (const row of source) {
      const v = row[columnKey];
      if (v !== undefined && v !== null && String(v).trim()) {
        seen.add(String(v));
      }
    }
    return Array.from(seen).sort();
  }, []);

  const refetch = useCallback(async (params?: PaginationParams) => {
    const p = params || lastParamsRef.current;
    lastParamsRef.current = p;
    const source = allDataRef.current;

    const page = p.page || 1;
    const pageSize = p.pageSize || 50;
    const search = (p.search || '').toLowerCase();
    const filters = p.filters || {};
    const sorts = p.sort || [];

    // 1) Filter
    let filtered = source.filter(row => {
      for (const [key, val] of Object.entries(filters)) {
        if (val === undefined || val === null || val === '') continue;
        const cell = String(row[key] ?? '');
        if (Array.isArray(val)) {
          if (val.length > 0 && !val.includes(cell)) return false;
        } else {
          if (cell !== String(val)) return false;
        }
      }
      return true;
    });

    // 2) Search
    if (search) {
      filtered = filtered.filter(row =>
        Object.values(row).some(v => String(v ?? '').toLowerCase().includes(search)),
      );
    }

    // 3) Sort
    if (sorts.length > 0) {
      filtered.sort((a, b) => {
        for (const s of sorts) {
          const aVal = a[s.field];
          const bVal = b[s.field];
          let cmp = 0;
          if (typeof aVal === 'number' && typeof bVal === 'number') {
            cmp = aVal - bVal;
          } else {
            cmp = String(aVal ?? '').localeCompare(String(bVal ?? ''));
          }
          if (cmp !== 0) return s.direction === 'desc' ? -cmp : cmp;
        }
        return 0;
      });
    }

    // 4) Paginate
    const totalItems = filtered.length;
    const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
    const safePage = Math.min(page, totalPages);
    const start = (safePage - 1) * pageSize;
    const paged = filtered.slice(start, start + pageSize);

    setVisibleData(paged);
    setPagination({ currentPage: safePage, pageSize, totalItems, totalPages });
  }, []);

  // Re-apply whenever allData changes
  useEffect(() => {
    refetch(lastParamsRef.current);
  }, [allData, refetch]);

  return { visibleData, pagination, refetch, fetchFilterValues };
}


// ---------------------------------------------------------------------------
// StatsTab
// ---------------------------------------------------------------------------

const StatsTab: React.FC = () => {
  const { t } = useLanguage();
  const [allStats, setAllStats] = useState<TableStat[]>([]);
  const [loading, setLoading] = useState(false);
  const [dbFilter, setDbFilter] = useState<string>('');

  const _fetchStats = useCallback(async () => {
    try {
      setLoading(true);
      const params = dbFilter ? `?db=${encodeURIComponent(dbFilter)}` : '';
      const res = await api.get(`/api/admin/database-health/stats${params}`);
      const rows = (res.data.stats || []).map((s: any, i: number) => ({
        ...s,
        id: `${s.db}-${s.table}-${i}`,
      }));
      setAllStats(rows);
    } catch {
      setAllStats([]);
    } finally {
      setLoading(false);
    }
  }, [dbFilter]);

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

  const { visibleData, pagination, refetch, fetchFilterValues } = _useClientPagination(allStats);

  const databases = useMemo(
    () => Array.from(new Set(allStats.map(s => s.db))).sort(),
    [allStats],
  );

  const totals = useMemo(() => {
    let rows = 0, size = 0, idx = 0;
    for (const s of allStats) {
      rows += s.estimatedRows;
      size += s.totalSizeBytes;
      idx += s.indexSizeBytes;
    }
    return { rows, size, idx, tables: allStats.length, dbs: databases.length };
  }, [allStats, databases]);

  const columns: ColumnConfig[] = useMemo(() => [
    {
      key: 'db',
      label: t('Datenbank'),
      sortable: true,
      filterable: true,
      searchable: true,
      width: 200,
      filterOptions: databases,
    },
    {
      key: 'table',
      label: t('Tabelle'),
      sortable: true,
      searchable: true,
      width: 200,
    },
    {
      key: 'estimatedRows',
      label: t('Zeilen (ca.)'),
      type: 'number',
      sortable: true,
      width: 120,
      formatter: (v: number) => _formatNumber(v),
    },
    {
      key: 'totalSizeBytes',
      label: t('Total Size'),
      type: 'number',
      sortable: true,
      width: 120,
      formatter: (v: number) => _formatBytes(v),
    },
    {
      key: 'indexSizeBytes',
      label: t('Index Size'),
      type: 'number',
      sortable: true,
      width: 120,
      formatter: (v: number) => _formatBytes(v),
    },
    {
      key: 'lastVacuum',
      label: t('Last Vacuum'),
      sortable: true,
      width: 170,
      formatter: (v: string | null) => v ?? '—',
    },
    {
      key: 'lastAnalyze',
      label: t('Last Analyze'),
      sortable: true,
      width: 170,
      formatter: (v: string | null) => v ?? '—',
    },
  ], [t, databases]);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
      {/* Controls */}
      <div className={styles.filterSection}>
        <div className={styles.filterGroup}>
          <label className={styles.filterLabel}>{t('Datenbank')}</label>
          <select
            className={styles.filterSelect}
            value={dbFilter}
            onChange={e => setDbFilter(e.target.value)}
          >
            <option value="">{t('Alle')}</option>
            {databases.map(db => <option key={db} value={db}>{db}</option>)}
          </select>
        </div>
        <button className={styles.secondaryButton} onClick={_fetchStats} disabled={loading}>
          <FaSync className={loading ? 'spinning' : ''} /> {t('Aktualisieren')}
        </button>
      </div>

      {/* Summary */}
      <div className={styles.filterSection} style={{ gap: '0.75rem', flexWrap: 'wrap' }}>
        <span className={styles.filterLabel}>{t('{dbs} Datenbanken', { dbs: totals.dbs })}</span>
        <span className={styles.filterLabel}>{t('{tables} Tabellen', { tables: totals.tables })}</span>
        <span className={styles.filterLabel}>{t('{rows} Zeilen (ca.)', { rows: _formatNumber(totals.rows) })}</span>
        <span className={styles.filterLabel}>{t('Total {size}', { size: _formatBytes(totals.size) })}</span>
        <span className={styles.filterLabel}>{t('Index {size}', { size: _formatBytes(totals.idx) })}</span>
      </div>

      <div className={styles.tableContainer}>
        <FormGeneratorTable
          data={visibleData}
          columns={columns}
          loading={loading}
          sortable={true}
          searchable={true}
          filterable={true}
          pagination={true}
          pageSize={50}
          selectable={false}
          hookData={{
            refetch,
            pagination,
            fetchFilterValues,
          }}
          emptyMessage={t('Keine Tabellen gefunden')}
        />
      </div>
    </div>
  );
};


// ---------------------------------------------------------------------------
// OrphansTab
// ---------------------------------------------------------------------------

const OrphansTab: React.FC = () => {
  const { t } = useLanguage();
  const toast = useToast();
  const { confirm, ConfirmDialog } = useConfirm();

  const [allOrphans, setAllOrphans] = useState<OrphanEntry[]>([]);
  const [loading, setLoading] = useState(false);
  const [cleaning, setCleaning] = useState<string | null>(null);
  const [downloading, setDownloading] = useState<string | null>(null);
  const [cleaningAll, setCleaningAll] = useState(false);
  const [onlyProblems, setOnlyProblems] = useState(true);
  // Default ON: deleted-user remnants belong to a dedicated purge workflow,
  // not to generic FK cleanup. Hiding them by default prevents confusion
  // (and accidental "Alle bereinigen" runs) when the SysAdmin scans for
  // genuine FK drift.
  const [excludeUserFks, setExcludeUserFks] = useState(true);
  const [dbFilter, setDbFilter] = useState<string>('');

  const _fetchOrphans = useCallback(async () => {
    try {
      setLoading(true);
      const qs = new URLSearchParams();
      if (dbFilter) qs.set('db', dbFilter);
      if (excludeUserFks) qs.set('excludeUserFks', 'true');
      const params = qs.toString() ? `?${qs.toString()}` : '';
      const res = await api.get(`/api/admin/database-health/orphans${params}`);
      const rows = (res.data.orphans || []).map((o: any, i: number) => ({
        ...o,
        id: `${o.sourceDb}.${o.sourceTable}.${o.sourceColumn}-${i}`,
      }));
      setAllOrphans(rows);
    } catch {
      setAllOrphans([]);
    } finally {
      setLoading(false);
    }
  }, [dbFilter, excludeUserFks]);

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

  const displayed = useMemo(
    () => onlyProblems ? allOrphans.filter(o => o.orphanCount > 0) : allOrphans,
    [allOrphans, onlyProblems],
  );

  const { visibleData, pagination, refetch, fetchFilterValues } = _useClientPagination(displayed);

  const databases = useMemo(
    () => Array.from(new Set(allOrphans.map(o => o.sourceDb))).sort(),
    [allOrphans],
  );

  const totalOrphans = useMemo(() => allOrphans.reduce((s, o) => s + o.orphanCount, 0), [allOrphans]);

  const _postCleanOne = async (o: OrphanEntry, force: boolean): Promise<number | 'refused'> => {
    try {
      const res = await api.post('/api/admin/database-health/orphans/clean', {
        db: o.sourceDb,
        table: o.sourceTable,
        column: o.sourceColumn,
        force,
      });
      return res.data.deleted as number;
    } catch (err: any) {
      const status = err?.response?.status;
      const detail = err?.response?.data?.detail;
      if (status === 409 && detail?.refused) {
        return 'refused';
      }
      const reason = typeof detail === 'string' ? detail : (detail?.reason || t('Fehler beim Bereinigen'));
      throw new Error(reason);
    }
  };

  const _downloadOne = async (o: OrphanEntry) => {
    const key = `${o.sourceDb}.${o.sourceTable}.${o.sourceColumn}`;
    setDownloading(key);
    try {
      const res = await api.get('/api/admin/database-health/orphans/list', {
        params: {
          db: o.sourceDb,
          table: o.sourceTable,
          column: o.sourceColumn,
          limit: 5000,
        },
      });
      const payload = {
        sourceDb: o.sourceDb,
        sourceTable: o.sourceTable,
        sourceColumn: o.sourceColumn,
        targetDb: o.targetDb,
        targetTable: o.targetTable,
        targetColumn: o.targetColumn,
        scannedOrphanCount: o.orphanCount,
        downloadedAt: new Date().toISOString(),
        ...res.data,
      };
      const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      const ts = new Date().toISOString().replace(/[:.]/g, '-');
      a.href = url;
      a.download = `orphans_${o.sourceDb}_${o.sourceTable}_${o.sourceColumn}_${ts}.json`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
      toast.showSuccess(t('{count} verwaiste Datensätze heruntergeladen', { count: res.data?.count ?? 0 }));
    } catch (err: any) {
      const detail = err?.response?.data?.detail;
      const reason = typeof detail === 'string' ? detail : (detail?.reason || t('Fehler beim Download'));
      toast.showError(reason);
    } finally {
      setDownloading(null);
    }
  };

  const _cleanOne = async (o: OrphanEntry) => {
    const baseMsg = t('{count} verwaiste Einträge in {table}.{column} löschen?', { count: o.orphanCount, table: o.sourceTable, column: o.sourceColumn });
    const warning = (o.targetEmpty || o.wouldDeleteAll)
      ? '\n\n' + t('WARNUNG: Target-Tabelle {target} ist leer oder die Bereinigung würde alle Source-Zeilen löschen. Das ist meist eine Fehlkonfiguration!', { target: `${o.targetDb}.${o.targetTable}` })
      : '';
    const ok = await confirm(baseMsg + warning, { title: t('Orphans bereinigen'), variant: 'danger' });
    if (!ok) return;
    const key = `${o.sourceDb}.${o.sourceTable}.${o.sourceColumn}`;
    setCleaning(key);
    try {
      let result = await _postCleanOne(o, false);
      if (result === 'refused') {
        const forceOk = await confirm(
          t('Sicherheits-Check ausgelöst (leere Target-Tabelle oder >50% der Source würden gelöscht). Trotzdem mit force=true bereinigen?'),
          { title: t('Bereinigung erzwingen?'), variant: 'danger' },
        );
        if (!forceOk) {
          toast.showInfo(t('Bereinigung abgebrochen'));
          return;
        }
        result = await _postCleanOne(o, true);
      }
      toast.showSuccess(t('{deleted} Einträge gelöscht', { deleted: result as number }));
      _fetchOrphans();
    } catch (err: any) {
      toast.showError(err?.message || t('Fehler beim Bereinigen'));
    } finally {
      setCleaning(null);
    }
  };

  const _cleanAll = async (force: boolean = false) => {
    const ok = await confirm(
      t('{count} verwaiste Einträge in {relations} Beziehungen löschen?', {
        count: totalOrphans,
        relations: allOrphans.filter(o => o.orphanCount > 0).length,
      }) + (force ? '\n\n' + t('FORCE-Modus: Sicherheits-Checks werden ignoriert!') : ''),
      { title: t('Alle Orphans bereinigen'), variant: 'danger' },
    );
    if (!ok) return;
    setCleaningAll(true);
    try {
      const res = await api.post('/api/admin/database-health/orphans/clean-all', { force, excludeUserFks });
      const results: CleanResult[] = res.data.results || [];
      const totalDeleted = results.reduce((s, r) => s + r.deleted, 0);
      const errors = results.filter(r => r.error);
      const skipped = results.filter(r => r.skipped);
      if (skipped.length > 0 && !force) {
        const retryOk = await confirm(
          t('{deleted} gelöscht. {skipped} Bereinigungen wurden vom Sicherheits-Check abgelehnt (leere Target-Tabelle oder >50% Löschung). Mit force=true erneut versuchen?', { deleted: totalDeleted, skipped: skipped.length }),
          { title: t('Force benötigt'), variant: 'danger' },
        );
        if (retryOk) {
          setCleaningAll(false);
          await _cleanAll(true);
          return;
        }
      }
      if (errors.length > 0) {
        toast.showWarning(t('{deleted} gelöscht, {errors} Fehler, {skipped} übersprungen', { deleted: totalDeleted, errors: errors.length, skipped: skipped.length }));
      } else if (skipped.length > 0) {
        toast.showWarning(t('{deleted} gelöscht, {skipped} übersprungen', { deleted: totalDeleted, skipped: skipped.length }));
      } else {
        toast.showSuccess(t('{deleted} Einträge gelöscht', { deleted: totalDeleted }));
      }
      _fetchOrphans();
    } catch (err: any) {
      toast.showError(err.response?.data?.detail || t('Fehler beim Bereinigen'));
    } finally {
      setCleaningAll(false);
    }
  };

  const columns: ColumnConfig[] = useMemo(() => [
    {
      key: 'sourceDb',
      label: t('Source DB'),
      sortable: true,
      filterable: true,
      searchable: true,
      width: 180,
      filterOptions: databases,
    },
    {
      key: 'sourceTable',
      label: t('Tabelle'),
      sortable: true,
      searchable: true,
      width: 180,
    },
    {
      key: 'sourceColumn',
      label: t('FK-Spalte'),
      sortable: true,
      searchable: true,
      width: 150,
    },
    {
      key: 'targetTable',
      label: t('Referenz'),
      sortable: true,
      width: 220,
      formatter: (_val: string, row: OrphanEntry) => {
        const isCrossDb = row.sourceDb !== row.targetDb;
        return (
          <span>
            <code>{row.targetTable}.{row.targetColumn}</code>
            {isCrossDb && (
              <span style={{
                marginLeft: '0.4rem',
                padding: '0.125rem 0.375rem',
                borderRadius: '4px',
                fontSize: '0.625rem',
                fontWeight: 600,
                textTransform: 'uppercase',
                letterSpacing: '0.03em',
                background: 'var(--primary-dark-bg, rgba(242, 88, 67, 0.12))',
                color: 'var(--primary-color, #f25843)',
              }}>
                {t('cross-db')}
              </span>
            )}
          </span>
        );
      },
    },
    {
      key: 'orphanCount',
      label: t('Orphans'),
      type: 'number',
      sortable: true,
      width: 100,
      formatter: (v: number) => (
        <span style={v > 0 ? { color: 'var(--danger-color, #e53e3e)', fontWeight: 600 } : undefined}>
          {_formatNumber(v)}
        </span>
      ),
    },
  ], [t, databases]);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
      <ConfirmDialog />

      {/* Controls */}
      <div className={styles.filterSection}>
        <div className={styles.filterGroup}>
          <label className={styles.filterLabel}>{t('Datenbank')}</label>
          <select
            className={styles.filterSelect}
            value={dbFilter}
            onChange={e => setDbFilter(e.target.value)}
          >
            <option value="">{t('Alle')}</option>
            {databases.map(db => <option key={db} value={db}>{db}</option>)}
          </select>
        </div>
        <div className={styles.filterGroup}>
          <label className={styles.checkboxLabel}>
            <input type="checkbox" checked={onlyProblems} onChange={e => setOnlyProblems(e.target.checked)} />
            {t('Nur Probleme')}
          </label>
        </div>
        <div className={styles.filterGroup}>
          <label
            className={styles.checkboxLabel}
            title={t('FK-Referenzen auf UserInDB.id ausblenden — diese werden über den User-Purge-Workflow separat behandelt.')}
          >
            <input
              type="checkbox"
              checked={excludeUserFks}
              onChange={e => setExcludeUserFks(e.target.checked)}
            />
            {t('Ohne FK-Referenzen zu UserInDB.id')}
          </label>
        </div>
        <div className={styles.headerActions}>
          <button className={styles.secondaryButton} onClick={_fetchOrphans} disabled={loading}>
            <FaSync className={loading ? 'spinning' : ''} /> {t('Scan')}
          </button>
          {totalOrphans > 0 && (
            <button className={styles.dangerButton} onClick={() => _cleanAll(false)} disabled={cleaningAll || loading}>
              <FaBroom className={cleaningAll ? 'spinning' : ''} /> {t('Alle bereinigen')} ({_formatNumber(totalOrphans)})
            </button>
          )}
        </div>
      </div>

      {totalOrphans > 0 && (
        <div className={styles.infoBox} style={{ background: 'var(--warning-bg, #fffbeb)', borderColor: 'var(--warning-color, #d69e2e)' }}>
          <FaExclamationTriangle style={{ marginRight: 8, color: 'var(--warning-color, #d69e2e)' }} />
          {t('{count} verwaiste Einträge in {relations} Beziehungen gefunden', {
            count: _formatNumber(totalOrphans),
            relations: allOrphans.filter(o => o.orphanCount > 0).length,
          })}
        </div>
      )}

      <div className={styles.tableContainer}>
        <FormGeneratorTable
          data={visibleData}
          columns={columns}
          loading={loading}
          sortable={true}
          searchable={true}
          filterable={true}
          pagination={true}
          pageSize={50}
          selectable={false}
          customActions={[
            {
              id: 'download',
              icon: <FaDownload />,
              onClick: (row: OrphanEntry) => _downloadOne(row),
              visible: (row: OrphanEntry) => row.orphanCount > 0,
              loading: (row: OrphanEntry) => downloading === `${row.sourceDb}.${row.sourceTable}.${row.sourceColumn}`,
              title: t('Orphan-Liste herunterladen (JSON)'),
            },
            {
              id: 'clean',
              icon: <FaTrashAlt />,
              onClick: (row: OrphanEntry) => _cleanOne(row),
              visible: (row: OrphanEntry) => row.orphanCount > 0,
              loading: (row: OrphanEntry) => cleaning === `${row.sourceDb}.${row.sourceTable}.${row.sourceColumn}` || cleaningAll,
              title: t('Orphans löschen'),
            },
          ]}
          hookData={{
            refetch,
            pagination,
            fetchFilterValues,
          }}
          emptyMessage={onlyProblems ? t('Keine Orphans gefunden') : t('Keine FK-Beziehungen gefunden')}
        />
      </div>
    </div>
  );
};


// ---------------------------------------------------------------------------
// Types (Migration)
// ---------------------------------------------------------------------------

interface MigrationDatabase {
  name: string;
  tableCount: number;
  recordCount: number;
}

interface ValidationSummaryItem {
  database: string;
  tableCount: number;
  recordCount: number;
  registered: boolean;
}

interface ValidationResult {
  valid: boolean;
  summary: ValidationSummaryItem[];
  warnings: string[];
  systemObjectsFound: Array<{ type: string; label: string; payloadId: string }>;
}

interface ProgressLogEntry {
  ts: string;
  message: string;
  status: 'info' | 'success' | 'error';
}


// ---------------------------------------------------------------------------
// MigrationTab
// ---------------------------------------------------------------------------

const MigrationTab: React.FC = () => {
  const { t } = useLanguage();
  const toast = useToast();
  const { confirm, ConfirmDialog } = useConfirm();

  // --- Backup state ---
  const [databases, setDatabases] = useState<MigrationDatabase[]>([]);
  const [selectedDbs, setSelectedDbs] = useState<Set<string>>(new Set());
  const [loadingDbs, setLoadingDbs] = useState(false);
  const [exporting, setExporting] = useState(false);
  const [exportLog, setExportLog] = useState<ProgressLogEntry[]>([]);

  // --- Restore state ---
  const [uploadedFile, setUploadedFile] = useState<File | null>(null);
  const [validating, setValidating] = useState(false);
  const [validation, setValidation] = useState<ValidationResult | null>(null);
  const [importMode, setImportMode] = useState<'replace' | 'merge'>('merge');
  const [importing, setImporting] = useState(false);
  const [importLog, setImportLog] = useState<ProgressLogEntry[]>([]);

  const fileInputRef = useRef<HTMLInputElement>(null);
  const exportLogRef = useRef<HTMLDivElement>(null);
  const importLogRef = useRef<HTMLDivElement>(null);

  // --- Fetch databases ---
  const _fetchDatabases = useCallback(async () => {
    try {
      setLoadingDbs(true);
      const res = await api.get('/api/admin/database-health/migration/databases');
      const dbs: MigrationDatabase[] = res.data.databases || [];
      setDatabases(dbs);
      setSelectedDbs(new Set(dbs.map(d => d.name)));
    } catch {
      setDatabases([]);
    } finally {
      setLoadingDbs(false);
    }
  }, []);

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

  // --- Backup: DB selection ---
  const allSelected = databases.length > 0 && selectedDbs.size === databases.length;

  const _toggleAll = () => {
    if (allSelected) {
      setSelectedDbs(new Set());
    } else {
      setSelectedDbs(new Set(databases.map(d => d.name)));
    }
  };

  const _toggleDb = (name: string) => {
    setSelectedDbs(prev => {
      const next = new Set(prev);
      if (next.has(name)) {
        next.delete(name);
      } else {
        next.add(name);
      }
      return next;
    });
  };

  // --- Backup: Export (per-DB with progress) ---
  const _addExportLog = useCallback((message: string, logStatus: ProgressLogEntry['status'] = 'info') => {
    const ts = new Date().toLocaleTimeString();
    setExportLog(prev => [...prev, { ts, message, status: logStatus }]);
    setTimeout(() => exportLogRef.current?.scrollTo({ top: exportLogRef.current.scrollHeight }), 50);
  }, []);

  const _startExport = async () => {
    if (selectedDbs.size === 0) return;
    setExporting(true);
    setExportLog([]);

    const dbList = Array.from(selectedDbs);
    const dbsParam = allSelected ? 'all' : dbList.join(',');

    _addExportLog(t('Export gestartet: {count} Datenbanken (Streaming)', { count: dbList.length }));

    try {
      const baseURL = api.defaults.baseURL || '';
      const url = `${baseURL}/api/admin/database-health/migration/export-stream?databases=${encodeURIComponent(dbsParam)}`;

      const response = await fetch(url, { credentials: 'include' });

      if (!response.ok) {
        const errText = await response.text();
        throw new Error(`Server ${response.status}: ${errText}`);
      }
      if (!response.body) throw new Error('ReadableStream not supported');

      const cd = response.headers.get('Content-Disposition');
      let filename = `db_export_${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
      if (cd) {
        const m = cd.match(/filename="?([^"]+)"?/);
        if (m) filename = m[1];
      }

      const handle = await (window as any).showSaveFilePicker({
        suggestedName: filename,
        types: [{ description: 'JSON', accept: { 'application/json': ['.json'] } }],
      });
      const writable = await handle.createWritable();

      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let totalBytes = 0;
      let lastLogBytes = 0;
      let currentDb = '';
      let dbCount = 0;
      const totalDbs = dbList.length;
      let tailBuf = '';

      for (;;) {
        const { done, value } = await reader.read();
        if (done) break;
        await writable.write(value);
        totalBytes += value.length;

        const text = decoder.decode(value, { stream: true });
        tailBuf += text;

        const match = tailBuf.match(/"(poweron_[a-z_]+)":\{"tables"/);
        if (match && match[1] !== currentDb) {
          currentDb = match[1];
          dbCount++;
          tailBuf = '';
          const mb = (totalBytes / (1024 * 1024)).toFixed(1);
          _addExportLog(t('{index}/{total}: {db} — {mb} MB', {
            index: dbCount, total: totalDbs, db: currentDb, mb,
          }));
          lastLogBytes = totalBytes;
        } else if (totalBytes - lastLogBytes >= 2 * 1024 * 1024) {
          const mb = (totalBytes / (1024 * 1024)).toFixed(1);
          setExportLog(prev => {
            const updated = [...prev];
            updated[updated.length - 1] = {
              ts: new Date().toLocaleTimeString(),
              message: t('{index}/{total}: {db} — {mb} MB', {
                index: dbCount, total: totalDbs, db: currentDb || '...', mb,
              }),
              status: 'info',
            };
            return updated;
          });
          lastLogBytes = totalBytes;
        }

        if (tailBuf.length > 500) tailBuf = tailBuf.slice(-200);
      }

      await writable.close();
      const mb = (totalBytes / (1024 * 1024)).toFixed(1);
      _addExportLog(t('Export abgeschlossen: {count} Datenbanken, {mb} MB', { count: dbCount, mb }), 'success');
      toast.showSuccess(t('Export gespeichert'));
    } catch (err: any) {
      _addExportLog(t('Fehler: {error}', { error: String(err) }), 'error');
      toast.showError(t('Export fehlgeschlagen'));
    } finally {
      setExporting(false);
    }
  };

  // --- Restore: File upload ---
  const _handleFileSelect = (file: File) => {
    setUploadedFile(file);
    setValidation(null);
    _validateFile(file);
  };

  const _onFileInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) _handleFileSelect(file);
    if (e.target) e.target.value = '';
  };

  const _onDrop = (e: React.DragEvent) => {
    e.preventDefault();
    const file = e.dataTransfer.files?.[0];
    if (file && file.name.endsWith('.json')) {
      _handleFileSelect(file);
    }
  };

  const _onDragOver = (e: React.DragEvent) => {
    e.preventDefault();
  };

  // --- Restore: Validate (uploads file to server, streams to disk) ---
  const importTokenRef = useRef('');

  const [uploadProgress, setUploadProgress] = useState('');

  const _validateFile = async (file: File) => {
    setValidating(true);
    setValidation(null);
    setUploadProgress('');
    importTokenRef.current = '';

    const fileMb = (file.size / (1024 * 1024)).toFixed(1);

    try {
      // Phase 1: Upload file to disk
      const formData = new FormData();
      formData.append('file', file);
      const uploadRes = await api.post('/api/admin/database-health/migration/upload-import', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
        timeout: 0,
        onUploadProgress: (e) => {
          if (e.total) {
            const pct = Math.round((e.loaded / e.total) * 100);
            const loadedMb = (e.loaded / (1024 * 1024)).toFixed(1);
            setUploadProgress(
              pct < 100
                ? t('Upload: {loaded} / {total} MB ({pct}%)', { loaded: loadedMb, total: fileMb, pct })
                : t('Verarbeitung wird gestartet...'),
            );
          }
        },
      });

      const token = uploadRes.data.token;
      if (!token) throw new Error('No token returned from upload');

      // Phase 2: Stream validation + split with progress
      const baseURL = api.defaults.baseURL || '';
      const streamUrl = `${baseURL}/api/admin/database-health/migration/process-import-stream?token=${encodeURIComponent(token)}`;
      const streamRes = await fetch(streamUrl, { credentials: 'include' });

      if (!streamRes.ok) {
        const errText = await streamRes.text();
        throw new Error(`Server ${streamRes.status}: ${errText}`);
      }
      if (!streamRes.body) throw new Error('ReadableStream not supported');

      const reader = streamRes.body.getReader();
      const decoder = new TextDecoder();
      let buf = '';
      let finalResult: any = null;

      for (;;) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += decoder.decode(value, { stream: true });

        const lines = buf.split('\n');
        buf = lines.pop() || '';

        for (const line of lines) {
          if (!line.trim()) continue;
          try {
            const evt = JSON.parse(line);
            if (evt.phase === 'validate') {
              setUploadProgress(
                t('Pass 1 Validierung: {db}.{table} ({rows} Datensaetze)', {
                  db: evt.db, table: evt.table, rows: evt.rows,
                }),
              );
            } else if (evt.phase === 'split') {
              setUploadProgress(
                t('Pass 2 Split: {db}.{table} ({rows} Datensaetze)', {
                  db: evt.db, table: evt.table, rows: evt.rows,
                }),
              );
            } else if (evt.phase === 'done') {
              finalResult = evt.result;
            } else if (evt.phase === 'error') {
              throw new Error(evt.detail || 'Processing failed');
            }
          } catch (parseErr) {
            if ((parseErr as Error).message?.startsWith('Processing'))
              throw parseErr;
          }
        }
      }

      setUploadProgress('');

      if (!finalResult) throw new Error('No result received from processing stream');

      importTokenRef.current = finalResult.token || token;
      setValidation({
        valid: finalResult.valid,
        summary: (finalResult.databases || []).map((d: any) => ({
          database: d.database,
          tableCount: d.tableCount,
          recordCount: d.recordCount,
          registered: true,
        })),
        warnings: finalResult.warnings || [],
        systemObjectsFound: finalResult.systemObjectsFound || [],
      });
    } catch (err: any) {
      setUploadProgress('');
      const detail = err?.response?.data?.detail || err?.message;
      setValidation({
        valid: false, summary: [], systemObjectsFound: [],
        warnings: [typeof detail === 'string' ? detail : t('Upload oder Validierung fehlgeschlagen')],
      });
    } finally {
      setValidating(false);
    }
  };

  // --- Restore: Import (per-DB with progress) ---
  const _addImportLog = useCallback((message: string, logStatus: ProgressLogEntry['status'] = 'info') => {
    const ts = new Date().toLocaleTimeString();
    setImportLog(prev => [...prev, { ts, message, status: logStatus }]);
    setTimeout(() => importLogRef.current?.scrollTo({ top: importLogRef.current.scrollHeight }), 50);
  }, []);

  const _startImport = async () => {
    if (!importTokenRef.current || !validation?.valid || importing) return;

    setImporting(true);

    const modeLabel = importMode === 'replace'
      ? t('Neu (Datenbank leeren und importieren)')
      : t('Zusammenfuehren (bestehende Daten belassen, neue ergaenzen)');

    const ok = await confirm(
      t('Import mit Modus "{mode}" starten? Dieser Vorgang kann nicht rueckgaengig gemacht werden.', { mode: modeLabel }),
      { title: t('Import starten'), variant: importMode === 'replace' ? 'danger' : 'primary' },
    );
    if (!ok) { setImporting(false); return; }
    setImportLog([]);

    const token = importTokenRef.current;
    const dbList = validation.summary.filter(s => s.registered);
    const totalDbs = dbList.length;
    let totalRecords = 0;
    let errors = 0;

    _addImportLog(t('Import gestartet: {count} Datenbanken', { count: totalDbs }));

    for (let i = 0; i < dbList.length; i++) {
      const dbInfo = dbList[i];
      _addImportLog(
        t('Importiere {index}/{total}: {db} ({records} Datensaetze)...', {
          index: i + 1, total: totalDbs, db: dbInfo.database, records: dbInfo.recordCount,
        }),
      );
      try {
        const res = await api.post('/api/admin/database-health/migration/import-single', {
          token,
          database: dbInfo.database,
          mode: importMode,
        });
        const result = res.data;
        totalRecords += result.recordCount || 0;
        const dbWarnings: string[] = result.warnings || [];
        for (const w of dbWarnings) {
          _addImportLog(t('Warnung: {msg}', { msg: w }), 'error');
        }
        _addImportLog(
          t('{db}: {count} Datensaetze importiert', { db: dbInfo.database, count: result.recordCount || 0 }),
          dbWarnings.length > 0 ? 'error' : 'success',
        );
      } catch (err: any) {
        errors++;
        const detail = err?.response?.data?.detail;
        _addImportLog(
          t('Fehler bei {db}: {error}', {
            db: dbInfo.database,
            error: typeof detail === 'string' ? detail : String(err),
          }),
          'error',
        );
      }
    }

    try { await api.post('/api/admin/database-health/migration/import-done', { token }); } catch { /* ignore */ }

    _addImportLog(
      t('Import abgeschlossen: {records} Datensaetze in {dbs} Datenbanken', {
        records: totalRecords, dbs: totalDbs,
      }),
      'success',
    );

    if (errors > 0) {
      toast.showWarning(t('{count} Datensaetze importiert, {errors} Fehler', { count: totalRecords, errors }));
    } else {
      toast.showSuccess(t('{count} Datensaetze erfolgreich importiert', { count: totalRecords }));
    }
    importTokenRef.current = '';
    setImporting(false);
  };

  const _resetUpload = () => {
    setUploadedFile(null);
    setValidation(null);
    importTokenRef.current = '';
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0, overflow: 'auto', gap: '2rem', padding: '0.5rem 0' }}>
      <ConfirmDialog />

      {/* ---- BACKUP SECTION ---- */}
      <section>
        <h2 style={{ fontSize: '1.125rem', fontWeight: 600, color: 'var(--text-primary)', margin: '0 0 1rem 0', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
          <FaDownload /> {t('Backup')}
        </h2>

        <p style={{ fontSize: '0.875rem', color: 'var(--text-secondary)', margin: '0 0 0.75rem 0' }}>
          {t('Datenbanken fuer Export auswaehlen')}
        </p>

        {loadingDbs ? (
          <div className={styles.loadingContainer}>
            <div className={styles.spinner} />
          </div>
        ) : (
          <>
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem', marginBottom: '1rem' }}>
              <label className={styles.checkboxLabel} style={{ fontWeight: 600 }}>
                <input type="checkbox" checked={allSelected} onChange={_toggleAll} />
                {t('Alle Datenbanken')}
              </label>
              <div style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem', paddingLeft: '0.5rem' }}>
                {databases.map(db => (
                  <label key={db.name} className={styles.checkboxLabel}>
                    <input
                      type="checkbox"
                      checked={selectedDbs.has(db.name)}
                      onChange={() => _toggleDb(db.name)}
                    />
                    <span>{db.name}</span>
                    <span style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>
                      ({db.tableCount} {t('Tabellen')}, ~{_formatNumber(db.recordCount)} {t('Zeilen')})
                    </span>
                  </label>
                ))}
              </div>
            </div>

            <button
              className={styles.primaryButton}
              onClick={_startExport}
              disabled={exporting || selectedDbs.size === 0}
            >
              {exporting ? (
                <><FaSync className="spinning" /> {t('Export laeuft...')}</>
              ) : (
                <><FaDownload /> {t('Export starten')}</>
              )}
            </button>

            {exportLog.length > 0 && (
              <div
                ref={exportLogRef}
                style={{
                  marginTop: '0.75rem',
                  maxHeight: '200px',
                  overflow: 'auto',
                  background: 'var(--bg-secondary)',
                  border: '1px solid var(--border-color)',
                  borderRadius: '6px',
                  padding: '0.5rem 0.75rem',
                  fontFamily: 'monospace',
                  fontSize: '0.8125rem',
                  lineHeight: '1.6',
                }}
              >
                {exportLog.map((entry, i) => (
                  <div key={i} style={{
                    color: entry.status === 'error' ? 'var(--danger-color, #e53e3e)'
                      : entry.status === 'success' ? '#388e3c' : 'var(--text-secondary)',
                  }}>
                    <span style={{ color: 'var(--text-tertiary)', marginRight: '0.5rem' }}>{entry.ts}</span>
                    {entry.message}
                  </div>
                ))}
              </div>
            )}
          </>
        )}
      </section>

      {/* ---- DIVIDER ---- */}
      <hr style={{ border: 'none', borderTop: '1px solid var(--border-color)', margin: 0 }} />

      {/* ---- RESTORE SECTION ---- */}
      <section>
        <h2 style={{ fontSize: '1.125rem', fontWeight: 600, color: 'var(--text-primary)', margin: '0 0 1rem 0', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
          <FaUpload /> {t('Restore')}
        </h2>

        {/* File upload zone */}
        {!uploadedFile ? (
          <div
            onDrop={_onDrop}
            onDragOver={_onDragOver}
            onClick={() => fileInputRef.current?.click()}
            style={{
              border: '2px dashed var(--border-color)',
              borderRadius: '8px',
              padding: '2rem',
              textAlign: 'center',
              cursor: 'pointer',
              background: 'var(--bg-secondary)',
              transition: 'border-color 0.2s',
            }}
          >
            <FaUpload style={{ fontSize: '1.5rem', color: 'var(--text-tertiary)', marginBottom: '0.5rem' }} />
            <p style={{ margin: 0, fontSize: '0.875rem', color: 'var(--text-secondary)' }}>
              {t('Datei hier ablegen oder klicken')}
            </p>
            <p style={{ margin: '0.25rem 0 0', fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>
              {t('JSON-Datei hochladen')}
            </p>
            <input
              ref={fileInputRef}
              type="file"
              accept=".json"
              onChange={_onFileInputChange}
              style={{ display: 'none' }}
            />
          </div>
        ) : (
          <div>
            {/* File info */}
            <div className={styles.infoBox} style={{ justifyContent: 'space-between' }}>
              <span style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                <FaDatabase />
                {uploadedFile.name} ({_formatBytes(uploadedFile.size)})
              </span>
              <button
                className={styles.secondaryButton}
                onClick={_resetUpload}
                style={{ padding: '0.25rem 0.5rem', fontSize: '0.75rem' }}
              >
                {t('Andere Datei')}
              </button>
            </div>

            {/* Validation */}
            {validating && (
              <div className={styles.loadingContainer} style={{ padding: '1rem' }}>
                <div className={styles.spinner} />
                <span>{uploadProgress || t('Validierung laeuft...')}</span>
              </div>
            )}

            {validation && !validating && (
              <div style={{ marginTop: '1rem' }}>
                <h3 style={{ fontSize: '1rem', fontWeight: 600, margin: '0 0 0.75rem 0' }}>
                  {t('Pruefung')}
                </h3>

                {/* Validation warnings */}
                {validation.warnings.length > 0 && (
                  <div className={styles.infoBox} style={{
                    background: 'var(--warning-bg, #fffbeb)',
                    borderColor: 'var(--warning-color, #d69e2e)',
                    flexDirection: 'column',
                    alignItems: 'flex-start',
                    gap: '0.25rem',
                  }}>
                    {validation.warnings.map((w, i) => (
                      <span key={i} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                        <FaExclamationTriangle style={{ color: 'var(--warning-color, #d69e2e)', flexShrink: 0 }} /> {w}
                      </span>
                    ))}
                  </div>
                )}

                {/* Summary table */}
                {validation.summary.length > 0 && (
                  <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.875rem', marginBottom: '1rem' }}>
                    <thead>
                      <tr style={{ borderBottom: '2px solid var(--border-color)' }}>
                        <th style={{ textAlign: 'left', padding: '0.5rem 0.75rem' }}>{t('Datenbank')}</th>
                        <th style={{ textAlign: 'right', padding: '0.5rem 0.75rem' }}>{t('Tabellen')}</th>
                        <th style={{ textAlign: 'right', padding: '0.5rem 0.75rem' }}>{t('Datensaetze')}</th>
                        <th style={{ textAlign: 'center', padding: '0.5rem 0.75rem' }}>{t('Status')}</th>
                      </tr>
                    </thead>
                    <tbody>
                      {validation.summary.map(s => (
                        <tr key={s.database} style={{ borderBottom: '1px solid var(--border-color)' }}>
                          <td style={{ padding: '0.5rem 0.75rem' }}>{s.database}</td>
                          <td style={{ textAlign: 'right', padding: '0.5rem 0.75rem' }}>{s.tableCount}</td>
                          <td style={{ textAlign: 'right', padding: '0.5rem 0.75rem' }}>{_formatNumber(s.recordCount)}</td>
                          <td style={{ textAlign: 'center', padding: '0.5rem 0.75rem' }}>
                            {s.registered ? (
                              <FaCheckCircle style={{ color: '#388e3c' }} />
                            ) : (
                              <FaExclamationTriangle style={{ color: 'var(--warning-color, #d69e2e)' }} />
                            )}
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                )}

                {/* System objects info */}
                {validation.systemObjectsFound.length > 0 && (
                  <div className={styles.infoBox} style={{ flexDirection: 'column', alignItems: 'flex-start', gap: '0.25rem' }}>
                    <span style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontWeight: 500 }}>
                      <FaInfoCircle style={{ color: 'var(--primary-color, #f25843)' }} />
                      {t('Systemdaten werden beim Import nicht geloescht')}
                    </span>
                    <span style={{ fontSize: '0.8125rem', color: 'var(--text-secondary)', paddingLeft: '1.5rem' }}>
                      {validation.systemObjectsFound.map(o => o.label).join(', ')}
                    </span>
                  </div>
                )}

                {/* Import settings */}
                {validation.valid && (
                  <div style={{ marginTop: '1rem' }}>
                    <h3 style={{ fontSize: '1rem', fontWeight: 600, margin: '0 0 0.75rem 0' }}>
                      {t('Import-Einstellungen')}
                    </h3>

                    <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', marginBottom: '1rem' }}>
                      <label className={styles.checkboxLabel} style={{ cursor: 'pointer' }}>
                        <input
                          type="radio"
                          name="importMode"
                          checked={importMode === 'merge'}
                          onChange={() => setImportMode('merge')}
                        />
                        <div>
                          <div style={{ fontWeight: 500 }}>{t('Zusammenfuehren (bestehende Daten belassen, neue ergaenzen)')}</div>
                          <div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>
                            {t('Bestehende Datensaetze bleiben erhalten, nur fehlende werden eingefuegt')}
                          </div>
                        </div>
                      </label>
                      <label className={styles.checkboxLabel} style={{ cursor: 'pointer' }}>
                        <input
                          type="radio"
                          name="importMode"
                          checked={importMode === 'replace'}
                          onChange={() => setImportMode('replace')}
                        />
                        <div>
                          <div style={{ fontWeight: 500 }}>{t('Neu (Datenbank leeren und importieren)')}</div>
                          <div style={{ fontSize: '0.75rem', color: 'var(--text-tertiary)' }}>
                            {t('Bestehende Daten werden geloescht und durch importierte Daten ersetzt')}
                          </div>
                        </div>
                      </label>
                    </div>

                    {importMode === 'replace' && (
                      <div className={styles.infoBox} style={{
                        background: 'var(--danger-bg, #fff5f5)',
                        borderColor: 'var(--danger-color, #e53e3e)',
                      }}>
                        <FaExclamationTriangle style={{ marginRight: 8, color: 'var(--danger-color, #e53e3e)', flexShrink: 0 }} />
                        {t('Achtung: Bestehende Daten werden unwiderruflich geloescht. Erstellen Sie zuerst ein Backup.')}
                      </div>
                    )}

                    <button
                      className={importMode === 'replace' ? styles.dangerButton : styles.primaryButton}
                      onClick={_startImport}
                      disabled={importing}
                      style={{ marginTop: '0.5rem' }}
                    >
                      {importing ? (
                        <><FaSync className="spinning" /> {t('Import laeuft...')}</>
                      ) : (
                        <><FaUpload /> {t('Import starten')}</>
                      )}
                    </button>

                    {importLog.length > 0 && (
                      <div
                        ref={importLogRef}
                        style={{
                          marginTop: '0.75rem',
                          maxHeight: '200px',
                          overflow: 'auto',
                          background: 'var(--bg-secondary)',
                          border: '1px solid var(--border-color)',
                          borderRadius: '6px',
                          padding: '0.5rem 0.75rem',
                          fontFamily: 'monospace',
                          fontSize: '0.8125rem',
                          lineHeight: '1.6',
                        }}
                      >
                        {importLog.map((entry, i) => (
                          <div key={i} style={{
                            color: entry.status === 'error' ? 'var(--danger-color, #e53e3e)'
                              : entry.status === 'success' ? '#388e3c' : 'var(--text-secondary)',
                          }}>
                            <span style={{ color: 'var(--text-tertiary)', marginRight: '0.5rem' }}>{entry.ts}</span>
                            {entry.message}
                          </div>
                        ))}
                      </div>
                    )}
                  </div>
                )}
              </div>
            )}
          </div>
        )}
      </section>
    </div>
  );
};


// ---------------------------------------------------------------------------
// LegacyCleanupTab
// ---------------------------------------------------------------------------

interface LegacyTable {
  id: string;
  db: string;
  table: string;
  rowCount: number;
  sizeBytes: number;
}

const LegacyCleanupTab: React.FC = () => {
  const { t } = useLanguage();
  const toast = useToast();
  const { confirm, ConfirmDialog } = useConfirm();

  const [allLegacy, setAllLegacy] = useState<LegacyTable[]>([]);
  const [loading, setLoading] = useState(false);
  const [dropping, setDropping] = useState<string | null>(null);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [droppingBatch, setDroppingBatch] = useState(false);

  const _fetchLegacy = useCallback(async () => {
    try {
      setLoading(true);
      const res = await api.get('/api/admin/database-health/legacy-tables');
      const rows: LegacyTable[] = (res.data.legacyTables || []).map((t: any) => ({
        ...t,
        id: `${t.db}.${t.table}`,
      }));
      setAllLegacy(rows);
      setSelected(new Set());
    } catch {
      setAllLegacy([]);
    } finally {
      setLoading(false);
    }
  }, []);

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

  const { visibleData, pagination, refetch, fetchFilterValues } = _useClientPagination(allLegacy);

  const databases = useMemo(
    () => Array.from(new Set(allLegacy.map(l => l.db))).sort(),
    [allLegacy],
  );

  const totals = useMemo(() => {
    let rows = 0, size = 0;
    for (const l of allLegacy) { rows += l.rowCount; size += l.sizeBytes; }
    return { count: allLegacy.length, rows, size, dbs: databases.length };
  }, [allLegacy, databases]);

  const _dropOne = async (entry: LegacyTable) => {
    const ok = await confirm(
      t('Legacy-Tabelle {db}.{table} ({rows} Zeilen, {size}) unwiderruflich löschen?', {
        db: entry.db, table: entry.table, rows: _formatNumber(entry.rowCount), size: _formatBytes(entry.sizeBytes),
      }),
      { title: t('Legacy-Tabelle löschen'), variant: 'danger' },
    );
    if (!ok) return;
    setDropping(entry.id);
    try {
      await api.post('/api/admin/database-health/legacy-tables/drop', { db: entry.db, table: entry.table });
      toast.showSuccess(t('{db}.{table} gelöscht', { db: entry.db, table: entry.table }));
      _fetchLegacy();
    } catch (err: any) {
      const detail = err?.response?.data?.detail;
      toast.showError(typeof detail === 'string' ? detail : t('Fehler beim Löschen'));
    } finally {
      setDropping(null);
    }
  };

  const _dropSelected = async () => {
    if (selected.size === 0) return;
    const selectedEntries = allLegacy.filter(l => selected.has(l.id));
    const totalRows = selectedEntries.reduce((s, l) => s + l.rowCount, 0);
    const ok = await confirm(
      t('{count} Legacy-Tabellen mit insgesamt {rows} Zeilen unwiderruflich löschen?', {
        count: selected.size, rows: _formatNumber(totalRows),
      }),
      { title: t('Ausgewählte löschen'), variant: 'danger' },
    );
    if (!ok) return;
    setDroppingBatch(true);
    let deleted = 0;
    let errors = 0;
    for (const entry of selectedEntries) {
      try {
        await api.post('/api/admin/database-health/legacy-tables/drop', { db: entry.db, table: entry.table });
        deleted++;
      } catch {
        errors++;
      }
    }
    if (errors > 0) {
      toast.showWarning(t('{deleted} gelöscht, {errors} Fehler', { deleted, errors }));
    } else {
      toast.showSuccess(t('{deleted} Legacy-Tabellen gelöscht', { deleted }));
    }
    setDroppingBatch(false);
    _fetchLegacy();
  };

  const _toggleSelect = (id: string) => {
    setSelected(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };

  const _toggleAll = () => {
    if (selected.size === allLegacy.length) {
      setSelected(new Set());
    } else {
      setSelected(new Set(allLegacy.map(l => l.id)));
    }
  };

  const columns: ColumnConfig[] = useMemo(() => [
    {
      key: '_select',
      label: '',
      width: 40,
      formatter: (_val: any, row: LegacyTable) => (
        <input
          type="checkbox"
          checked={selected.has(row.id)}
          onChange={() => _toggleSelect(row.id)}
        />
      ),
    },
    {
      key: 'db',
      label: t('Datenbank'),
      sortable: true,
      filterable: true,
      searchable: true,
      width: 200,
      filterOptions: databases,
    },
    {
      key: 'table',
      label: t('Tabelle'),
      sortable: true,
      searchable: true,
      width: 250,
      formatter: (v: string) => (
        <span style={{ color: 'var(--danger-color, #e53e3e)' }}>
          <code>{v}</code>
          <span style={{
            marginLeft: '0.4rem',
            padding: '0.125rem 0.375rem',
            borderRadius: '4px',
            fontSize: '0.625rem',
            fontWeight: 600,
            textTransform: 'uppercase',
            letterSpacing: '0.03em',
            background: 'var(--primary-dark-bg, rgba(242, 88, 67, 0.12))',
            color: 'var(--primary-color, #f25843)',
          }}>
            {t('kein Modell')}
          </span>
        </span>
      ),
    },
    {
      key: 'rowCount',
      label: t('Zeilen (ca.)'),
      type: 'number' as const,
      sortable: true,
      width: 120,
      formatter: (v: number) => _formatNumber(v),
    },
    {
      key: 'sizeBytes',
      label: t('Grösse'),
      type: 'number' as const,
      sortable: true,
      width: 120,
      formatter: (v: number) => _formatBytes(v),
    },
  ], [t, databases, selected]);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
      <ConfirmDialog />

      <div className={styles.filterSection}>
        <div className={styles.headerActions}>
          <button className={styles.secondaryButton} onClick={_fetchLegacy} disabled={loading}>
            <FaSync className={loading ? 'spinning' : ''} /> {t('Scan')}
          </button>
          <label className={styles.checkboxLabel} style={{ fontWeight: 600 }}>
            <input type="checkbox" checked={selected.size === allLegacy.length && allLegacy.length > 0} onChange={_toggleAll} />
            {t('Alle')}
          </label>
          {selected.size > 0 && (
            <button className={styles.dangerButton} onClick={_dropSelected} disabled={droppingBatch || loading}>
              <FaTrashAlt className={droppingBatch ? 'spinning' : ''} />
              {t('Ausgewählte löschen')} ({selected.size})
            </button>
          )}
        </div>
      </div>

      {allLegacy.length > 0 && (
        <div className={styles.infoBox} style={{ background: 'var(--warning-bg, #fffbeb)', borderColor: 'var(--warning-color, #d69e2e)' }}>
          <FaExclamationTriangle style={{ marginRight: 8, color: 'var(--warning-color, #d69e2e)' }} />
          {t('{count} Legacy-Tabellen in {dbs} Datenbanken ({rows} Zeilen, {size})', {
            count: totals.count, dbs: totals.dbs, rows: _formatNumber(totals.rows), size: _formatBytes(totals.size),
          })}
        </div>
      )}

      <div className={styles.tableContainer}>
        <FormGeneratorTable
          data={visibleData}
          columns={columns}
          loading={loading}
          sortable={true}
          searchable={true}
          filterable={true}
          pagination={true}
          pageSize={50}
          selectable={false}
          customActions={[
            {
              id: 'drop',
              icon: <FaTrashAlt />,
              onClick: (row: LegacyTable) => _dropOne(row),
              loading: (row: LegacyTable) => dropping === row.id || droppingBatch,
              title: t('Tabelle löschen'),
            },
          ]}
          hookData={{
            refetch,
            pagination,
            fetchFilterValues,
          }}
          emptyMessage={t('Keine Legacy-Tabellen gefunden')}
        />
      </div>
    </div>
  );
};


// ---------------------------------------------------------------------------
// Page
// ---------------------------------------------------------------------------

export const AdminDatabaseHealthPage: React.FC = () => {
  const { t } = useLanguage();

  const tabs = useMemo(() => [
    {
      id: 'stats',
      label: t('Statistiken'),
      content: <StatsTab />,
    },
    {
      id: 'orphans',
      label: t('Orphan Cleanup'),
      content: <OrphansTab />,
    },
    {
      id: 'legacy',
      label: t('Legacy Cleanup'),
      content: <LegacyCleanupTab />,
    },
    {
      id: 'migration',
      label: t('Migration'),
      content: <MigrationTab />,
    },
  ], [t]);

  return (
    <div className={`${styles.adminPage} ${styles.adminPageFill}`}>
      <div className={styles.pageHeader}>
        <div>
          <h1 className={styles.pageTitle}>{t('Datenbank-Gesundheit')}</h1>
          <p className={styles.pageSubtitle}>{t('Tabellenstatistiken, verwaiste Datensaetze und Migration')}</p>
        </div>
      </div>

      <Tabs tabs={tabs} defaultTabId="stats" />
    </div>
  );
};

export default AdminDatabaseHealthPage;
