import React, { useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router';

import {
  Card,
  Table,
} from 'react-bootstrap';
import StreamStatus from '../../components/StreamStatus';
import KeyboardShortcutsModal from '../../components/KeyboardShortcutsModal';
import { useKeyboardListNavigation } from '../../hooks/useKeyboardListNavigation';
import { useRadiosByProtocol } from '../../contexts/RadiosContext';
import type { MeshCorePacketRecord } from './MeshCoreContext';
import {
  payloadUrlByValue,
  payloadValueByUrl,
  routeUrlByValue,
  routeValueByUrl,
} from './MeshCoreData';
import MeshCorePacketFilters from './MeshCorePacketFilters';
import MeshCorePacketRows, { type MeshCorePacketGroup } from './MeshCorePacketRows';

interface MeshCorePacketTableProps {
  packets: MeshCorePacketRecord[];
  selectedHash: string | null;
  onSelect: (packet: MeshCorePacketRecord) => void;
  onClearSelection: () => void;
  streamReady: boolean;
}

const MeshCorePacketTable: React.FC<MeshCorePacketTableProps> = ({ packets, selectedHash, onSelect, onClearSelection, streamReady }) => {
  const [searchParams, setSearchParams] = useSearchParams();
  const radios = useRadiosByProtocol('meshcore');
  const scrollRef = React.useRef<HTMLDivElement>(null);

  const [filterPayloadTypes, setFilterPayloadTypes] = useState<Set<number>>(() => {
    const payloads = searchParams.get('payloads');
    if (!payloads) return new Set();
    return new Set(payloads.split(',').map((urlName) => payloadValueByUrl[urlName]).filter((v) => v !== undefined));
  });

  const [filterRouteTypes, setFilterRouteTypes] = useState<Set<number>>(() => {
    const routes = searchParams.get('routes');
    if (!routes) return new Set();
    return new Set(routes.split(',').map((urlName) => routeValueByUrl[urlName]).filter((v) => v !== undefined));
  });

  const [filterRadios, setFilterRadios] = useState<Set<string>>(() => {
    const radiosParam = searchParams.get('radios');
    if (!radiosParam) return new Set();
    return new Set(radiosParam.split(',').map(decodeURIComponent));
  });

  const [expandedHashes, setExpandedHashes] = useState<Set<string>>(new Set());

  useEffect(() => {
    const newParams = new URLSearchParams(searchParams);

    if (filterPayloadTypes.size > 0) {
      newParams.set('payloads', Array.from(filterPayloadTypes).map((v) => payloadUrlByValue[v]).join(','));
    } else {
      newParams.delete('payloads');
    }

    if (filterRouteTypes.size > 0) {
      newParams.set('routes', Array.from(filterRouteTypes).map((v) => routeUrlByValue[v]).join(','));
    } else {
      newParams.delete('routes');
    }

    if (filterRadios.size > 0) {
      newParams.set('radios', Array.from(filterRadios).map(encodeURIComponent).join(','));
    } else {
      newParams.delete('radios');
    }

    setSearchParams(newParams, { replace: true });
  }, [filterPayloadTypes, filterRouteTypes, filterRadios, searchParams, setSearchParams]);

  const uniquePayloadTypes = useMemo(() => {
    const types = new Set(packets.map((packet) => packet.payloadType));
    return Array.from(types).sort((a, b) => a - b);
  }, [packets]);

  const uniqueRouteTypes = useMemo(() => {
    const types = new Set(packets.map((packet) => packet.routeType));
    return Array.from(types).sort((a, b) => a - b);
  }, [packets]);

  const uniqueRadioNames = useMemo(() => {
    const namesFromPackets = new Set(packets.map((packet) => packet.radioName).filter((name): name is string => !!name));
    const namesFromAPI = new Set(radios.map((radio) => radio.name));
    const allNames = new Set([...namesFromPackets, ...namesFromAPI]);
    return Array.from(allNames).sort();
  }, [packets, radios]);

  const groupedPackets = useMemo((): MeshCorePacketGroup[] => {
    const groups = new Map<string, MeshCorePacketRecord[]>();

    packets.forEach((packet) => {
      if (filterPayloadTypes.size > 0 && !filterPayloadTypes.has(packet.payloadType)) return;
      if (filterRouteTypes.size > 0 && !filterRouteTypes.has(packet.routeType)) return;
      if (filterRadios.size > 0 && packet.radioName && !filterRadios.has(packet.radioName)) return;

      const existing = groups.get(packet.hash);
      if (existing) {
        existing.push(packet);
      } else {
        groups.set(packet.hash, [packet]);
      }
    });

    return Array.from(groups.entries())
      .map(([hash, grouped]) => ({
        hash,
        packets: grouped.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()),
        mostRecent: grouped.reduce((latest, packet) => (
          packet.timestamp > latest.timestamp ? packet : latest
        )),
      }))
      .sort((a, b) => b.mostRecent.timestamp.getTime() - a.mostRecent.timestamp.getTime());
  }, [packets, filterPayloadTypes, filterRouteTypes, filterRadios]);

  const handlePayloadTypeToggle = (value: number, isChecked: boolean) => {
    const newSet = new Set(filterPayloadTypes);
    if (isChecked) {
      newSet.add(value);
    } else {
      newSet.delete(value);
    }
    setFilterPayloadTypes(newSet);
  };

  const handleRouteTypeToggle = (value: number, isChecked: boolean) => {
    const newSet = new Set(filterRouteTypes);
    if (isChecked) {
      newSet.add(value);
    } else {
      newSet.delete(value);
    }
    setFilterRouteTypes(newSet);
  };

  const handleRadioToggle = (value: string, isChecked: boolean) => {
    const newSet = new Set(filterRadios);
    if (isChecked) {
      newSet.add(value);
    } else {
      newSet.delete(value);
    }
    setFilterRadios(newSet);
  };

  const toggleExpanded = (hash: string) => {
    const newExpanded = new Set(expandedHashes);
    if (newExpanded.has(hash)) {
      newExpanded.delete(hash);
    } else {
      newExpanded.add(hash);
    }
    setExpandedHashes(newExpanded);
  };

  const navigablePackets = useMemo(() => groupedPackets.map((group) => group.mostRecent), [groupedPackets]);
  const selectedIndex = useMemo(() => {
    if (!selectedHash) {
      return null;
    }
    const index = navigablePackets.findIndex((packet) => packet.hash === selectedHash);
    return index >= 0 ? index : null;
  }, [navigablePackets, selectedHash]);

  const { showShortcuts, setShowShortcuts, shortcuts } = useKeyboardListNavigation({
    itemCount: navigablePackets.length,
    selectedIndex,
    onSelectIndex: (index) => {
      if (index === null) {
        onClearSelection();
        return;
      }
      const packet = navigablePackets[index];
      if (packet) {
        onSelect(packet);
      }
    },
    scrollContainerRef: scrollRef,
  });

  return (
    <Card className="data-table-card h-100 d-flex flex-column">
      <Card.Header className="data-table-header d-flex justify-content-between align-items-center">
        <span>MeshCore Packets</span>
        <div className="d-flex align-items-center gap-2">
          <StreamStatus ready={streamReady} />
        </div>
      </Card.Header>
      <Card.Body className="data-table-body p-0 d-flex flex-column">
        <MeshCorePacketFilters
          uniquePayloadTypes={uniquePayloadTypes}
          uniqueRadioNames={uniqueRadioNames}
          uniqueRouteTypes={uniqueRouteTypes}
          filterPayloadTypes={filterPayloadTypes}
          filterRadios={filterRadios}
          filterRouteTypes={filterRouteTypes}
          onPayloadToggle={handlePayloadTypeToggle}
          onPayloadSelectAll={() => setFilterPayloadTypes(new Set())}
          onRadioToggle={handleRadioToggle}
          onRadioSelectAll={() => setFilterRadios(new Set())}
          onRouteToggle={handleRouteTypeToggle}
          onRouteSelectAll={() => setFilterRouteTypes(new Set())}
        />
        <div className="data-table-scroll" ref={scrollRef}>
          <Table hover responsive className="data-table mb-0" size="sm">
            <thead>
              <tr>
                <th style={{ width: '30px' }}></th>
                <th style={{ width: '100px' }}>Time</th>
                <th style={{ width: '80px' }}>Hash</th>
                <th style={{ width: '50px' }}>Type</th>
                <th>Info</th>
              </tr>
            </thead>
            <tbody>
              <MeshCorePacketRows
                groupedPackets={groupedPackets}
                expandedHashes={expandedHashes}
                onToggleExpanded={toggleExpanded}
                onSelect={onSelect}
                selectedHash={selectedHash}
              />
            </tbody>
          </Table>
        </div>
      </Card.Body>
      <KeyboardShortcutsModal
        show={showShortcuts}
        onHide={() => setShowShortcuts(false)}
        shortcuts={shortcuts}
      />
    </Card>
  );
};

export default MeshCorePacketTable;
