/**
 * AuditLog — Admin page
 *
 * Displays the immutable audit trail with filtering, pagination, and CSV export.
 * Uses server-side pagination via the DataGrid's paginationMode="server" prop.
 *
 * Required packages (add to frontend/package.json dependencies):
 *   "@mui/x-data-grid": "^6.x || ^7.x"
 *   "@mui/x-date-pickers": "^6.x || ^7.x"
 *   "date-fns": "^3.x"
 *   "@date-io/date-fns": "^3.x"    (adapter for MUI date pickers)
 *
 * Install:
 *   npm install @mui/x-data-grid @mui/x-date-pickers date-fns @date-io/date-fns
 *
 * Route registration in App.tsx:
 *   import AuditLog from './pages/admin/AuditLog';
 *   // Inside <Routes>:
 *   <Route
 *     path="/admin/audit-log"
 *     element={
 *       <ProtectedRoute requireRole="admin">
 *         <AuditLog />
 *       </ProtectedRoute>
 *     }
 *   />
 */

import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
  Alert,
  Autocomplete,
  Box,
  Button,
  Chip,
  CircularProgress,
  Container,
  Dialog,
  DialogContent,
  DialogTitle,
  Divider,
  IconButton,
  Paper,
  Skeleton,
  Stack,
  TextField,
  Tooltip,
  Typography,
} from '@mui/material';
import {
  DataGrid,
  GridColDef,
  GridPaginationModel,
  GridRenderCellParams,
  GridRowParams,
} from '@mui/x-data-grid';
import { de } from 'date-fns/locale';
import { format, parseISO } from 'date-fns';
import CloseIcon from '@mui/icons-material/Close';
import DownloadIcon from '@mui/icons-material/Download';
import FilterAltIcon from '@mui/icons-material/FilterAlt';
import DashboardLayout from '../../components/dashboard/DashboardLayout';
import { api } from '../../services/api';
import { fromGermanDate } from '../../utils/dateInput';

// ---------------------------------------------------------------------------
// Types — mirror the backend AuditLogEntry interface
// ---------------------------------------------------------------------------

type AuditAction =
  | 'CREATE' | 'UPDATE' | 'DELETE'
  | 'LOGIN' | 'LOGOUT' | 'EXPORT'
  | 'PERMISSION_DENIED' | 'PASSWORD_CHANGE' | 'ROLE_CHANGE';

type AuditResourceType =
  | 'MEMBER' | 'INCIDENT' | 'VEHICLE' | 'EQUIPMENT'
  | 'QUALIFICATION' | 'USER' | 'SYSTEM';

interface AuditLogEntry {
  id:            string;
  user_id:       string | null;
  user_email:    string | null;
  action:        AuditAction;
  resource_type: AuditResourceType;
  resource_id:   string | null;
  old_value:     Record<string, unknown> | null;
  new_value:     Record<string, unknown> | null;
  ip_address:    string | null;
  user_agent:    string | null;
  metadata:      Record<string, unknown>;
  created_at:    string;  // ISO string from JSON
}

interface AuditLogPage {
  entries: AuditLogEntry[];
  total:   number;
  page:    number;
  pages:   number;
}

interface AuditFilters {
  userId?:       string;
  action?:       AuditAction[];
  resourceType?: AuditResourceType[];
  dateFrom?:     string;
  dateTo?:       string;
}

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const ALL_ACTIONS: AuditAction[] = [
  'CREATE', 'UPDATE', 'DELETE', 'LOGIN', 'LOGOUT',
  'EXPORT', 'PERMISSION_DENIED', 'PASSWORD_CHANGE', 'ROLE_CHANGE',
];

const ALL_RESOURCE_TYPES: AuditResourceType[] = [
  'MEMBER', 'INCIDENT', 'VEHICLE', 'EQUIPMENT',
  'QUALIFICATION', 'USER', 'SYSTEM',
];

// ---------------------------------------------------------------------------
// Action chip colour map
// ---------------------------------------------------------------------------

const ACTION_COLORS: Record<AuditAction, 'success' | 'primary' | 'error' | 'warning' | 'default' | 'info'> = {
  CREATE:           'success',
  UPDATE:           'primary',
  DELETE:           'error',
  LOGIN:            'info',
  LOGOUT:           'default',
  EXPORT:           'warning',
  PERMISSION_DENIED:'error',
  PASSWORD_CHANGE:  'warning',
  ROLE_CHANGE:      'warning',
};

// ---------------------------------------------------------------------------
// Utility helpers
// ---------------------------------------------------------------------------

function formatTimestamp(isoString: string): string {
  try {
    return format(parseISO(isoString), 'dd.MM.yyyy HH:mm:ss', { locale: de });
  } catch {
    return isoString;
  }
}

function truncate(value: string | null | undefined, maxLength = 24): string {
  if (!value) return '—';
  return value.length > maxLength ? value.substring(0, maxLength) + '…' : value;
}

// ---------------------------------------------------------------------------
// JSON diff viewer (simple before / after display)
// ---------------------------------------------------------------------------

interface JsonDiffViewerProps {
  oldValue: Record<string, unknown> | null;
  newValue: Record<string, unknown> | null;
}

const JsonDiffViewer: React.FC<JsonDiffViewerProps> = ({ oldValue, newValue }) => {
  if (!oldValue && !newValue) {
    return <Typography variant="body2" color="text.secondary">Keine Datenaenderung aufgezeichnet.</Typography>;
  }

  const allKeys = Array.from(
    new Set([
      ...Object.keys(oldValue ?? {}),
      ...Object.keys(newValue ?? {}),
    ])
  ).sort();

  return (
    <Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
      {oldValue && (
        <Box sx={{ flex: 1, minWidth: 240 }}>
          <Typography variant="overline" color="error.main">Vorher</Typography>
          <Paper
            variant="outlined"
            sx={{
              p: 1.5, mt: 0.5,
              backgroundColor: 'error.light',
              fontFamily: 'monospace',
              fontSize: '0.75rem',
              overflowX: 'auto',
              maxHeight: 320,
            }}
          >
            <pre style={{ margin: 0 }}>
              {JSON.stringify(oldValue, null, 2)}
            </pre>
          </Paper>
        </Box>
      )}
      {newValue && (
        <Box sx={{ flex: 1, minWidth: 240 }}>
          <Typography variant="overline" color="success.main">Nachher</Typography>
          <Paper
            variant="outlined"
            sx={{
              p: 1.5, mt: 0.5,
              backgroundColor: 'success.light',
              fontFamily: 'monospace',
              fontSize: '0.75rem',
              overflowX: 'auto',
              maxHeight: 320,
            }}
          >
            <pre style={{ margin: 0 }}>
              {JSON.stringify(newValue, null, 2)}
            </pre>
          </Paper>
        </Box>
      )}
      {/* Highlight changed fields */}
      {oldValue && newValue && allKeys.length > 0 && (
        <Box sx={{ width: '100%', mt: 1 }}>
          <Typography variant="overline">Geaenderte Felder</Typography>
          <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5, mt: 0.5 }}>
            {allKeys.map((key) => {
              const changed =
                JSON.stringify((oldValue as Record<string, unknown>)[key]) !==
                JSON.stringify((newValue as Record<string, unknown>)[key]);
              if (!changed) return null;
              return (
                <Chip
                  key={key}
                  label={key}
                  size="small"
                  color="warning"
                  variant="outlined"
                />
              );
            })}
          </Box>
        </Box>
      )}
    </Box>
  );
};

// ---------------------------------------------------------------------------
// Entry detail dialog
// ---------------------------------------------------------------------------

interface EntryDialogProps {
  entry: AuditLogEntry | null;
  onClose: () => void;
  showIp: boolean;
}

const EntryDialog: React.FC<EntryDialogProps> = ({ entry, onClose, showIp }) => {
  if (!entry) return null;

  return (
    <Dialog open={!!entry} onClose={onClose} maxWidth="md" fullWidth>
      <DialogTitle>
        <Stack direction="row" alignItems="center" justifyContent="space-between">
          <Typography variant="h6">
            Audit-Eintrag — {entry.action} / {entry.resource_type}
          </Typography>
          <IconButton onClick={onClose} size="small" aria-label="Schliessen">
            <CloseIcon />
          </IconButton>
        </Stack>
      </DialogTitle>
      <DialogContent dividers>
        <Stack spacing={2}>
          <Box>
            <Typography variant="overline">Metadaten</Typography>
            <Box
              sx={{
                display: 'grid',
                gridTemplateColumns: 'auto 1fr',
                gap: '4px 16px',
                mt: 0.5,
              }}
            >
              {[
                ['Zeitpunkt',     formatTimestamp(entry.created_at)],
                ['Benutzer',      entry.user_email ?? '—'],
                ['Benutzer-ID',   entry.user_id    ?? '—'],
                ['Aktion',        entry.action],
                ['Ressourcentyp', entry.resource_type],
                ['Ressourcen-ID', entry.resource_id ?? '—'],
                ...(showIp ? [['IP-Adresse', entry.ip_address ?? '—']] : []),
              ].map(([label, value]) => (
                <React.Fragment key={label}>
                  <Typography variant="body2" color="text.secondary" sx={{ whiteSpace: 'nowrap' }}>
                    {label}:
                  </Typography>
                  <Typography variant="body2" sx={{ wordBreak: 'break-all' }}>
                    {value}
                  </Typography>
                </React.Fragment>
              ))}
            </Box>
          </Box>

          {Object.keys(entry.metadata ?? {}).length > 0 && (
            <Box>
              <Typography variant="overline">Zusatzdaten</Typography>
              <Paper variant="outlined" sx={{ p: 1, mt: 0.5, fontFamily: 'monospace', fontSize: '0.75rem' }}>
                <pre style={{ margin: 0 }}>{JSON.stringify(entry.metadata, null, 2)}</pre>
              </Paper>
            </Box>
          )}

          <Divider />

          <Box>
            <Typography variant="overline">Datenaenderung</Typography>
            <Box sx={{ mt: 1 }}>
              <JsonDiffViewer oldValue={entry.old_value} newValue={entry.new_value} />
            </Box>
          </Box>
        </Stack>
      </DialogContent>
    </Dialog>
  );
};

// ---------------------------------------------------------------------------
// Filter panel
// ---------------------------------------------------------------------------

interface FilterPanelProps {
  filters:   AuditFilters;
  onChange:  (f: AuditFilters) => void;
  onReset:   () => void;
}

const FilterPanel: React.FC<FilterPanelProps> = ({ filters, onChange, onReset }) => {
  return (
    <Paper variant="outlined" sx={{ p: 2, mb: 2 }}>
      <Stack spacing={2}>
        <Typography variant="subtitle2" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
          <FilterAltIcon fontSize="small" />
          Filter
        </Typography>

        <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2 }}>
          {/* Date range */}
          <TextField
            label="Von"
            size="small"
            placeholder="TT.MM.JJJJ"
            value={filters.dateFrom ?? ''}
            onChange={(e) => onChange({ ...filters, dateFrom: e.target.value })}
            sx={{ minWidth: 160 }}
            InputLabelProps={{ shrink: true }}
          />
          <TextField
            label="Bis"
            size="small"
            placeholder="TT.MM.JJJJ"
            value={filters.dateTo ?? ''}
            onChange={(e) => onChange({ ...filters, dateTo: e.target.value })}
            sx={{ minWidth: 160 }}
            InputLabelProps={{ shrink: true }}
          />

          {/* Action multi-select */}
          <Autocomplete
            multiple
            options={ALL_ACTIONS}
            value={filters.action ?? []}
            onChange={(_, value) => onChange({ ...filters, action: value as AuditAction[] })}
            renderInput={(params) => (
              <TextField {...params} label="Aktionen" size="small" sx={{ minWidth: 200 }} />
            )}
            renderTags={(value, getTagProps) =>
              value.map((option, index) => (
                <Chip
                  {...getTagProps({ index })}
                  key={option}
                  label={option}
                  size="small"
                  color={ACTION_COLORS[option]}
                />
              ))
            }
          />

          {/* Resource type multi-select */}
          <Autocomplete
            multiple
            options={ALL_RESOURCE_TYPES}
            value={filters.resourceType ?? []}
            onChange={(_, value) => onChange({ ...filters, resourceType: value as AuditResourceType[] })}
            renderInput={(params) => (
              <TextField {...params} label="Ressourcentypen" size="small" sx={{ minWidth: 200 }} />
            )}
            renderTags={(value, getTagProps) =>
              value.map((option, index) => (
                <Chip {...getTagProps({ index })} key={option} label={option} size="small" />
              ))
            }
          />

          <Button variant="outlined" size="small" onClick={onReset} sx={{ alignSelf: 'flex-end' }}>
            Zuruecksetzen
          </Button>
        </Box>
      </Stack>
    </Paper>
  );
};

// ---------------------------------------------------------------------------
// Main page component
// ---------------------------------------------------------------------------

const DEFAULT_FILTERS: AuditFilters = {
  action:       [],
  resourceType: [],
  dateFrom:     '',
  dateTo:       '',
};

const AuditLog: React.FC = () => {
  // Grid state
  const [paginationModel, setPaginationModel] = useState<GridPaginationModel>({
    page:     0,   // DataGrid is 0-based
    pageSize: 25,
  });
  const [rowCount, setRowCount]     = useState(0);
  const [rows, setRows]             = useState<AuditLogEntry[]>([]);
  const [loading, setLoading]       = useState(false);
  const [error, setError]           = useState<string | null>(null);

  // Filters
  const [filters, setFilters]       = useState<AuditFilters>(DEFAULT_FILTERS);
  const [appliedFilters, setApplied]= useState<AuditFilters>(DEFAULT_FILTERS);

  // Detail dialog
  const [selectedEntry, setSelected]= useState<AuditLogEntry | null>(null);

  // Export state
  const [exporting, setExporting]   = useState(false);

  // The admin always sees IPs — toggle this based on role check if needed
  const showIp = true;

  // -------------------------------------------------------------------------
  // Data fetching
  // -------------------------------------------------------------------------

  const fetchData = useCallback(async (
    pagination: GridPaginationModel,
    f: AuditFilters,
  ) => {
    setLoading(true);
    setError(null);
    try {
      const params = new URLSearchParams();
      params.set('page', String(pagination.page + 1));
      params.set('pageSize', String(pagination.pageSize));

      if (f.dateFrom) {
        const iso = fromGermanDate(f.dateFrom);
        if (iso) params.set('dateFrom', new Date(iso).toISOString());
      }
      if (f.dateTo) {
        const iso = fromGermanDate(f.dateTo);
        if (iso) params.set('dateTo', new Date(iso + 'T23:59:59').toISOString());
      }
      if (f.action && f.action.length > 0) {
        f.action.forEach((a) => params.append('action', a));
      }
      if (f.resourceType && f.resourceType.length > 0) {
        f.resourceType.forEach((rt) => params.append('resourceType', rt));
      }
      if (f.userId) params.set('userId', f.userId);

      const queryString = params.toString();
      const response = await api.get<{ success: boolean; data: AuditLogPage }>(
        `/api/admin/audit-log?${queryString}`
      );

      setRows(response.data.data.entries);
      setRowCount(response.data.data.total);
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : 'Unbekannter Fehler';
      setError(`Audit-Log konnte nicht geladen werden: ${msg}`);
    } finally {
      setLoading(false);
    }
  }, []);

  // Fetch when pagination or applied filters change
  useEffect(() => {
    fetchData(paginationModel, appliedFilters);
  }, [paginationModel, appliedFilters, fetchData]);

  // -------------------------------------------------------------------------
  // Filter handlers
  // -------------------------------------------------------------------------

  const handleApplyFilters = () => {
    setApplied(filters);
    setPaginationModel((prev: any) => ({ ...prev, page: 0 }));
  };

  const handleResetFilters = () => {
    setFilters(DEFAULT_FILTERS);
    setApplied(DEFAULT_FILTERS);
    setPaginationModel((prev: any) => ({ ...prev, page: 0 }));
  };

  // -------------------------------------------------------------------------
  // CSV export
  // -------------------------------------------------------------------------

  const handleExport = async () => {
    setExporting(true);
    try {
      const params: Record<string, string> = {};
      if (appliedFilters.dateFrom) {
        const iso = fromGermanDate(appliedFilters.dateFrom);
        if (iso) params.dateFrom = new Date(iso).toISOString();
      }
      if (appliedFilters.dateTo) {
        const iso = fromGermanDate(appliedFilters.dateTo);
        if (iso) params.dateTo = new Date(iso + 'T23:59:59').toISOString();
      }
      if (appliedFilters.action   && appliedFilters.action.length   > 0) {
        params.action = appliedFilters.action.join(',');
      }
      if (appliedFilters.resourceType && appliedFilters.resourceType.length > 0) {
        params.resourceType = appliedFilters.resourceType.join(',');
      }

      const queryString = new URLSearchParams(params).toString();
      const response = await api.get<Blob>(
        `/api/admin/audit-log/export?${queryString}`,
        { responseType: 'blob' }
      );

      const url      = URL.createObjectURL(response.data);
      const filename = `audit_log_${format(new Date(), 'yyyy-MM-dd_HH-mm')}.csv`;
      const link     = document.createElement('a');
      link.href      = url;
      link.download  = filename;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);
    } catch {
      setError('CSV-Export fehlgeschlagen. Bitte versuchen Sie es erneut.');
    } finally {
      setExporting(false);
    }
  };

  // -------------------------------------------------------------------------
  // Column definitions
  // -------------------------------------------------------------------------

  const columns: GridColDef<AuditLogEntry>[] = useMemo(() => [
    {
      field:        'created_at',
      headerName:   'Zeitpunkt',
      width:        160,
      valueFormatter: (value: string) => formatTimestamp(value),
      sortable:     false,
    },
    {
      field:        'user_email',
      headerName:   'Benutzer',
      flex:         1,
      minWidth:     160,
      renderCell:   (params: GridRenderCellParams<AuditLogEntry>) =>
        params.value ? (
          <Tooltip title={params.row.user_id ?? ''}>
            <Typography variant="body2" noWrap>{params.value}</Typography>
          </Tooltip>
        ) : (
          <Typography variant="body2" color="text.disabled">—</Typography>
        ),
      sortable: false,
    },
    {
      field:      'action',
      headerName: 'Aktion',
      width:      160,
      renderCell: (params: GridRenderCellParams<AuditLogEntry>) => (
        <Chip
          label={params.value as string}
          size="small"
          color={ACTION_COLORS[params.value as AuditAction] ?? 'default'}
        />
      ),
      sortable: false,
    },
    {
      field:      'resource_type',
      headerName: 'Ressourcentyp',
      width:      140,
      sortable:   false,
    },
    {
      field:      'resource_id',
      headerName: 'Ressourcen-ID',
      width:      130,
      renderCell: (params: GridRenderCellParams<AuditLogEntry>) => (
        <Tooltip title={params.value ?? ''}>
          <Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: '0.75rem' }}>
            {truncate(params.value, 12)}
          </Typography>
        </Tooltip>
      ),
      sortable: false,
    },
    ...(showIp ? [{
      field:      'ip_address',
      headerName: 'IP-Adresse',
      width:      140,
      renderCell: (params: GridRenderCellParams<AuditLogEntry>) => (
        <Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: '0.75rem' }}>
          {params.value ?? '—'}
        </Typography>
      ),
      sortable: false,
    } as GridColDef<AuditLogEntry>] : []),
  ], [showIp]);

  // -------------------------------------------------------------------------
  // Loading skeleton
  // -------------------------------------------------------------------------

  if (loading && rows.length === 0) {
    return (
      <DashboardLayout>
        <Container maxWidth="xl">
          <Skeleton variant="text" width={300} height={48} sx={{ mb: 2 }} />
          <Skeleton variant="rectangular" height={80} sx={{ mb: 2, borderRadius: 1 }} />
          <Skeleton variant="rectangular" height={400} sx={{ borderRadius: 1 }} />
        </Container>
      </DashboardLayout>
    );
  }

  // -------------------------------------------------------------------------
  // Render
  // -------------------------------------------------------------------------

  return (
    <DashboardLayout>
        <Container maxWidth="xl">
          {/* Header */}
          <Stack
            direction="row"
            alignItems="center"
            justifyContent="space-between"
            sx={{ mb: 3 }}
          >
            <Box>
              <Typography variant="h4">Audit-Protokoll</Typography>
              <Typography variant="body2" color="text.secondary">
                DSGVO Art. 5(2) — Unveraenderliches Protokoll aller Datenzugriffe
              </Typography>
            </Box>
            <Button
              variant="contained"
              startIcon={exporting ? <CircularProgress size={16} color="inherit" /> : <DownloadIcon />}
              disabled={exporting}
              onClick={handleExport}
            >
              CSV-Export
            </Button>
          </Stack>

          {/* Error */}
          {error && (
            <Alert severity="error" onClose={() => setError(null)} sx={{ mb: 2 }}>
              {error}
            </Alert>
          )}

          {/* Filter panel */}
          <FilterPanel
            filters={filters}
            onChange={setFilters}
            onReset={handleResetFilters}
          />

          {/* Apply filters button */}
          <Box sx={{ mb: 2, display: 'flex', justifyContent: 'flex-end' }}>
            <Button variant="outlined" size="small" onClick={handleApplyFilters}>
              Filter anwenden
            </Button>
          </Box>

          {/* Data grid */}
          <Paper variant="outlined" sx={{ width: '100%' }}>
            <DataGrid<AuditLogEntry>
              rows={rows}
              columns={columns}
              rowCount={rowCount}
              loading={loading}
              paginationMode="server"
              paginationModel={paginationModel}
              onPaginationModelChange={setPaginationModel}
              pageSizeOptions={[25, 50, 100]}
              disableRowSelectionOnClick={false}
              onRowClick={(params: GridRowParams<AuditLogEntry>) =>
                setSelected(params.row)
              }
              sx={{
                border: 'none',
                '& .MuiDataGrid-row': { cursor: 'pointer' },
                '& .MuiDataGrid-row:hover': {
                  backgroundColor: 'action.hover',
                },
              }}
              localeText={{
                noRowsLabel: 'Keine Eintraege gefunden',
                paginationRowsPerPage: 'Eintraege pro Seite:',
                paginationDisplayedRows: ({ from, to, count }: { from: any; to: any; count: any }) =>
                  `${from}–${to} von ${count !== -1 ? count : `mehr als ${to}`}`,
              }}
              autoHeight
            />
          </Paper>

          {/* Detail dialog */}
          <EntryDialog
            entry={selectedEntry}
            onClose={() => setSelected(null)}
            showIp={showIp}
          />
        </Container>
      </DashboardLayout>
  );
};

export default AuditLog;
