import React, { useEffect, useState, useCallback, useMemo, useRef } from "react";
import { useNavigate } from "react-router-dom";
import {
  Box,
  Typography,
  Button,
  IconButton,
  Tooltip,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  TablePagination,
  Paper,
  TableSortLabel,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  Grid,
} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import VisibilityIcon from "@mui/icons-material/Visibility";
import type { ResourceConfig, FieldConfig } from "../types";
import { useResource } from "../context/useResource";
import { useAppContext } from "../context/AppContext";
import { ListCellRenderer, DetailFieldRenderer, applyDisplayFormat } from "./fields";
import { FilterBar } from "./FilterBar";
import { readSseCache, appendSseCache, clearSseCache, nextSseSeq, setSseConnected } from "../context/useResource";
import { SseConnectionStatus } from "./SseConnectionStatus";

interface ResourceListProps {
  resource: ResourceConfig;
  basePath: string;
}

function matchRow(row: any, filters: Record<string, string>, fields: FieldConfig[], allResources: ResourceConfig[]): boolean {
  for (const field of fields) {
    if (!field.filterable) continue;

    const isRange = field.type === "integer" || field.type === "number" || field.format === "date" || field.format === "date-time";

    if (isRange) {
      const from = filters[field.name + "_from"];
      const to = filters[field.name + "_to"];
      if (from || to) {
        const cell = row[field.name];
        if (cell == null) return false;
        if (field.type === "integer" || field.type === "number") {
          if (from && Number(cell) < Number(from)) return false;
          if (to && Number(cell) > Number(to)) return false;
        } else {
          if (from && String(cell) < String(from)) return false;
          if (to && String(cell) > String(to)) return false;
        }
      }
      continue;
    }

    const val = filters[field.name];
    if (!val) continue;

    const cell = row[field.name];
    if (cell == null) return false;

    let str: string;
    if (field.fk && typeof cell === "object" && cell !== null) {
      const targetRes = allResources.find((r) => r.name === field.fk!.resource);
      if (targetRes) {
        const items = Array.isArray(cell) ? cell : [cell];
        str = items.map((item: any) => applyDisplayFormat(item, targetRes.displayFormat)).join(" ");
      } else {
        str = String(cell);
      }
    } else {
      str = String(cell);
    }
    const filterParts = val.split(",").filter(Boolean);
    if (!filterParts.some((part) => str.toLowerCase().includes(part.toLowerCase()))) return false;
  }
  return true;
}

export function ResourceList({ resource, basePath }: ResourceListProps) {
  const navigate = useNavigate();
  const { components, ...crud } = useResource(resource.name);
  const { resources: allResources, config } = useAppContext();
  const [data, setData] = useState<any[]>([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(0);
  const [rowsPerPage, setRowsPerPage] = useState(resource.pagination?.defaultLimit ?? 20);
  const [sortField, setSortField] = useState<string | null>(null);
  const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
  const [filters, setFilters] = useState<Record<string, string>>({});
  const [detailRow, setDetailRow] = useState<any | null>(null);

  const isStreaming = resource.streaming === true;
  const hasActions = resource.operations.get || resource.operations.update || resource.operations.delete;

  const filterMode = config.resourceConfig?.[resource.name]?.filterOptions?.mode ?? "client";
  const isClientMode = filterMode === "client" && !isStreaming;

  const visibleColumns = resource.listColumns
    .map((colName) => resource.fields.find((f) => f.name === colName))
    .filter((f): f is FieldConfig => !!f && !f.hidden?.list);

  useEffect(() => {
    setFilters({});
  }, [resource.name]);

  useEffect(() => {
    if (!isStreaming || !crud.stream) return;

    setData(readSseCache(resource.name));
    setSseConnected(resource.name, false);

    const sub = crud.stream({
      onEvent: (evt) => {
        const enriched = { ...evt, _received_at: new Date().toISOString(), _seq: nextSseSeq() };
        const updated = appendSseCache(resource.name, enriched);
        setData(updated);
      },
      onOpen: () => setSseConnected(resource.name, true),
      onError: () => setSseConnected(resource.name, false),
    });

    return () => {
      setSseConnected(resource.name, false);
      sub.close();
    };
  }, [isStreaming, crud.stream, resource.name]);

  const serverFetchData = useCallback(async () => {
    const params: Record<string, any> = {};
    if (resource.pagination) {
      params[resource.pagination.limitParam] = rowsPerPage;
      params[resource.pagination.offsetParam] = page * rowsPerPage;
    }
    if (sortField) {
      params.sort = sortDir === "desc" ? `-${sortField}` : sortField;
    }
    for (const [key, val] of Object.entries(filters)) {
      if (val) params[key] = val;
    }
    const result = await crud.list(params);
    setData(result.items ?? []);
    setTotal(result.total ?? result.items?.length ?? 0);
  }, [crud.list, resource.pagination, rowsPerPage, page, sortField, sortDir, filters]);

  const clientFetchAll = useCallback(async () => {
    const params: Record<string, any> = {};
    if (resource.pagination) {
      params[resource.pagination.limitParam] = 0;
    }
    const result = await crud.list(params);
    setData(result.items ?? []);
    setTotal(result.items?.length ?? 0);
  }, [crud.list, resource.pagination]);

  useEffect(() => {
    if (isStreaming) return;
    if (isClientMode) {
      clientFetchAll();
    } else {
      serverFetchData();
    }
  }, [isStreaming, isClientMode, clientFetchAll, serverFetchData]);

  useEffect(() => {
    if (isClientMode) {
      setPage(0);
    }
  }, [filters, isClientMode]);

  const filteredData = useMemo(() => {
    if (!isClientMode) return data;

    let items = data.filter((row) => matchRow(row, filters, resource.fields, allResources));

    if (sortField) {
      items = [...items].sort((a, b) => {
        const aVal = a[sortField];
        const bVal = b[sortField];
        if (aVal == null) return 1;
        if (bVal == null) return -1;
        if (aVal < bVal) return sortDir === "asc" ? -1 : 1;
        if (aVal > bVal) return sortDir === "asc" ? 1 : -1;
        return 0;
      });
    }

    const start = page * rowsPerPage;
    return items.slice(start, start + rowsPerPage);
  }, [data, isClientMode, filters, sortField, sortDir, page, rowsPerPage, resource.fields, allResources]);

  const clientTotal = useMemo(() => {
    if (!isClientMode) return total;
    return data.filter((row) => matchRow(row, filters, resource.fields, allResources)).length;
  }, [data, isClientMode, filters, resource.fields, allResources]);

  const displayData = isClientMode ? filteredData : data;
  const displayTotal = isClientMode ? clientTotal : total;

  const handleDelete = async (id: string | number) => {
    if (!window.confirm("Are you sure you want to delete this item?")) return;
    await crud.remove(id);
    if (isClientMode) {
      clientFetchAll();
    } else {
      serverFetchData();
    }
  };

  const handleSort = (field: string) => {
    if (sortField === field) {
      setSortDir((d) => (d === "asc" ? "desc" : "asc"));
    } else {
      setSortField(field);
      setSortDir("asc");
    }
  };

  const handleFilterChange = (fieldName: string, value: string) => {
    setFilters((prev) => ({ ...prev, [fieldName]: value }));
  };

  return (
    <Box>
      <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3 }}>
        <Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
          <Typography variant="h5" fontWeight={700}>
            {resource.displayName}
          </Typography>
          {isStreaming && <SseConnectionStatus resourceName={resource.name} />}
        </Box>
        <Box sx={{ display: "flex", gap: 1 }}>
          {isStreaming && data.length > 0 && (
            <Button variant="outlined" size="small" onClick={() => { setData([]); setTotal(0); clearSseCache(resource.name); }}>
              Clear ({data.length})
            </Button>
          )}
          {resource.operations.create && !isStreaming && (
            <Button
              variant="contained"
              startIcon={<AddIcon />}
              onClick={() => navigate(`${basePath}/${resource.name}/new`)}
            >
              Create
            </Button>
          )}
        </Box>
      </Box>

      {!isStreaming && (
        <FilterBar
          resourceName={resource.name}
          filters={filters}
          onFilterChange={handleFilterChange}
          onClear={() => setFilters({})}
          data={data}
        />
      )}

      <TableContainer component={Paper} variant="outlined">
        <Table size="small">
          <TableHead>
            <TableRow>
              {visibleColumns.map((col) => (
                <TableCell key={col.name} sx={{ fontWeight: 700 }}>
                  {col.sortable ? (
                    <TableSortLabel
                      active={sortField === col.name}
                      direction={sortField === col.name ? sortDir : "asc"}
                      onClick={() => handleSort(col.name)}
                    >
                      {col.label}
                    </TableSortLabel>
                  ) : (
                    col.label
                  )}
                </TableCell>
              ))}
              {hasActions && <TableCell align="right" sx={{ fontWeight: 700 }}>Actions</TableCell>}
            </TableRow>
          </TableHead>
          <TableBody>
            {displayData.length === 0 ? (
              <TableRow>
                <TableCell colSpan={visibleColumns.length + (hasActions ? 1 : 0)} align="center">
                  <Typography variant="body2" color="text.secondary" sx={{ py: 4 }}>
                    {isStreaming ? "Waiting for events\u2026" : "No records found"}
                  </Typography>
                </TableCell>
              </TableRow>
            ) : (
              displayData.map((row, idx) => {
                const rowId = isStreaming ? `evt-${row._seq ?? idx}` : row[resource.primaryKey];
                return (
                  <TableRow
                    key={rowId}
                    hover
                    sx={{ cursor: "pointer" }}
                    onClick={() => {
                      if (isStreaming) {
                        setDetailRow(row);
                      } else {
                        navigate(`${basePath}/${resource.name}/${rowId}`);
                      }
                    }}
                  >
                    {visibleColumns.map((col) => {
                      let value = row[col.name];
                      let fmt = resource.displayFormat;
                      if (col.fk) {
                        const targetRes = allResources.find((r) => r.name === col.fk!.resource);
                        fmt = targetRes!.displayFormat;
                      } else if (col.refSchema && !col.fk && col.inlineDisplayFormat) {
                        fmt = col.inlineDisplayFormat;
                      }
                      return (
                        <TableCell key={col.name}>
                          <ListCellRenderer field={col} value={value} displayFormat={fmt} basePath={basePath} />
                        </TableCell>
                      );
                    })}
                    {hasActions && (
                      <TableCell align="right" onClick={(e) => e.stopPropagation()}>
                        {resource.operations.get && !isStreaming && (
                          <Tooltip title="View">
                            <IconButton size="small" onClick={() => navigate(`${basePath}/${resource.name}/${rowId}`)}>
                              <VisibilityIcon fontSize="small" />
                            </IconButton>
                          </Tooltip>
                        )}
                        {resource.operations.update && (
                          <Tooltip title="Edit">
                            <IconButton size="small" onClick={() => navigate(`${basePath}/${resource.name}/${rowId}/edit`)}>
                              <EditIcon fontSize="small" />
                            </IconButton>
                          </Tooltip>
                        )}
                        {resource.operations.delete && (
                          <Tooltip title="Delete">
                            <IconButton size="small" onClick={() => handleDelete(rowId)} color="error">
                              <DeleteIcon fontSize="small" />
                            </IconButton>
                          </Tooltip>
                        )}
                      </TableCell>
                    )}
                  </TableRow>
                );
              })
            )}
          </TableBody>
        </Table>
      </TableContainer>

      {!isStreaming && (resource.pagination || isClientMode) && (
        <TablePagination
          component="div"
          count={displayTotal}
          page={page}
          onPageChange={(_, p) => setPage(p)}
          rowsPerPage={rowsPerPage}
          onRowsPerPageChange={(e) => {
            setRowsPerPage(parseInt(e.target.value, 10));
            setPage(0);
          }}
          rowsPerPageOptions={[10, 20, 50, 100]}
        />
      )}

      {!isStreaming && displayData.length > 0 && (
        <Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: "block", textAlign: "right" }}>
          {displayTotal} record{displayTotal !== 1 ? "s" : ""}
        </Typography>
      )}

      <Dialog open={!!detailRow} onClose={() => setDetailRow(null)} maxWidth="sm" fullWidth>
        <DialogTitle>{resource.displayName} Event</DialogTitle>
        <DialogContent dividers>
          {detailRow && (
            <Grid container spacing={2} sx={{ mt: 0.5 }}>
              {visibleColumns.map((col) => (
                <Grid key={col.name} item xs={12} sm={6}>
                    <DetailFieldRenderer
                      field={col}
                      value={detailRow[col.name]}
                      displayFormat={resource.displayFormat}
                      basePath={basePath}
                    />
                </Grid>
              ))}
            </Grid>
          )}
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setDetailRow(null)}>Close</Button>
        </DialogActions>
      </Dialog>
    </Box>
  );
}
