'use client';

import { useEffect, useState, useRef } from 'react';
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
import { Recognition } from '../../types/plates';
import { Pagination } from './Pagination';
import RecognitionRow from './RecognitionRow';
import Table from './Table';
import RecognitionsTableFilters from './RecognitionsTableFilters';
import { useSSE } from './SSEContext';
import Checkbox from './Checkbox';
import { Button } from './Button';
import Modal from './Modal';
import CheckboxGroup from './CheckboxGroup';
import Image from 'next/image';

type Props = {
  initialSearch: string;
  initialPage: number;
  resetNewMarkers?: boolean;

  /** Optional: von außen gesteuerte Auswahl */
  selected?: Recognition | null;
  onSelect?: (rec: Recognition | null) => void;
};

// Welche Keys sind exportierbar:
type ExportFieldKey =
  | 'id'
  | 'license'
  | 'licenseFormatted'
  | 'country'
  | 'brand'
  | 'model'
  | 'confidence'
  | 'timestampLocal'
  | 'cameraName'
  | 'direction'
  | 'directionDegrees';

const EXPORT_FIELDS: { key: ExportFieldKey; label: string }[] = [
  { key: 'licenseFormatted', label: 'Kennzeichen (formatiert)' },
  { key: 'license',          label: 'Kennzeichen (roh)' },
  { key: 'country',          label: 'Land' },
  { key: 'brand',            label: 'Marke' },
  { key: 'model',            label: 'Modell' },
  { key: 'confidence',       label: 'Treffsicherheit' },
  { key: 'timestampLocal',   label: 'Zeit' },
  { key: 'cameraName',       label: 'Kamera' },
  { key: 'direction',        label: 'Richtung' },
  { key: 'directionDegrees', label: 'Richtung (°)' },
  { key: 'id',               label: 'ID' },
];

const HIDDEN_FROM_GRID: ExportFieldKey[] = ['directionDegrees'];

const LABELS: Record<ExportFieldKey, string> =
  Object.fromEntries(EXPORT_FIELDS.map(f => [f.key, f.label])) as Record<ExportFieldKey, string>;

function applyCoupling(keys: ExportFieldKey[]): ExportFieldKey[] {
  const s = new Set<ExportFieldKey>(keys);
  if (s.has('direction')) s.add('directionDegrees');
  else s.delete('directionDegrees');
  return Array.from(s);
}

export default function RecognitionsTable({
  resetNewMarkers,
  initialPage,
  initialSearch,
  selected,
  onSelect,
}: Props) {
  const [data, setData] = useState<Recognition[]>([]);
  const [searchTerm, setSearchTerm] = useState(initialSearch);
  const [currentPage, setCurrentPage] = useState(initialPage);
  const [totalPages, setTotalPages] = useState(1);
  const [newestId, setNewestId] = useState<number | null>(null);
  const [dateRange, setDateRange] = useState<{ from: Date | null; to: Date | null }>({
    from: null,
    to: null
  });
  const [directionFilter, setDirectionFilter] = useState<string>('');
  const [cameraFilter, setCameraFilter] = useState<string>(''); // '' = alle
  const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());

  const [exportRunning, setExportRunning] = useState(false);
  const [exportStage, setExportStage] = useState<string>('');
  const [exportCounts, setExportCounts] = useState<{done:number,total:number}|null>(null);
  const [exportProgress, setExportProgress] = useState<number>(0);
  const [exportError, setExportError] = useState<string | null>(null);

  // controlled/uncontrolled selection
  const [internalSelected, setInternalSelected] = useState<Recognition | null>(null);
  const controlled = typeof onSelect === 'function' || selected !== undefined;
  const selectedRow = controlled ? (selected ?? null) : internalSelected;
  const setSelectedRow = controlled ? (onSelect as (r: Recognition | null) => void) : setInternalSelected;

  const selectAllFields   = () =>
    setExportFields(applyCoupling(EXPORT_FIELDS.map(f => f.key)));

  const DEFAULT_EXPORT_FIELDS: ExportFieldKey[] = [
    'licenseFormatted','country','brand','model',
    'confidence','timestampLocal','cameraName','direction'
  ];

  const selectDefault     = () =>
    setExportFields(applyCoupling(DEFAULT_EXPORT_FIELDS));

  const [exportOpen, setExportOpen] = useState(false);
  const [exportFormat, setExportFormat] = useState<'csv' | 'json' | 'pdf'>('csv');
  const [exportFields, setExportFields] = useState<ExportFieldKey[]>(DEFAULT_EXPORT_FIELDS);

  const onToggleFieldCoupled = (key: ExportFieldKey, checked: boolean) => {
    setExportFields(prev => {
      const next = new Set(prev);
      if (checked) next.add(key); else next.delete(key);
      return applyCoupling(Array.from(next));
    });
  };

  const [allMatchingSelected, setAllMatchingSelected] = useState(false);
  const [deselectedIds, setDeselectedIds] = useState<Set<number>>(new Set());
  const [totalMatching, setTotalMatching] = useState<number>(0);

  const masterRef = useRef<HTMLInputElement>(null);

  const { onNewRecognition, onExportProgress } = useSSE();
  const exportJobIdRef = useRef<string | null>(null);
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const itemsPerPage = 10;
  const pageStart = totalMatching === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1;
  const pageEnd   = Math.min(currentPage * itemsPerPage, totalMatching);

  /** Icons */
  const CsvImg = (
    <Image src="/assets/img/csv.svg" alt="CSV" width={32} height={32} className="h-8 w-8 object-contain" />
  );
  const JsonImg = (
    <Image src="/assets/img/json.svg" alt="JSON" width={32} height={32} className="h-8 w-8 object-contain" />
  );
  const PdfImg = (
    <Image src="/assets/img/pdf.svg" alt="PDF" width={32} height={32} className="h-8 w-8 object-contain" />
  );

  // Auswahl bei Filterwechsel leeren (optional)
  useEffect(() => {
    setSelectedIds(new Set());
  }, [searchTerm, dateRange, directionFilter]);

  // SSE-Listener für Export-Fortschritt
  useEffect(() => {
    if (!onExportProgress) return;
    const off = onExportProgress((msg: { jobId?: string; stage?: string; done?: number; total?: number; progress?: number }) => {
      if (!msg || msg.jobId !== exportJobIdRef.current) return;
      setExportCounts({ done: msg.done ?? 0, total: msg.total ?? 0 });
      if (msg.stage) setExportStage(msg.stage);

      if (typeof msg.progress === 'number') {
        setExportProgress(Math.max(1, Math.min(99, Math.round(msg.progress))));
      } else if ((msg.done ?? 0) > 0 && (msg.total ?? 0) > 0) {
        const p = Math.round(((msg.done as number) / (msg.total as number)) * 98);
        setExportProgress(Math.max(1, Math.min(99, p)));
      }
    });
    return off;
  }, [onExportProgress]);

  // Query-Parameter einlesen
  useEffect(() => {
    const pageParam     = searchParams.get('page');
    const searchParam   = searchParams.get('search');
    const from          = searchParams.get('timestampFrom');
    const to            = searchParams.get('timestampTo');
    const directionParam= searchParams.get('direction');
    const cameraParam   = searchParams.get('camera');

    if (pageParam) setCurrentPage(parseInt(pageParam));
    if (searchParam) setSearchTerm(searchParam);
    if (from || to) {
      setDateRange({
        from: from ? new Date(from) : null,
        to  : to   ? new Date(to)   : null,
      });
    }
    if (directionParam) setDirectionFilter(directionParam);
    if (cameraParam !== null) setCameraFilter(cameraParam); // '' oder Name
  }, [searchParams]);

  // URL aktuell halten
  useEffect(() => {
    const params = new URLSearchParams();
    if (searchTerm)             params.set('search', searchTerm);
    if (currentPage > 1)        params.set('page', String(currentPage));
    if (dateRange.from)         params.set('timestampFrom', dateRange.from.toISOString());
    if (dateRange.to)           params.set('timestampTo', dateRange.to.toISOString());
    if (directionFilter)        params.set('direction', directionFilter);
    if (cameraFilter)           params.set('camera', cameraFilter);

    router.replace(`${pathname}?${params.toString()}`);
  }, [searchTerm, currentPage, dateRange, directionFilter, cameraFilter, pathname, router]);

  useEffect(() => {
    if (resetNewMarkers) {
      setNewestId(null);
    }
  }, [resetNewMarkers]);

  useEffect(() => {
    const query = new URLSearchParams({
      page: String(currentPage),
      limit: String(itemsPerPage),
      search: searchTerm,
      direction: directionFilter,
    });

    if (dateRange.from) query.set('timestampFrom', dateRange.from.toISOString());
    if (dateRange.to) query.set('timestampTo', dateRange.to.toISOString());
    if (directionFilter) query.set('direction', directionFilter);
    if (cameraFilter)   query.set('camera', cameraFilter);

    fetch(`/api/recognitions?${query}`, { credentials: "include", method: "GET" })
      .then((res) => res.json())
      .then((json) => {
        if (Array.isArray(json.data)) {
          setData(json.data);
          setTotalPages(json.totalPages || 1);
          setTotalMatching(json.totalCount || 0);
        } else {
          setData([]);
          setTotalPages(1);
          setTotalMatching(0);
        }
      })
      .catch((err) => {
        console.error('❌ API-Aufruf fehlgeschlagen:', err);
        setData([]);
        setTotalPages(1);
      });
  }, [currentPage, searchTerm, dateRange, directionFilter, cameraFilter]);

  useEffect(() => {
    let timeoutId: number | null = null;

    const off = onNewRecognition((newEntry: Recognition) => {
      if (currentPage !== 1) return;

      setData(prev => {
        if (prev.some(entry => entry.id === newEntry.id)) return prev;
        return [newEntry, ...prev].slice(0, itemsPerPage);
      });

      fetch(`/api/recognitions/count`, { credentials: 'include' })
        .then(res => res.json())
        .then(({ count }) => {
          const pages = Math.max(1, Math.ceil(count / itemsPerPage));
          setTotalPages(pages);
        })
        .catch(console.error);

      setNewestId(newEntry.id);

      if (timeoutId) window.clearTimeout(timeoutId);
      timeoutId = window.setTimeout(() => {
        setNewestId(prevId => (prevId === newEntry.id ? null : prevId));
      }, 2000);
    });

    return () => {
      off();
      if (timeoutId) clearTimeout(timeoutId);
    };
  }, [currentPage, onNewRecognition]);

  const goToPage = (page: number) => {
    setCurrentPage(Math.max(1, Math.min(page, totalPages)));
  };

  // Hilfsfunktionen
  const isSelected = (id: number) =>
    allMatchingSelected ? !deselectedIds.has(id) : selectedIds.has(id);

  const toggleRow = (id: number) => {
    if (allMatchingSelected) {
      setDeselectedIds(prev => {
        const next = new Set(prev);
        if (next.has(id)) next.delete(id);
        else next.add(id);
        return next;
      });
    } else {
      setSelectedIds(prev => {
        const next = new Set(prev);
        if (next.has(id)) next.delete(id);
        else next.add(id);
        return next;
      });
    }
  };

  const allOnPageSelected =
    data.length > 0 && data.every(r => isSelected(r.id));
  const someOnPageSelected =
    data.some(r => isSelected(r.id)) && !allOnPageSelected;

  const toggleAllOnPage = (checked: boolean) => {
    if (allMatchingSelected) {
      setDeselectedIds(prev => {
        const next = new Set(prev);
        for (const row of data) {
          if (checked) next.delete(row.id); // Seite vollständig auswählen
          else next.add(row.id);            // Seite vollständig abwählen
        }
        return next;
      });
    } else {
      setSelectedIds(prev => {
        const next = new Set(prev);
        for (const row of data) {
          if (checked) next.add(row.id);
          else next.delete(row.id);
        }
        return next;
      });
    }
  };

  const handleExport = async () => {
    if (selectedCount === 0) {
      alert('Keine Einträge ausgewählt.');
      return;
    }

    setExportError(null);

    const filters = {
      search:        searchTerm,
      direction:     directionFilter,
      timestampFrom: dateRange.from?.toISOString() ?? null,
      timestampTo:   dateRange.to?.toISOString() ?? null,
      camera:        cameraFilter || '',
    };

    const selection = allMatchingSelected
      ? ({ mode: 'selected-all-except', exceptIds: Array.from(deselectedIds) } as const)
      : ({ mode: 'selected', ids: Array.from(selectedIds) } as const);

    const jobId = (typeof crypto !== 'undefined' && 'randomUUID' in crypto)
      ? crypto.randomUUID()
      : `job_${Date.now()}_${Math.random().toString(36).slice(2)}`;
    exportJobIdRef.current = jobId;

    const filenameFromDisposition = (h?: string | null, fallback = `recognitions.${exportFormat}`) => {
      if (!h) return fallback;
      const m = /filename\*?=(?:UTF-8''|")?([^\";]+)/i.exec(h);
      if (!m) return fallback;
      try {
        return decodeURIComponent(m[1].replace(/"/g, ''));
      } catch {
        return m[1].replace(/"/g, '') || fallback;
      }
    };

    try {
      setExportRunning(true);
      setExportProgress(1);
      setExportStage('');
      setExportCounts(null);

      const res = await fetch('/api/recognitions/export', {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          format: exportFormat,
          filters,
          selection,
          fields: applyCoupling(exportFields),
          clientJobId: jobId,
        }),
      });

      const blob = await res.blob();
      const disp = res.headers.get('Content-Disposition');
      const fileName = filenameFromDisposition(disp);

      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = fileName || `recognitions.${exportFormat}`;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(url);

      setExportProgress(100);
      setExportRunning(false);
      setExportStage('');
      setExportCounts(null);
      exportJobIdRef.current = null;

      setExportOpen(false);
    } catch (err) {
      console.error('❌ Export:', err);
      const msg = String(err instanceof Error ? err.message : err);
      setExportError(msg);
      setExportRunning(false);
      setExportProgress(0);
      setExportStage('');
      setExportCounts(null);
      exportJobIdRef.current = null;
    }
  };

  const selectedCount = allMatchingSelected
    ? Math.max(0, (totalMatching || 0) - deselectedIds.size)
    : selectedIds.size;

  useEffect(() => {
    if (masterRef.current) {
      masterRef.current.indeterminate = !allOnPageSelected && someOnPageSelected;
    }
  }, [allOnPageSelected, someOnPageSelected]);

  return (
    <div className="space-y-4">
      {/* Linke Spalte: Filter/Toolbar/Tabelle/Pagination */}
      <div className="min-w-0 flex flex-col">
        <RecognitionsTableFilters
          searchTerm={searchTerm}
          directionFilter={directionFilter}
          selectedCamera={cameraFilter}
          setSearchTerm={setSearchTerm}
          setDirectionFilter={setDirectionFilter}
          setDateRange={setDateRange}
          setCurrentPage={setCurrentPage}
          setSelectedCamera={setCameraFilter}
          dateRange={dateRange}
        />

        {/* Toolbar */}
        <div className="mt-2 mb-2 flex flex-wrap items-center gap-2">
          <span className="text-sm text-gray-500">
            {pageStart}–{pageEnd} von {totalMatching.toLocaleString('de-DE')} Einträgen
          </span>

          {(!allMatchingSelected || selectedCount < totalMatching) ? (
            <Button
              size="small"
              variant="ghost"
              onClick={() => {
                setAllMatchingSelected(true);
                setSelectedIds(new Set());
                setDeselectedIds(new Set());
              }}
            >
              <span className="inline-flex items-center gap-1.5">
                <svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                  <path d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z" />
                  <path d="M8.8 12.2l2.2 2.2 4.2-4.2" />
                </svg>
                Alle Einträge auswählen
              </span>
            </Button>
          ) : (
            <Button
              size="small"
              variant="ghost"
              onClick={() => {
                setAllMatchingSelected(false);
                setSelectedIds(new Set());
                setDeselectedIds(new Set());
              }}
            >
              <span className="inline-flex items-center gap-1.5">
                <svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                  <path d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z" />
                  <path d="M15 9l-6 6M9 9l6 6" />
                </svg>
                Auswahl leeren
              </span>
            </Button>
          )}

          {(selectedCount > 0 || allMatchingSelected) && (
            <>
              <span className="ml-auto text-sm text-gray-500">
                {selectedCount.toLocaleString('de-DE')} {selectedCount === 1 ? 'Eintrag' : 'Einträge'} ausgewählt
              </span>

              <div className="flex gap-2">
                <Button
                  size="small"
                  variant="ghost"
                  onClick={() => {
                    setAllMatchingSelected(false);
                    setSelectedIds(new Set());
                    setDeselectedIds(new Set());
                  }}
                >
                  <span className="inline-flex items-center gap-1.5">
                    <svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                      <path d="M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z" />
                      <path d="M15 9l-6 6M9 9l6 6" />
                    </svg>
                    Auswahl aufheben
                  </span>
                </Button>

                <Button
                  size="small"
                  variant="ghost"
                  onClick={() => setExportOpen(true)}
                >
                  <span className="inline-flex items-center gap-1.5">
                    <svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                      <path d="M12 3v12" />
                      <path d="M8.25 11.25 12 15l3.75-3.75" />
                      <path d="M3 16.5V19a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-2.5" />
                    </svg>
                    Exportieren
                  </span>
                </Button>
              </div>
            </>
          )}
        </div>

        {/* Tabelle */}
        <Table>
          <Table.Head>
            <Table.Row>
              <Table.Cell header align="center">
                <Checkbox
                  checked={allOnPageSelected}
                  indeterminate={!allOnPageSelected && someOnPageSelected}
                  onChange={(e) => toggleAllOnPage(e.currentTarget.checked)}
                  label={<span className="sr-only">Alle auf dieser Seite auswählen</span>}
                  containerClassName="justify-center"
                />
              </Table.Cell>
              <Table.Cell>Kennzeichen</Table.Cell>
              <Table.Cell>Land</Table.Cell>
              <Table.Cell>Marke</Table.Cell>
              <Table.Cell>Modell</Table.Cell>
              <Table.Cell>Treffsicherheit</Table.Cell>
              <Table.Cell colSpan={2}>Richtung</Table.Cell>
              <Table.Cell>Zeit</Table.Cell>
              <Table.Cell>Kamera</Table.Cell>
            </Table.Row>
          </Table.Head>
          <Table.Body>
            {data.map((entry) => (
              <RecognitionRow
                key={entry.id}
                entry={entry}
                isSelected={selectedRow?.id === entry.id}
                isNew={newestId === entry.id}
                onClick={() =>
                  setSelectedRow(selectedRow?.id === entry.id ? null : entry)
                }
                checked={isSelected(entry.id)}
                onToggle={() => toggleRow(entry.id)}
              />
            ))}
            {data.length === 0 && (
              <tr>
                <td colSpan={10} className="p-5 text-center text-gray-500 dark:text-neutral-400">
                  Keine Daten gefunden.
                </td>
              </tr>
            )}
          </Table.Body>
        </Table>

        <Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={goToPage} />
      </div>

      {/* Export-Modal */}
      <Modal
        open={exportOpen}
        onClose={() => {
          if (exportRunning) return;
          setExportOpen(false);
          setExportError(null);
        }}
        title="Ausgewählte Einträge exportieren"
        saveButton
        onSave={handleExport}
        maxWidth="max-w-xl"
        busy={exportRunning}
      >
        <div className="space-y-6">
          {exportError && (
            <div
              role="alert"
              aria-live="polite"
              className="rounded-md border border-red-300 bg-red-50 p-3 text-sm text-red-800"
            >
              <div className="font-medium mb-1">Export fehlgeschlagen</div>
              <pre className="whitespace-pre-wrap break-words text-xs m-0">{exportError}</pre>
            </div>
          )}

          <section className="rounded-lg border border-gray-200 dark:border-neutral-700 p-4">
            <div className="flex items-center justify-between gap-3 mb-3">
              <h3 className="font-medium text-base text-gray-900 dark:text-neutral-100">
                Welche Informationen sollen exportiert werden?
              </h3>
              <div className="flex gap-1">
                <Button size="small" variant="ghost" onClick={selectAllFields} disabled={exportRunning}>Alle</Button>
                <Button size="small" variant="ghost" onClick={selectDefault}   disabled={exportRunning}>Standard</Button>
              </div>
            </div>

            <div className="mb-3 flex flex-wrap gap-2">
              {exportFields.length === 0 ? (
                <span className="text-xs text-gray-500 dark:text-neutral-400">Keine Felder ausgewählt</span>
              ) : (
                exportFields.map(k => (
                  <span
                    key={k}
                    className="text-xs px-2 py-1 rounded border border-gray-300 dark:border-neutral-600 bg-white dark:bg-neutral-800"
                  >
                    {LABELS[k] ?? k}
                  </span>
                ))
              )}
            </div>

            <fieldset className="grid grid-cols-1 sm:grid-cols-2 gap-2">
              {EXPORT_FIELDS
                .filter(f => !HIDDEN_FROM_GRID.includes(f.key))
                .map(f => (
                  <Checkbox
                    key={f.key}
                    checked={exportFields.includes(f.key)}
                    onChange={e => onToggleFieldCoupled(f.key, e.currentTarget.checked)}
                    label={f.label}
                    disabled={exportRunning}
                  />
              ))}
            </fieldset>
          </section>

          <section className="rounded-lg border border-gray-200 dark:border-neutral-700 p-4">
            <h3 className="mb-3 font-medium text-base text-gray-900 dark:text-neutral-100">
              Format wählen
            </h3>

            <div className="grid sm:grid-cols-3 gap-2 max-w-none">
              <CheckboxGroup
                multiple={false}
                orientation="vertical"
                value={[exportFormat]}
                onChange={(vals) => setExportFormat((vals[0] as 'csv' | 'json' | 'pdf') ?? 'csv')}
                options={[
                  { value: 'csv', label: 'CSV',  description: 'Trennzeichen: Semikolon (;)', icon: CsvImg },
                  { value: 'pdf', label: 'PDF',
                    description: exportRunning && exportCounts
                      ? `${exportCounts.done}/${exportCounts.total} – ${exportStage || 'erzeuge…'}`
                      : `${selectedCount} ${selectedCount===1?'Seite':'Seiten'}`,
                    icon: PdfImg
                  },
                  { value: 'json', label: 'JSON', icon: JsonImg },
                ]}
                className="sm:col-span-3 max-w-full"
                itemClassName="w-full"
                progressByValue={exportRunning ? { [exportFormat]: exportProgress } : undefined}
                disabled={exportRunning}
              />
            </div>
          </section>
        </div>
      </Modal>
    </div>
  );
}
