import { sentidoMovimiento } from "./sentidoMovimiento";
import { ArrowDownward, ArrowUpward, Close, TableRows, Receipt, Search, FileDownload, KeyboardArrowDown, KeyboardArrowRight } from "@mui/icons-material";
import { IconButton, Collapse } from "@mui/material";
import * as XLSX from "xlsx";
import {
  Box,
  Button,
  Chip,
  CircularProgress,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  FormControl,
  InputAdornment,
  InputLabel,
  MenuItem,
  Select,
  TextField,
  ToggleButton,
  ToggleButtonGroup,
  Tooltip,
  Typography,
} from "@mui/material";
import { Icon } from "@iconify/react";
import PropTypes from "prop-types";
import { Fragment, useEffect, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { fmtFechaCalendario } from "../../utils/fecha";
import styled from "styled-components";
import {
  useAnularTesMovimientoMutation,
  useActualizarTesMovimientoMutation,
  useAprobarTesMovimientoMutation,
  useConfirmarTesMovimientoMutation,
  useEliminarTesMovimientoMutation,
  useTesCuentasQuery,
  useTesMovimientosQuery,
} from "../../tanstack/TesoreriaStack";
import { TesExtractoView } from "./TesExtractoView";
import { hoyISO } from "../../utils/fecha";
import {
  EmptyState,
  ScreenGuia,
  fmtMoneda,
  getTesMovimientoEstadoChip,
  getTesMovimientoOrigen,
  sanitizeRecords,
} from "../_standards";

const todayStr = () => hoyISO();
const hace30 = () => {
  const d = new Date();
  d.setDate(d.getDate() - 30);
  return d.toISOString().split("T")[0];
};

const fmtFecha = (val) => {
  if (!val) return "—";
  const m = String(val).match(/^(\d{4})-(\d{2})-(\d{2})/);
  return m ? `${m[3]}/${m[2]}/${m[1]}` : val;
};

const fmtMonto = (n, moneda) => fmtMoneda(n, moneda || "PYG");

export const TesMovimientosTab = () => {
  const [filters, setFilters] = useState({
    fechaDesde: hace30(),
    fechaHasta: todayStr(),
    cuentaId: "",
    tipo: "",
    estado: "",
    search: "",
    page: 1,
    limit: 20,
  });
  const [busqueda, setBusqueda] = useState("");
  const [anularDialog, setAnularDialog] = useState(null);
  const [editarDialog, setEditarDialog] = useState(null);
  const [editForm, setEditForm] = useState({ fecha: "", descripcion: "", referencia: "" });
  const [origenDialog, setOrigenDialog] = useState(null);
  const [eliminarDialog, setEliminarDialog] = useState(null);
  const [motivo, setMotivo] = useState("");
  const [vista, setVista] = useState("global");
  const [expanded, setExpanded] = useState({});
  const toggleExpand = (id) => setExpanded(e => ({ ...e, [id]: !e[id] }));

  const { data: cuentasData } = useTesCuentasQuery();
  const cuentas = Array.isArray(cuentasData) ? cuentasData : (cuentasData?.data || []);

  // La búsqueda va al backend: filtrando en el front sólo se miraba la página
  // cargada (20 filas), así que buscar una boleta o un importe casi nunca
  // encontraba nada aunque estuviera en el período.
  useEffect(() => {
    const t = setTimeout(
      () => setFilters(f => (f.search === busqueda.trim() ? f : { ...f, search: busqueda.trim(), page: 1 })),
      400,
    );
    return () => clearTimeout(t);
  }, [busqueda]);

  // Buscando se manda `search` SIN fechas: el usuario busca una boleta puntual y
  // no tiene por qué saber en qué período cayó.
  const buscando = !!filters.search;
  const queryParams = useMemo(
    () => (buscando ? { ...filters, fechaDesde: undefined, fechaHasta: undefined } : filters),
    [filters, buscando],
  );
  const { data, isLoading } = useTesMovimientosQuery(queryParams);
  const movimientos = Array.isArray(data) ? data : (data?.data || []);
  const total = data?.meta?.total ?? data?.total ?? movimientos.length;
  const totalPages = Math.ceil(total / filters.limit) || 1;

  const confirmar = useConfirmarTesMovimientoMutation();
  const aprobar = useAprobarTesMovimientoMutation();
  const anular = useAnularTesMovimientoMutation();
  const actualizar = useActualizarTesMovimientoMutation();
  const eliminar = useEliminarTesMovimientoMutation();

  const setFilter = (key, value) => setFilters(f => ({ ...f, [key]: value, page: 1 }));

  const exportarExcel = () => {
    const rows = movimientos.map(m => ({
      Fecha: fmtFecha(m.fecha),
      Cuenta: m.cuenta?.nombre || "—",
      Tipo: m.tipo || "—",
      Categorías: (m.detalles || []).map(d => d.categoria?.nombre || "Sin categoría").join(", ") || "—",
      Descripción: m.descripcion || "—",
      Referencia: m.referencia || "—",
      Monto: Number(m.monto || 0),
      Moneda: m.cuenta?.moneda || "PYG",
      Estado: m.estado || "—",
      "Registrado por": m.usuario?.nombres || "—",
    }));
    const ws = XLSX.utils.json_to_sheet(sanitizeRecords(rows));
    ws["!cols"] = [10, 20, 10, 25, 30, 15, 14, 8, 12, 20].map(w => ({ wch: w }));
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, "Movimientos");
    XLSX.writeFile(wb, `movimientos-tesoreria-${filters.fechaDesde}-${filters.fechaHasta}.xlsx`);
  };

  const handleAnular = async () => {
    if (!motivo.trim()) return;
    await anular.mutateAsync({ id: anularDialog.id, data: { motivo_anulacion: motivo } });
    setAnularDialog(null);
    setMotivo("");
  };

  const handleEditar = async () => {
    await actualizar.mutateAsync({
      id: editarDialog.id,
      data: {
        fecha: editForm.fecha,
        descripcion: editForm.descripcion,
        referencia: editForm.referencia || undefined,
      },
    });
    setEditarDialog(null);
  };

  const handleEliminar = async () => {
    if (!eliminarDialog) return;
    await eliminar.mutateAsync(eliminarDialog.id);
    setEliminarDialog(null);
  };

  return (
    <Container>
      <ViewToggleBar>
        <ToggleButtonGroup value={vista} exclusive onChange={(_, v) => v && setVista(v)} size="small">
          <Tooltip title="Vista global — todos los movimientos">
            <ToggleButton value="global"><TableRows fontSize="small" sx={{ mr: 0.5 }} />Global</ToggleButton>
          </Tooltip>
          <Tooltip title="Vista extracto — saldo acumulado línea a línea por cuenta">
            <ToggleButton value="extracto"><Receipt fontSize="small" sx={{ mr: 0.5 }} />Extracto</ToggleButton>
          </Tooltip>
        </ToggleButtonGroup>
      </ViewToggleBar>

      <ScreenGuia
        moduloPadre="Tesorería"
        titulo="Movimientos"
        intro="Todo ingreso y egreso de tus cuentas de tesorería. Un movimiento sólo mueve el saldo cuando se confirma."
        pasos={[
          { icon: "lucide:file-plus", color: "#94a3b8", titulo: "Borrador",
            desc: "Se registra pero todavía no toca el saldo. Se puede eliminar sin dejar rastro." },
          { icon: "lucide:shield-check", color: "#f59e0b", titulo: "Aprobación",
            desc: "Sólo si el monto supera el umbral configurado en Tesorería › Configuración." },
          { icon: "lucide:check-circle", color: "#22c55e", titulo: "Confirmado",
            desc: "Recién acá se actualiza el saldo de la cuenta y se genera el asiento contable." },
          { icon: "lucide:x-circle", color: "#ef4444", titulo: "Anulado",
            desc: "Revierte el saldo y el asiento. Queda la fila con el motivo, no se borra." },
        ]}
        notas={[
          { icon: "lucide:link",
            text: "Los movimientos con una etiqueta de origen (Cheque, Gasto, Orden de pago, Recibo…) los generó otro documento. No se anulan acá: hay que anular ese documento, y así se revierten el saldo y el documento juntos." },
          { icon: "lucide:archive",
            text: "Los movimientos de cuentas cerradas se siguen mostrando —son historia contable—, pero esas cuentas ya no suman en los totales de Cuentas y Saldos." },
        ]}
      />

      {vista === "extracto" ? (
        <TesExtractoView cuentas={cuentas} />
      ) : (
        <>
          <FiltersBar>
            {/* El aviso va DEBAJO del campo y sólo mientras se busca: como
                helperText fijo empujaba el input y lo desalineaba del resto. */}
            <Box sx={{ flex: "1 1 240px", minWidth: 190 }}>
              <TextField
                label="Buscar"
                size="small"
                fullWidth
                placeholder="Nro. de boleta, descripción, importe..."
                value={busqueda}
                onChange={e => setBusqueda(e.target.value)}
                InputProps={{
                  startAdornment: <InputAdornment position="start"><Search fontSize="small" /></InputAdornment>,
                  endAdornment: busqueda ? (
                    <InputAdornment position="end">
                      <IconButton size="small" onClick={() => setBusqueda("")} aria-label="Limpiar búsqueda">
                        <Close fontSize="small" />
                      </IconButton>
                    </InputAdornment>
                  ) : null,
                }}
              />
              {buscando && (
                <Typography variant="caption" color="text.disabled" sx={{ px: 0.5, display: "block", mt: 0.25 }}>
                  Buscando en todos los movimientos
                </Typography>
              )}
            </Box>
            {/* Con búsqueda activa el backend ignora las fechas: se atenúan para
                que se vea por qué aparecen movimientos fuera del período. */}
            <Tooltip title={buscando ? "Ignorado: la búsqueda recorre todos los movimientos" : ""} disableHoverListener={!buscando}>
              <Box sx={{ display: "flex", gap: 1.25, opacity: buscando ? 0.4 : 1, pointerEvents: buscando ? "none" : "auto" }}>
                <TextField
                  label="Desde"
                  type="date"
                  size="small"
                  value={filters.fechaDesde}
                  onChange={e => setFilter("fechaDesde", e.target.value)}
                  InputLabelProps={{ shrink: true }}
                  sx={{ width: 150 }}
                />
                <TextField
                  label="Hasta"
                  type="date"
                  size="small"
                  value={filters.fechaHasta}
                  onChange={e => setFilter("fechaHasta", e.target.value)}
                  InputLabelProps={{ shrink: true }}
                  sx={{ width: 150 }}
                />
              </Box>
            </Tooltip>
            <FormControl size="small" sx={{ flex: "1 1 140px", minWidth: 130 }}>
              <InputLabel id="mov-cuenta-label">Cuenta</InputLabel>
              {/* labelId: sin la asociación el Select no tiene nombre accesible. */}
              <Select value={filters.cuentaId} labelId="mov-cuenta-label" label="Cuenta"
                onChange={e => setFilter("cuentaId", e.target.value)}>
                <MenuItem value="">Todas</MenuItem>
                {/* Las cuentas cerradas siguen en el filtro: su historial se tiene
                    que poder consultar. Van marcadas y al final (el backend las
                    ordena por activo desc) para no confundirlas con las vigentes. */}
                {cuentas.map(c => (
                  <MenuItem key={c.id} value={c.id}>
                    {c.nombre}{c.activo === false ? " (inactiva)" : ""}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
            <FormControl size="small" sx={{ flex: "1 1 110px", minWidth: 100 }}>
              <InputLabel>Tipo</InputLabel>
              <Select value={filters.tipo} label="Tipo" onChange={e => setFilter("tipo", e.target.value)}>
                <MenuItem value="">Todos</MenuItem>
                <MenuItem value="INGRESO">Ingreso</MenuItem>
                <MenuItem value="EGRESO">Egreso</MenuItem>
              </Select>
            </FormControl>
            <FormControl size="small" sx={{ flex: "1 1 120px", minWidth: 110 }}>
              <InputLabel>Estado</InputLabel>
              <Select value={filters.estado} label="Estado" onChange={e => setFilter("estado", e.target.value)}>
                <MenuItem value="">Todos</MenuItem>
                <MenuItem value="BORRADOR">Borrador</MenuItem>
                <MenuItem value="CONFIRMADO">Confirmado</MenuItem>
                <MenuItem value="ANULADO">Anulado</MenuItem>
              </Select>
            </FormControl>
            <Button variant="outlined" size="small" startIcon={<FileDownload />}
              onClick={exportarExcel} disabled={movimientos.length === 0}
              sx={{ whiteSpace: "nowrap", flexShrink: 0, ml: "auto", height: 40 }}>
              Excel
            </Button>
          </FiltersBar>

          {/* Desktop: tabla con scroll horizontal */}
          <TableWrap className="desktop-table">
            <ScrollX>
              <StyledTable>
                <thead>
                  <tr>
                    <Th style={{ width: 32 }}></Th>
                    <Th>Fecha</Th>
                    <Th>Cuenta</Th>
                    <Th>Tipo</Th>
                    <Th>Categorías</Th>
                    <Th>Descripción</Th>
                    <Th>Monto</Th>
                    <Th>Estado</Th>
                    <Th>Acciones</Th>
                  </tr>
                </thead>
                <tbody>
                  {isLoading ? (
                    <tr><Td colSpan={9} style={{ textAlign: "center", padding: 32 }}><CircularProgress size={24} /></Td></tr>
                  ) : movimientos.length === 0 ? (
                    <tr><Td colSpan={9} style={{ padding: 0 }}>
                      <EmptyState
                        compact
                        icon="lucide:receipt"
                        title={buscando ? "Ningún movimiento coincide con la búsqueda" : "No hay movimientos en este período"}
                        description={buscando
                          ? "Se buscó en todos los movimientos, sin límite de fechas. Revisá el texto o el importe, o quitá los filtros de cuenta, tipo y estado."
                          : "Probá ampliando el rango de fechas o quitando los filtros de cuenta, tipo y estado."}
                      />
                    </Td></tr>
                  ) : movimientos.map(mov => {
                    const cats = mov.detalles || [];
                    const isOpen = !!expanded[mov.id];
                    return (
                    <Fragment key={mov.id}>
                    <Tr>
                      <Td style={{ width: 32, padding: "4px 4px 4px 8px" }}>
                        {cats.length > 0 && (
                          <IconButton size="small" onClick={() => toggleExpand(mov.id)}
                            aria-label={isOpen ? "Colapsar" : "Expandir categorías"}>
                            {isOpen ? <KeyboardArrowDown fontSize="small" /> : <KeyboardArrowRight fontSize="small" />}
                          </IconButton>
                        )}
                      </Td>
                      <Td style={{ whiteSpace: "nowrap" }}>{fmtFecha(mov.fecha)}</Td>
                      <Td style={{ whiteSpace: "nowrap" }}>
                        {mov.cuenta?.nombre || "—"}
                        {mov.cuenta?.activo === false && (
                          <Tooltip title="Cuenta cerrada. El movimiento sigue en la historia, pero la cuenta ya no suma en los saldos.">
                            <Chip label="inactiva" size="small" variant="outlined"
                              sx={{ ml: 0.75, height: 18, fontSize: 10, opacity: 0.7 }} />
                          </Tooltip>
                        )}
                      </Td>
                      <Td style={{ whiteSpace: "nowrap" }}>
                        <MontoCell $tipo={sentidoMovimiento(mov)}>
                          {sentidoMovimiento(mov) === "INGRESO" ? <ArrowUpward fontSize="small" /> : <ArrowDownward fontSize="small" />}
                          {mov.tipo}
                        </MontoCell>
                      </Td>
                      <Td style={{ whiteSpace: "nowrap" }}>
                        {cats.length === 0 ? (
                          <span style={{ opacity: 0.4, fontSize: 12 }}>—</span>
                        ) : cats.length === 1 ? (
                          <span style={{ fontSize: 12, opacity: 0.85 }}>
                            {cats[0].categoria?.nombre || cats[0].nombre || "—"}
                          </span>
                        ) : (
                          <Chip label={`${cats.length} categorías`} size="small" variant="outlined"
                            onClick={() => toggleExpand(mov.id)} sx={{ cursor: "pointer" }} />
                        )}
                      </Td>
                      <Td style={{ minWidth: 140, maxWidth: 220, wordBreak: "break-word" }}>
                        {mov.descripcion}
                        <OrigenChip origenTipo={mov.origen_tipo} origen={mov.origen} />
                      </Td>
                      <Td style={{ whiteSpace: "nowrap" }}>
                        <MontoCell $tipo={mov.tipo}>{fmtMonto(mov.monto, mov.cuenta?.moneda)}</MontoCell>
                      </Td>
                      <Td style={{ whiteSpace: "nowrap" }}>
                        <Chip {...getTesMovimientoEstadoChip(mov.estado)} size="small" />
                      </Td>
                      <Td>
                        <AccionesCell>
                          {mov.estado === "BORRADOR" && mov.requiere_aprobacion && !mov.aprobado_por && (
                            <Tooltip title="Este movimiento supera el umbral configurado y necesita aprobación de un supervisor antes de confirmarse">
                              <Button size="small" variant="outlined" color="warning"
                                onClick={() => aprobar.mutate(mov.id)} disabled={aprobar.isPending}>
                                Aprobar
                              </Button>
                            </Tooltip>
                          )}
                          {mov.estado === "BORRADOR" && (
                            <Button size="small" variant="outlined" color="success"
                              onClick={() => confirmar.mutate(mov.id)}
                              disabled={confirmar.isPending || (mov.requiere_aprobacion && !mov.aprobado_por)}>
                              Confirmar
                            </Button>
                          )}
                          {mov.estado === "BORRADOR" && (
                            <Tooltip title="Eliminar borrador — no afecta saldo">
                              <Button size="small" variant="outlined" color="error"
                                onClick={() => setEliminarDialog(mov)}
                                disabled={eliminar.isPending}>
                                Eliminar
                              </Button>
                            </Tooltip>
                          )}
                          {mov.estado !== "ANULADO" && (
                            <Tooltip title="Corregir fecha o descripción. El importe y la cuenta no se editan: si están mal, anulá el movimiento.">
                              <Button size="small" variant="outlined"
                                onClick={() => {
                                  setEditarDialog(mov);
                                  setEditForm({
                                    fecha: String(mov.fecha || "").slice(0, 10),
                                    descripcion: mov.descripcion || "",
                                    referencia: mov.referencia || "",
                                  });
                                }}>
                                Editar
                              </Button>
                            </Tooltip>
                          )}
                          {mov.estado === "CONFIRMADO" && (
                            <BotonAnular origenTipo={mov.origen_tipo} origen={mov.origen} onAnular={() => { setAnularDialog(mov); setMotivo(""); }} onExplicar={() => setOrigenDialog(mov)} />
                          )}
                        </AccionesCell>
                      </Td>
                    </Tr>
                    {cats.length > 0 && isOpen && (
                      <tr key={`${mov.id}-detail`}>
                        <Td colSpan={9} style={{ background: "rgba(255,255,255,0.02)", padding: 0 }}>
                          <DetailWrap>
                            <DetailTitle>Detalle de categorías ({cats.length})</DetailTitle>
                            <DetailTable>
                              <thead>
                                <tr>
                                  <DetailTh>Categoría</DetailTh>
                                  <DetailTh>Descripción</DetailTh>
                                  <DetailTh style={{ textAlign: "right" }}>Monto</DetailTh>
                                  <DetailTh style={{ textAlign: "right", width: 90 }}>% del total</DetailTh>
                                </tr>
                              </thead>
                              <tbody>
                                {cats.map((c, i) => {
                                  const monto = Number(c.monto || 0);
                                  const total = Number(mov.monto || 0);
                                  const pct = total > 0 ? (monto / total) * 100 : 0;
                                  return (
                                    <tr key={i}>
                                      <DetailTd>{c.categoria?.nombre || c.nombre || "—"}</DetailTd>
                                      <DetailTd style={{ opacity: 0.75 }}>{c.descripcion || "—"}</DetailTd>
                                      <DetailTd style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                                        {fmtMonto(monto, mov.cuenta?.moneda)}
                                      </DetailTd>
                                      <DetailTd style={{ textAlign: "right", opacity: 0.7 }}>
                                        {pct.toFixed(1)}%
                                      </DetailTd>
                                    </tr>
                                  );
                                })}
                              </tbody>
                            </DetailTable>
                          </DetailWrap>
                        </Td>
                      </tr>
                    )}
                    </Fragment>
                  );})}
                </tbody>
              </StyledTable>
            </ScrollX>

            <Pagination>
              <Typography variant="caption" sx={{ opacity: 0.6 }}>
                Página {filters.page} de {totalPages} ({total} registros)
              </Typography>
              <Button size="small" disabled={filters.page <= 1} onClick={() => setFilters(f => ({ ...f, page: f.page - 1 }))}>
                Anterior
              </Button>
              <Button size="small" disabled={filters.page >= totalPages} onClick={() => setFilters(f => ({ ...f, page: f.page + 1 }))}>
                Siguiente
              </Button>
            </Pagination>
          </TableWrap>

          {/* Mobile: cards */}
          <CardList className="mobile-cards">
            {isLoading ? (
              <div style={{ textAlign: "center", padding: 32 }}><CircularProgress size={24} /></div>
            ) : movimientos.length === 0 ? (
              <EmptyState
                compact
                icon="lucide:receipt"
                title={buscando ? "Ningún movimiento coincide con la búsqueda" : "No hay movimientos en este período"}
                description={buscando
                  ? "Se buscó en todos los movimientos, sin límite de fechas."
                  : "Probá ampliando el rango de fechas o quitando los filtros."}
              />
            ) : movimientos.map(mov => (
              <MovCard key={mov.id}>
                <CardTop>
                  <MontoCell $tipo={sentidoMovimiento(mov)} style={{ fontSize: 15 }}>
                    {sentidoMovimiento(mov) === "INGRESO" ? <ArrowUpward fontSize="small" /> : <ArrowDownward fontSize="small" />}
                    {fmtMonto(mov.monto, mov.cuenta?.moneda)}
                  </MontoCell>
                  <Chip {...getTesMovimientoEstadoChip(mov.estado)} size="small" />
                </CardTop>
                <CardMeta>
                  <span>{mov.cuenta?.nombre || "—"}</span>
                  <span style={{ opacity: 0.5 }}>•</span>
                  <span>{fmtFecha(mov.fecha)}</span>
                </CardMeta>
                {(mov.detalles || []).length > 0 && (() => {
                  const cats = mov.detalles || [];
                  const isOpen = !!expanded[mov.id];
                  return (
                    <>
                      <CardCatsToggle onClick={() => toggleExpand(mov.id)}>
                        {isOpen ? <KeyboardArrowDown fontSize="small" /> : <KeyboardArrowRight fontSize="small" />}
                        {cats.length === 1
                          ? (cats[0].categoria?.nombre || cats[0].nombre || "—")
                          : `${cats.length} categorías`}
                      </CardCatsToggle>
                      <Collapse in={isOpen} unmountOnExit>
                        <CardCats>
                          {cats.map((c, i) => (
                            <CardCatRow key={i}>
                              <span>{c.categoria?.nombre || c.nombre || "—"}</span>
                              <strong>{fmtMonto(c.monto, mov.cuenta?.moneda)}</strong>
                            </CardCatRow>
                          ))}
                        </CardCats>
                      </Collapse>
                    </>
                  );
                })()}
                {mov.descripcion && <CardDesc>{mov.descripcion}</CardDesc>}
                <OrigenChip origenTipo={mov.origen_tipo} origen={mov.origen} />
                <CardActions>
                  {mov.estado === "BORRADOR" && mov.requiere_aprobacion && !mov.aprobado_por && (
                    <Button size="small" variant="outlined" color="warning"
                      onClick={() => aprobar.mutate(mov.id)} disabled={aprobar.isPending}>
                      Aprobar
                    </Button>
                  )}
                  {mov.estado === "BORRADOR" && (
                    <Button size="small" variant="outlined" color="success"
                      onClick={() => confirmar.mutate(mov.id)}
                      disabled={confirmar.isPending || (mov.requiere_aprobacion && !mov.aprobado_por)}>
                      Confirmar
                    </Button>
                  )}
                  {mov.estado === "BORRADOR" && (
                    <Button size="small" variant="outlined" color="error"
                      onClick={() => setEliminarDialog(mov)}
                      disabled={eliminar.isPending}>
                      Eliminar
                    </Button>
                  )}
                  {mov.estado === "CONFIRMADO" && (
                    <BotonAnular origenTipo={mov.origen_tipo} origen={mov.origen} onAnular={() => { setAnularDialog(mov); setMotivo(""); }} onExplicar={() => setOrigenDialog(mov)} />
                  )}
                </CardActions>
              </MovCard>
            ))}
            <Pagination style={{ borderTop: "none", justifyContent: "center" }}>
              <Typography variant="caption" sx={{ opacity: 0.6 }}>
                Página {filters.page} de {totalPages} ({total} registros)
              </Typography>
              <Button size="small" disabled={filters.page <= 1} onClick={() => setFilters(f => ({ ...f, page: f.page - 1 }))}>
                Ant
              </Button>
              <Button size="small" disabled={filters.page >= totalPages} onClick={() => setFilters(f => ({ ...f, page: f.page + 1 }))}>
                Sig
              </Button>
            </Pagination>
          </CardList>

          <Dialog open={!!editarDialog} onClose={() => setEditarDialog(null)} maxWidth="xs" fullWidth>
            <DialogTitle>Editar movimiento</DialogTitle>
            <DialogContent>
              {editarDialog && (
                <Typography variant="body2" sx={{ mb: 2, opacity: 0.75 }}>
                  {editarDialog.cuenta?.nombre || "—"} · {editarDialog.tipo} ·{" "}
                  {fmtMonto(editarDialog.monto, editarDialog.cuenta?.moneda)}
                </Typography>
              )}
              <TextField
                label="Fecha" type="date" fullWidth sx={{ mb: 2 }}
                InputLabelProps={{ shrink: true }}
                value={editForm.fecha}
                onChange={e => setEditForm(f => ({ ...f, fecha: e.target.value }))}
                helperText="La fecha define en qué mes cae el movimiento en el libro banco. Ej: si el depósito se acreditó el 05/05 pero se cargó en julio, corregila acá."
                autoFocus
              />
              <TextField
                label="Descripción" fullWidth multiline rows={2} sx={{ mb: 2 }}
                value={editForm.descripcion}
                onChange={e => setEditForm(f => ({ ...f, descripcion: e.target.value }))}
              />
              <TextField
                label="Referencia" fullWidth
                value={editForm.referencia}
                onChange={e => setEditForm(f => ({ ...f, referencia: e.target.value }))}
                placeholder="Ej: Boleta Nro 3230389"
              />
              <Typography variant="caption" sx={{ display: "block", mt: 2, opacity: 0.7 }}>
                El importe y la cuenta no se editan. Si están mal, anulá el movimiento y volvé a cargarlo
                — y si lo generó un documento (gasto, recibo, cheque), corregilo desde ahí.
              </Typography>
            </DialogContent>
            <DialogActions>
              <Button onClick={() => setEditarDialog(null)}>Cancelar</Button>
              <Button variant="contained" onClick={handleEditar} disabled={!editForm.fecha || actualizar.isPending}>
                {actualizar.isPending ? <CircularProgress size={18} /> : "Guardar"}
              </Button>
            </DialogActions>
          </Dialog>

          <Dialog open={!!anularDialog} onClose={() => setAnularDialog(null)} maxWidth="xs" fullWidth>
            <DialogTitle>Anular movimiento</DialogTitle>
            <DialogContent>
              <Typography variant="body2" sx={{ mb: 2 }}>
                ¿Estás seguro de anular este movimiento? Esta acción no se puede deshacer.
              </Typography>
              <TextField
                label="Motivo de anulación"
                fullWidth required multiline rows={2}
                value={motivo}
                onChange={e => setMotivo(e.target.value)}
                autoFocus
              />
            </DialogContent>
            <DialogActions>
              <Button onClick={() => setAnularDialog(null)}>Cancelar</Button>
              <Button variant="contained" color="error" onClick={handleAnular} disabled={!motivo.trim() || anular.isPending}>
                {anular.isPending ? <CircularProgress size={18} /> : "Anular"}
              </Button>
            </DialogActions>
          </Dialog>

          <Dialog open={!!eliminarDialog} onClose={() => setEliminarDialog(null)} maxWidth="xs" fullWidth>
            <DialogTitle>Eliminar borrador</DialogTitle>
            <DialogContent>
              <Typography variant="body2">
                Este movimiento está en <strong>borrador</strong> y no afectó el saldo de la cuenta. Al eliminarlo se borra del sistema definitivamente.
              </Typography>
              {eliminarDialog && (
                <Typography variant="body2" sx={{ mt: 1.5, opacity: 0.75 }}>
                  {fmtFecha(eliminarDialog.fecha)} · {eliminarDialog.cuenta?.nombre || "—"} · {fmtMonto(eliminarDialog.monto, eliminarDialog.cuenta?.moneda)}
                </Typography>
              )}
            </DialogContent>
            <DialogActions>
              <Button onClick={() => setEliminarDialog(null)}>Cancelar</Button>
              <Button variant="contained" color="error" onClick={handleEliminar} disabled={eliminar.isPending}>
                {eliminar.isPending ? <CircularProgress size={18} /> : "Eliminar"}
              </Button>
            </DialogActions>
          </Dialog>
        </>
      )}

      <DialogoOrigen movimiento={origenDialog} onClose={() => setOrigenDialog(null)} />
    </Container>
  );
};

// ── Auxiliares ───────────────────────────────────────────────────────────────

/**
 * Chip que dice de dónde salió el movimiento. Sólo aparece cuando `origen_tipo`
 * viene cargado, o sea cuando lo generó otro documento (un cheque, un gasto,
 * una orden de pago...). Sin esto la tabla no daba ninguna pista y el usuario
 * descubría la restricción recién al intentar anular.
 */
const OrigenChip = ({ origenTipo, origen: origenDoc }) => {
  const origen = getTesMovimientoOrigen(origenTipo);
  if (!origen) return null;
  const numero = origenDoc?.numero;
  return (
    <Tooltip
      title={`Generado desde ${origenDoc?.etiqueta ?? origen.label}${numero ? ` N° ${numero}` : ""}. Para revertirlo hay que anularlo ahí.`}
    >
      <OrigenTag>
        <Icon icon={origen.icon} width={11} />
        {origenDoc?.etiqueta ?? origen.label}
        {numero ? ` N° ${numero}` : ""}
      </OrigenTag>
    </Tooltip>
  );
};

/**
 * El backend rechaza anular un movimiento con origen: revertiría el saldo y
 * dejaría el documento de origen intacto, descuadrando los dos. Antes el botón
 * se mostraba igual y el error llegaba con un 400 después de escribir el
 * motivo; ahora se explica de entrada dónde sí se puede revertir.
 */
const BotonAnular = ({ origenTipo, origen: origenDoc, onAnular, onExplicar }) => {
  const origen = getTesMovimientoOrigen(origenTipo);
  // Sin origen, o con el documento ya anulado (el movimiento quedó colgado):
  // se anula acá, que es la única forma de cerrarlo.
  if (!origen || origenDoc?.permiteAnularDirecto) {
    return (
      <Button size="small" variant="outlined" color="error" onClick={onAnular}>
        Anular
      </Button>
    );
  }
  // Botón habilitado a propósito: uno deshabilitado no explica nada y en celular
  // el tooltip no aparece. Al tocarlo se abre el detalle con el documento y el
  // acceso directo a la pantalla donde sí se anula.
  return (
    <Button size="small" variant="outlined" color="error" onClick={onExplicar}>
      Anular
    </Button>
  );
};

/** Explica por qué este movimiento se anula en otro lado, y lleva ahí. */
const DialogoOrigen = ({ movimiento, onClose }) => {
  const navigate = useNavigate();
  if (!movimiento) return null;
  const origen = movimiento.origen;
  const meta = getTesMovimientoOrigen(movimiento.origen_tipo);
  const etiqueta = origen?.etiqueta ?? meta?.label ?? "un documento";
  return (
    <Dialog open onClose={onClose} maxWidth="xs" fullWidth>
      <DialogTitle>Este movimiento se anula desde su documento</DialogTitle>
      <DialogContent>
        <Typography variant="body2" sx={{ mb: 1.5 }}>
          Lo generó <strong>{etiqueta}{origen?.numero ? ` N° ${origen.numero}` : ""}</strong>
          {origen?.fecha ? ` del ${fmtFechaCalendario(origen.fecha)}` : ""}
          {origen?.estado ? ` (estado: ${origen.estado})` : ""}.
        </Typography>
        <Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
          Si lo anularas acá, el saldo del banco se corregiría pero el documento seguiría vigente y
          los dos quedarían descuadrados. Anulándolo en su pantalla se revierten juntos.
        </Typography>
        <Typography variant="body2">
          Dónde: <strong>{origen?.pantalla ?? "la pantalla del documento"}</strong>
        </Typography>
      </DialogContent>
      <DialogActions>
        <Button onClick={onClose}>Cerrar</Button>
        {origen?.ruta && (
          <Button variant="contained" onClick={() => navigate(origen.ruta)}>
            Ir a {origen.pantalla.split("→").pop().trim()}
          </Button>
        )}
      </DialogActions>
    </Dialog>
  );
};

OrigenChip.propTypes = { origenTipo: PropTypes.string, origen: PropTypes.object };
BotonAnular.propTypes = {
  origenTipo: PropTypes.string,
  origen: PropTypes.object,
  onAnular: PropTypes.func.isRequired,
  onExplicar: PropTypes.func.isRequired,
};
DialogoOrigen.propTypes = { movimiento: PropTypes.object, onClose: PropTypes.func.isRequired };

const Container = styled.div`
  padding: 16px;
  height: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
`;
const ViewToggleBar = styled.div`display: flex; justify-content: flex-end;`;
// align-items: flex-start — bajo el buscador aparece la leyenda "Buscando en
// todos los movimientos" y con `center` esa altura extra desplazaba toda la fila.
const FiltersBar = styled.div`display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start;`;
const TableWrap = styled.div`
  background: ${({ theme }) => theme.bg2};
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 10px;
  overflow-x: auto;
  @media (max-width: 700px) { display: none; }
`;
const ScrollX = styled.div``;
const StyledTable = styled.table`width: 100%; border-collapse: collapse; min-width: 700px;`;
const Th = styled.th`
  background: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.text2};
  padding: 10px 12px;
  text-align: left;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  border-bottom: 1px solid ${({ theme }) => theme.color2};
  white-space: nowrap;
`;
const Td = styled.td`
  padding: 10px 12px;
  border-bottom: 1px solid ${({ theme }) => theme.color2};
  font-size: 13px;
  color: ${({ theme }) => theme.text1};
  vertical-align: top;
`;
const Tr = styled.tr`
  &:last-child td { border-bottom: none; }
  &:hover { background: ${({ theme }) => theme.bg3}; }
`;
const Pagination = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 12px 16px;
  border-top: 1px solid ${({ theme }) => theme.color2};
`;
const MontoCell = styled.div`
  font-weight: 600;
  color: ${({ $tipo }) => $tipo === "INGRESO" ? "#22c55e" : "#ef4444"};
  display: flex;
  align-items: center;
  gap: 4px;
`;
const AccionesCell = styled.div`display: flex; gap: 6px; flex-wrap: wrap;`;

/* Mobile cards */
const CardList = styled.div`
  display: none;
  flex-direction: column;
  gap: 10px;
  @media (max-width: 700px) { display: flex; }
`;
const MovCard = styled.div`
  background: ${({ theme }) => theme.bg2};
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 10px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
`;
const CardTop = styled.div`display: flex; justify-content: space-between; align-items: center;`;
const CardMeta = styled.div`
  display: flex;
  gap: 6px;
  font-size: 12px;
  opacity: 0.65;
`;
const CardCats = styled.div`
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  opacity: 0.85;
  padding: 6px 8px;
  background: ${({ theme }) => theme.bg3};
  border-radius: 6px;
  margin-top: 4px;
`;
const CardCatRow = styled.div`
  display: flex;
  justify-content: space-between;
  gap: 8px;
`;
const CardCatsToggle = styled.button`
  display: flex;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: none;
  color: ${({ theme }) => theme.text2};
  font-size: 12px;
  padding: 2px 0;
  cursor: pointer;
  text-align: left;
`;
const DetailWrap = styled.div`
  padding: 10px 16px 14px 44px;
`;
const DetailTitle = styled.div`
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  opacity: 0.6;
  margin-bottom: 6px;
`;
const DetailTable = styled.table`
  width: 100%;
  border-collapse: collapse;
`;
const DetailTh = styled.th`
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  padding: 4px 8px;
  color: ${({ theme }) => theme.text2};
  border-bottom: 1px solid ${({ theme }) => theme.color2};
`;
const DetailTd = styled.td`
  font-size: 12px;
  padding: 6px 8px;
  border-bottom: 1px dashed ${({ theme }) => theme.color2};
  color: ${({ theme }) => theme.text1};
`;
const CardDesc = styled.div`font-size: 13px; opacity: 0.7;`;
const CardActions = styled.div`display: flex; gap: 8px; margin-top: 4px;`;

const OrigenTag = styled.span`
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 4px;
  padding: 1px 6px;
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 999px;
  font-size: 10px;
  line-height: 16px;
  opacity: 0.65;
  white-space: nowrap;
  cursor: default;
`;
