import { useEffect, useMemo, useState } from "react";
import {
  Alert,
  Box,
  Center,
  Group,
  Loader,
  ScrollArea,
  Table,
  Tabs,
  Text,
  TextInput,
  UnstyledButton,
} from "@mantine/core";
import {
  IconChevronDown,
  IconChevronUp,
  IconSearch,
  IconSelector,
} from "@tabler/icons-react";
import { fetchGraph, fetchNodes, type NodeRow } from "../api";
import { formatFrontmatterValue } from "../format";
import { tipoStyle } from "../tipos";
import { useNodeCard } from "../NodeCardContext";

// Una pestaña por tipo de nodo real (no fantasma). Cada pestaña carga
// perezosamente sus filas de /api/nodes?tipo=<t> y las muestra en una tabla
// Mantine ordenable + filtrable. Las columnas se deducen de las claves de
// frontmatter más comunes del tipo.

// Tipos que tienen tabla propia (los nodos reales del vault). Orden de aparición.
const TABLE_TIPOS = [
  "persona",
  "organizacion",
  "lugar",
  "documento",
  "caso",
  "dominio",
];

export function TablesView() {
  const [availableTipos, setAvailableTipos] = useState<string[]>([]);
  const [active, setActive] = useState<string>("persona");
  const [error, setError] = useState<string | null>(null);

  // Determinar qué tipos existen realmente en el vault (de los counts del grafo).
  useEffect(() => {
    let alive = true;
    fetchGraph()
      .then((d) => {
        if (!alive) return;
        const present = TABLE_TIPOS.filter((t) => (d.counts[t] ?? 0) > 0);
        // Añadir cualquier otro tipo real con nodos no contemplado arriba.
        const extra = Object.keys(d.counts).filter(
          (t) => (d.counts[t] ?? 0) > 0 && !TABLE_TIPOS.includes(t) && t !== "nota",
        );
        const all = [...present, ...extra];
        setAvailableTipos(all);
        if (all.length > 0 && !all.includes(active)) setActive(all[0]);
      })
      .catch((e) => alive && setError(String(e)));
    return () => {
      alive = false;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  if (error) {
    return (
      <Center h="100%" p="xl">
        <Alert color="red" title="No se pudieron cargar las tablas">
          {error}
        </Alert>
      </Center>
    );
  }

  if (availableTipos.length === 0) {
    return (
      <Center h="100%">
        <Loader />
      </Center>
    );
  }

  return (
    <Tabs
      value={active}
      onChange={(v) => v && setActive(v)}
      keepMounted={false}
      h="100%"
      style={{ display: "flex", flexDirection: "column" }}
    >
      <Tabs.List px="md" pt="xs">
        {availableTipos.map((t) => (
          <Tabs.Tab
            key={t}
            value={t}
            leftSection={
              <Box
                w={8}
                h={8}
                style={{
                  borderRadius: "50%",
                  background: tipoStyle(t).color,
                }}
              />
            }
          >
            {tipoStyle(t).label}
          </Tabs.Tab>
        ))}
      </Tabs.List>

      {availableTipos.map((t) => (
        <Tabs.Panel
          key={t}
          value={t}
          style={{ flex: 1, minHeight: 0, display: "flex" }}
        >
          {active === t && <TypeTable tipo={t} />}
        </Tabs.Panel>
      ))}
    </Tabs>
  );
}

type SortDir = "asc" | "desc" | null;

function TypeTable({ tipo }: { tipo: string }) {
  const { open } = useNodeCard();
  const [rows, setRows] = useState<NodeRow[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [filter, setFilter] = useState("");
  const [sortCol, setSortCol] = useState<string>("label");
  const [sortDir, setSortDir] = useState<SortDir>("asc");

  useEffect(() => {
    let alive = true;
    setLoading(true);
    fetchNodes(tipo)
      .then((d) => alive && setRows(d.rows))
      .catch((e) => alive && setError(String(e)))
      .finally(() => alive && setLoading(false));
    return () => {
      alive = false;
    };
  }, [tipo]);

  // Columnas: las claves de frontmatter más frecuentes de este conjunto, con
  // `label` (nombre del nodo) siempre primero. Limitado para no desbordar.
  const columns = useMemo(() => {
    const freq: Record<string, number> = {};
    for (const r of rows) {
      for (const k of Object.keys(r.frontmatter)) {
        if (k === "nombre" || k === "tipo") continue; // redundantes con label/tab
        freq[k] = (freq[k] ?? 0) + 1;
      }
    }
    const ranked = Object.entries(freq)
      .sort((a, b) => b[1] - a[1])
      .slice(0, 6)
      .map(([k]) => k);
    return ["label", ...ranked];
  }, [rows]);

  const cellValue = (row: NodeRow, col: string): string => {
    if (col === "label") return row.label;
    return formatFrontmatterValue(row.frontmatter[col]);
  };

  const filtered = useMemo(() => {
    const q = filter.trim().toLowerCase();
    let out = rows;
    if (q) {
      out = rows.filter((r) =>
        columns.some((c) => cellValue(r, c).toLowerCase().includes(q)),
      );
    }
    if (sortDir) {
      out = [...out].sort((a, b) => {
        const va = cellValue(a, sortCol).toLowerCase();
        const vb = cellValue(b, sortCol).toLowerCase();
        if (va < vb) return sortDir === "asc" ? -1 : 1;
        if (va > vb) return sortDir === "asc" ? 1 : -1;
        return 0;
      });
    }
    return out;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [rows, filter, sortCol, sortDir, columns]);

  function toggleSort(col: string) {
    if (sortCol !== col) {
      setSortCol(col);
      setSortDir("asc");
    } else {
      setSortDir((d) => (d === "asc" ? "desc" : d === "desc" ? null : "asc"));
    }
  }

  if (error) {
    return (
      <Alert color="red" title="Error" m="md">
        {error}
      </Alert>
    );
  }

  return (
    <Box style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
      <Group p="md" pb="xs" justify="space-between">
        <TextInput
          placeholder={`Filtrar ${tipoStyle(tipo).label.toLowerCase()}…`}
          leftSection={<IconSearch size={16} />}
          value={filter}
          onChange={(e) => setFilter(e.currentTarget.value)}
          w={300}
        />
        <Text size="sm" c="dimmed">
          {filtered.length} de {rows.length}
        </Text>
      </Group>

      {loading ? (
        <Center style={{ flex: 1 }}>
          <Loader />
        </Center>
      ) : (
        <ScrollArea style={{ flex: 1 }} px="md">
          <Table striped highlightOnHover stickyHeader withTableBorder>
            <Table.Thead>
              <Table.Tr>
                {columns.map((c) => (
                  <Table.Th key={c}>
                    <UnstyledButton onClick={() => toggleSort(c)}>
                      <Group gap={4} wrap="nowrap">
                        <Text fw={600} size="sm" tt="capitalize">
                          {c === "label" ? "nombre" : c.replace(/_/g, " ")}
                        </Text>
                        <SortIcon active={sortCol === c} dir={sortDir} />
                      </Group>
                    </UnstyledButton>
                  </Table.Th>
                ))}
              </Table.Tr>
            </Table.Thead>
            <Table.Tbody>
              {filtered.map((r) => (
                <Table.Tr
                  key={r.id}
                  style={{ cursor: "pointer" }}
                  onClick={() => open(r.id)}
                >
                  {columns.map((c) => (
                    <Table.Td key={c}>
                      <Text size="sm" lineClamp={2}>
                        {cellValue(r, c)}
                      </Text>
                    </Table.Td>
                  ))}
                </Table.Tr>
              ))}
              {filtered.length === 0 && (
                <Table.Tr>
                  <Table.Td colSpan={columns.length}>
                    <Text c="dimmed" ta="center" py="md">
                      Sin resultados
                    </Text>
                  </Table.Td>
                </Table.Tr>
              )}
            </Table.Tbody>
          </Table>
        </ScrollArea>
      )}
    </Box>
  );
}

function SortIcon({ active, dir }: { active: boolean; dir: SortDir }) {
  if (!active || dir === null) return <IconSelector size={14} opacity={0.5} />;
  return dir === "asc" ? (
    <IconChevronUp size={14} />
  ) : (
    <IconChevronDown size={14} />
  );
}
