import { Icon } from "@iconify/react";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { RevisionCxCGuia } from "./RevisionCxCGuia";
import styled from "styled-components";
import { FieldHint, MonedaInput } from "../_standards";
import {
  getCuentasCobrarByCliente,
  getCuentasCobrarPorCliente,
  marcarCuentaComoPagada,
  regenerarCuotas,
  updateCuentaCobrar,
} from "../../api/cobros.service";
import { getCondicionesPago } from "../../api/condiciones-pago.service";
import { getNcDisponiblesCliente, marcarNcConsumida } from "../../api/recibos-multi.service";
import { anularInteresManual, crearInteresManual, getInteresesManuales } from "../../api/interes-manual.service";
import { fmtMoneda } from "../_standards/enums/monedas";

// ─── Helpers ──────────────────────────────────────────────────────────────────
const fmtGs = (v) =>
  `Gs. ${Number(v || 0).toLocaleString("es-PY", { maximumFractionDigits: 0 })}`;

const fmtFecha = (val) => {
  if (!val) return "—";
  try {
    // Handle ISO strings with timezone offset that cause off-by-one
    // e.g. "2025-05-15T00:00:00.000Z" → parse as local date
    const str = typeof val === "string" ? val : val instanceof Date ? val.toISOString() : String(val);
    const match = str.match(/^(\d{4})-(\d{2})-(\d{2})/);
    if (match) {
      const [, y, m, d] = match;
      return `${d}/${m}/${y}`;
    }
    const date = new Date(str);
    if (isNaN(date.getTime())) return "—";
    return date.toLocaleDateString("es-PY", {
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
    });
  } catch {
    return "—";
  }
};

const addDays = (dateStr, days) => {
  const d = new Date(dateStr);
  d.setDate(d.getDate() + days);
  return d;
};

const formatDateInput = (date) => {
  if (!date) return "";
  const d = new Date(date);
  return d.toISOString().split("T")[0];
};

const parseGsInput = (str) => {
  const clean = String(str).replace(/[^\d]/g, "");
  return clean ? parseInt(clean, 10) : 0;
};

const formatGsInput = (num) =>
  Number(num || 0).toLocaleString("es-PY", { maximumFractionDigits: 0 });

// Redondeo adaptativo (mismo patrón que NuevaSolicitudCredito):
//   ≥ 10.000 → al millar   (115.385 → 115.000)
//   ≥  1.000 → a la centena (9.615 → 9.600)
//   <  1.000 → sin redondeo
// La diferencia se absorbe en la última cuota.
const redondearCuota = (monto) => {
  if (monto >= 10000) return Math.floor(monto / 1000) * 1000;
  if (monto >= 1000)  return Math.floor(monto / 100) * 100;
  return Math.round(monto);
};

const FRECUENCIAS = [
  { value: "semanal", label: "Semanal", dias: 7 },
  { value: "quincenal", label: "Quincenal", dias: 15 },
  { value: "mensual", label: "Mensual", dias: 30 },
];

const calcProximoVenc = (dias) => {
  const d = new Date();
  d.setDate(d.getDate() + dias);
  return formatDateInput(d);
};

const frecuenciaDesdeIntervaloDias = (dias) => {
  if (dias === 7) return "semanal";
  if (dias === 15) return "quincenal";
  return "mensual";
};

const PAGE_SIZE = 20;

// ─── Styles ───────────────────────────────────────────────────────────────────
const Layout = styled.div`
  display: flex;
  height: calc(100vh - 160px);
  gap: 0;
  background: ${({ theme }) => theme.bg1};
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid ${({ theme }) => theme.color2};
`;

const LeftPanel = styled.div`
  width: 300px;
  min-width: 300px;
  display: flex;
  flex-direction: column;
  border-right: 1px solid ${({ theme }) => theme.color2};
  background: ${({ theme }) => theme.bg2};
`;

const RightPanel = styled.div`
  flex: 1;
  overflow-y: auto;
  background: ${({ theme }) => theme.bg1};
`;

const PanelHeader = styled.div`
  padding: 12px 14px;
  border-bottom: 1px solid ${({ theme }) => theme.color2};
  background: ${({ theme }) => theme.bg2};

  h3 {
    margin: 0 0 10px 0;
    font-size: 13px;
    font-weight: 600;
    color: ${({ theme }) => theme.color1};
    text-transform: uppercase;
    letter-spacing: 0.5px;
  }
`;

const SearchInput = styled.input`
  width: 100%;
  height: 34px;
  padding: 0 10px;
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 6px;
  background: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.color1};
  font-size: 13px;
  box-sizing: border-box;
  outline: none;

  &:focus {
    border-color: ${({ theme }) => theme.primary};
  }

  &::placeholder {
    color: ${({ theme }) => theme.textSecondary};
  }
`;

const CheckLabel = styled.label`
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: ${({ theme }) => theme.textSecondary};
  cursor: pointer;
  margin-top: 8px;

  input[type="checkbox"] {
    accent-color: ${({ theme }) => theme.primary};
  }
`;

const ClientList = styled.div`
  flex: 1;
  overflow-y: auto;
`;

const ClientItem = styled.div`
  padding: 10px 14px;
  cursor: pointer;
  border-bottom: 1px solid ${({ theme }) => theme.color2}33;
  background: ${({ selected, theme }) =>
    selected ? `${theme.primary}18` : "transparent"};
  border-left: 3px solid
    ${({ selected, theme }) => (selected ? theme.primary : "transparent")};
  transition: background 0.12s;

  &:hover {
    background: ${({ theme }) => theme.bg3};
  }
`;

const ClientName = styled.div`
  font-size: 13px;
  font-weight: 600;
  color: ${({ theme }) => theme.color1};
  display: flex;
  align-items: center;
  gap: 6px;

  .check {
    color: #22c55e;
    font-size: 12px;
  }
`;

const ClientMeta = styled.div`
  font-size: 11px;
  color: ${({ theme }) => theme.textSecondary};
  margin-top: 2px;
  display: flex;
  justify-content: space-between;
`;

const Pagination = styled.div`
  padding: 10px 14px;
  border-top: 1px solid ${({ theme }) => theme.color2};
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  color: ${({ theme }) => theme.textSecondary};
`;

const PagBtn = styled.button`
  padding: 4px 10px;
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 5px;
  background: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.color1};
  font-size: 12px;
  cursor: pointer;
  &:disabled {
    opacity: 0.4;
    cursor: default;
  }
`;

// Right panel styles
const DetailWrap = styled.div`
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
`;

const EmptyState = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: ${({ theme }) => theme.textSecondary};
  gap: 10px;
  font-size: 14px;
`;

const DetailHeader = styled.div`
  background: ${({ theme }) => theme.bg2};
  border-radius: 10px;
  padding: 14px 18px;
  border: 1px solid ${({ theme }) => theme.color2};
`;

const ClientNameBig = styled.h2`
  margin: 0 0 4px 0;
  font-size: 18px;
  font-weight: 700;
  color: ${({ theme }) => theme.color1};
`;

const ClientRuc = styled.div`
  font-size: 13px;
  color: ${({ theme }) => theme.textSecondary};
`;

const SectionCard = styled.div`
  background: ${({ theme }) => theme.bg2};
  border-radius: 10px;
  border: 1px solid ${({ theme }) => theme.color2};
  overflow: hidden;
`;

const SectionTitle = styled.div`
  padding: 10px 16px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: ${({ theme }) => theme.textSecondary};
  border-bottom: 1px solid ${({ theme }) => theme.color2};
  background: ${({ theme }) => theme.bg3};
`;

const SectionBody = styled.div`
  padding: 14px 16px;
`;

const FieldRow = styled.div`
  display: grid;
  grid-template-columns: ${({ cols }) => cols || "1fr 1fr"};
  gap: 12px;
  margin-bottom: 12px;

  &:last-child {
    margin-bottom: 0;
  }
`;

const FieldLabel = styled.label`
  display: block;
  font-size: 11px;
  font-weight: 600;
  color: ${({ theme }) => theme.textSecondary};
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-bottom: 5px;
`;

const FieldValue = styled.div`
  font-size: 14px;
  color: ${({ theme }) => theme.color1};
`;

const StyledInput = styled.input`
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 7px;
  background: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.color1};
  font-size: 14px;
  box-sizing: border-box;
  outline: none;

  &:focus {
    border-color: ${({ theme }) => theme.primary};
  }
`;

const StyledSelect = styled.select`
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 7px;
  background: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.color1};
  font-size: 14px;
  box-sizing: border-box;
  outline: none;
  cursor: pointer;

  &:focus {
    border-color: ${({ theme }) => theme.primary};
  }
`;

const Preview = styled.div`
  background: ${({ theme }) => theme.bg1};
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 7px;
  padding: 10px 12px;
  font-size: 13px;
  color: ${({ theme }) => theme.color1};
  line-height: 1.6;

  .highlight {
    color: ${({ theme }) => theme.primary};
    font-weight: 600;
  }
`;

const BtnRow = styled.div`
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
`;

const Btn = styled.button`
  height: 36px;
  padding: 0 18px;
  border-radius: 7px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  transition: opacity 0.15s;
  border: none;

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;

const BtnPrimary = styled(Btn)`
  background: ${({ theme }) => theme.primary};
  color: #fff;

  &:hover:not(:disabled) {
    opacity: 0.88;
  }
`;

const BtnSecondary = styled(Btn)`
  background: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.color1};
  border: 1px solid ${({ theme }) => theme.color2};

  &:hover:not(:disabled) {
    background: ${({ theme }) => theme.bg4};
  }
`;

const BtnDanger = styled(Btn)`
  background: #ef4444;
  color: #fff;

  &:hover:not(:disabled) {
    opacity: 0.88;
  }
`;

const BtnSuccess = styled(Btn)`
  background: #22c55e;
  color: #fff;

  &:hover:not(:disabled) {
    opacity: 0.88;
  }
`;

// Cuotas table
const CuotasTable = styled.table`
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;

  th {
    text-align: left;
    padding: 8px 12px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: ${({ theme }) => theme.textSecondary};
    border-bottom: 1px solid ${({ theme }) => theme.color2};
    background: ${({ theme }) => theme.bg3};
  }

  td {
    padding: 8px 12px;
    border-bottom: 1px solid ${({ theme }) => theme.color2}55;
    color: ${({ theme }) => theme.color1};
  }

  tr:last-child td {
    border-bottom: none;
  }

  tr:hover td {
    background: ${({ theme }) => theme.bg3}55;
  }
`;

const EstadoBadge = styled.span`
  display: inline-block;
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 600;
  background: ${({ estado }) =>
    estado === "pendiente" ? "#f59e0b22" : "#22c55e22"};
  color: ${({ estado }) =>
    estado === "pendiente" ? "#d97706" : "#16a34a"};
`;

const InteresBadge = styled.span`
  display: inline-block;
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 600;
  background: #f59e0b22;
  color: #d97706;
`;

const BtnMini = styled.button`
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 6px;
  padding: 3px 8px;
  border-radius: 6px;
  border: 1px solid ${({ theme }) => theme.color2};
  background: transparent;
  color: ${({ theme }) => theme.color1};
  font-size: 11px;
  cursor: pointer;

  &:hover:not(:disabled) {
    background: ${({ theme }) => theme.bg3};
  }
  &:disabled {
    opacity: 0.5;
    cursor: default;
  }
`;

const InteresForm = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  align-items: end;
  padding: 12px;
  border-radius: 8px;
  background: ${({ theme }) => theme.bg3};

  label {
    display: block;
    margin-bottom: 4px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: ${({ theme }) => theme.textSecondary};
  }
`;

const CargosList = styled.ul`
  list-style: none;
  margin: 8px 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;

  li {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    font-size: 13px;
    color: ${({ theme }) => theme.color1};
  }

  .parcial,
  .motivo {
    font-size: 11px;
    color: ${({ theme }) => theme.textSecondary};
  }
`;

const Nota = styled.p`
  margin: 4px 0 0;
  font-size: 11px;
  line-height: 1.4;
  color: ${({ theme }) => theme.textSecondary};
`;

// Selector de cuenta: antes era una nube de chips (un cliente con 60 facturas
// llenaba la pantalla y tapaba la ficha). Ahora es una lista con buscador,
// filtro por saldo y scroll propio.
const SelectorBarra = styled.div`
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 16px;
  border-bottom: 1px solid ${({ theme }) => theme.color2};
`;

const BuscadorCuenta = styled.div`
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 220px;
  min-width: 180px;
  padding: 5px 10px;
  border-radius: 8px;
  border: 1px solid ${({ theme }) => theme.color2};
  background: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.textSecondary};

  input {
    flex: 1;
    min-width: 0;
    border: none;
    background: transparent;
    color: ${({ theme }) => theme.text};
    font-family: inherit;
    font-size: 12px;
    outline: none;
  }

  button {
    border: none;
    background: none;
    cursor: pointer;
    color: inherit;
    display: flex;
    padding: 0;
  }
`;

const Conteo = styled.div`
  font-size: 12px;
  color: ${({ theme }) => theme.textSecondary};
  white-space: nowrap;

  strong { color: ${({ theme }) => theme.text}; }
`;

const VerSaldadas = styled.label`
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: ${({ theme }) => theme.textSecondary};
  cursor: pointer;
  white-space: nowrap;
`;

const ListaCuentas = styled.div`
  display: flex;
  flex-direction: column;
`;

// Las columnas se definen una vez y las comparten encabezado y filas.
const COLUMNAS_CUENTA = "minmax(150px, 1.4fr) minmax(96px, 0.9fr) minmax(96px, 0.9fr) minmax(96px, 0.9fr) minmax(88px, 0.7fr)";

const ListaHead = styled.div`
  display: grid;
  grid-template-columns: ${COLUMNAS_CUENTA};
  gap: 8px;
  padding: 8px 16px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: ${({ theme }) => theme.textSecondary};
  border-bottom: 1px solid ${({ theme }) => theme.color2};

  .num { text-align: right; }

  @media (max-width: 720px) { display: none; }
`;

const ListaBody = styled.div`
  /* Alto acotado: la ficha del cliente y los datos de la cuenta tienen que
     seguir visibles sin scrollear toda la página. */
  max-height: 300px;
  overflow-y: auto;
`;

const FilaCuenta = styled.button`
  display: grid;
  grid-template-columns: ${COLUMNAS_CUENTA};
  gap: 8px;
  align-items: center;
  width: 100%;
  padding: 9px 16px;
  border: none;
  border-left: 3px solid ${({ $selected, theme }) => ($selected ? theme.primary : "transparent")};
  border-bottom: 1px solid ${({ theme }) => theme.color2}55;
  background: ${({ $selected, theme }) => ($selected ? `${theme.primary}18` : "transparent")};
  color: ${({ theme }) => theme.text};
  font-family: inherit;
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
  opacity: ${({ $saldada, $selected }) => ($saldada && !$selected ? 0.55 : 1)};

  &:hover { background: ${({ $selected, theme }) => ($selected ? `${theme.primary}18` : `${theme.bg3}`)}; }

  .nro { font-weight: 600; }
  .num { text-align: right; font-variant-numeric: tabular-nums; }
  .saldo { font-weight: 600; }
  .venc { color: #ef4444; font-weight: 600; }

  @media (max-width: 720px) {
    grid-template-columns: 1fr 1fr;
    row-gap: 2px;
    .num { text-align: left; }
  }
`;

const SinResultados = styled.div`
  padding: 18px 16px;
  font-size: 12.5px;
  text-align: center;
  color: ${({ theme }) => theme.textSecondary};
`;

const ConfirmOverlay = styled.div`
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
`;

const ConfirmBox = styled.div`
  background: ${({ theme }) => theme.bg2};
  border: 1px solid ${({ theme }) => theme.color2};
  border-radius: 12px;
  padding: 24px 28px;
  max-width: 400px;
  width: 90%;

  h4 {
    margin: 0 0 10px 0;
    font-size: 16px;
    font-weight: 700;
    color: ${({ theme }) => theme.color1};
  }

  p {
    margin: 0 0 20px 0;
    font-size: 13px;
    color: ${({ theme }) => theme.textSecondary};
    line-height: 1.5;
  }

  .actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
  }
`;

// ─── Component ────────────────────────────────────────────────────────────────
export function RevisionCxCTab() {
  // ── State: left panel ──────────────────────────────────────────────────────
  const [allClientes, setAllClientes] = useState([]);
  const [loadingList, setLoadingList] = useState(false);
  const [searchQ, setSearchQ] = useState("");
  const [soloMigrados, setSoloMigrados] = useState(false);
  const [page, setPage] = useState(1);
  const [revisados, setRevisados] = useState(new Set());

  // ── State: right panel ─────────────────────────────────────────────────────
  const [selectedCliente, setSelectedCliente] = useState(null);
  const [cuentas, setCuentas] = useState([]);
  const [loadingCuentas, setLoadingCuentas] = useState(false);
  const [selectedCuentaId, setSelectedCuentaId] = useState(null);
  // Selector de cuenta: por defecto sólo las que tienen saldo (lo que se viene a
  // revisar). Un cliente con 60 facturas tenía 60 chips, casi todos en Gs. 0.
  const [buscaCuenta, setBuscaCuenta] = useState("");
  const [verSaldadas, setVerSaldadas] = useState(false);

  // Edit saldo
  const [saldoInput, setSaldoInput] = useState(0);
  const [savingSaldo, setSavingSaldo] = useState(false);

  // Regenerar
  const [frecuencia, setFrecuencia] = useState("mensual");
  const [nroCuotasInput, setNroCuotasInput] = useState(1);
  // Monto por cuota editable + modo: "cantidad" = el usuario controla cantidad y monto se deriva;
  // "monto" = el usuario controla monto y cantidad se deriva. La última cuota absorbe el resto.
  const [montoCuotaInput, setMontoCuotaInput] = useState(0);
  const [modoCuota, setModoCuota] = useState("cantidad");
  const [duracionDias, setDuracionDias] = useState(30); // fuente de verdad para conversión de frecuencia
  const [fechaPrimerVenc, setFechaPrimerVenc] = useState("");
  // Fecha de vencimiento capturada al seleccionar la cuenta (no cambia tras regeneración)
  const [fechaVencRef, setFechaVencRef] = useState(null);
  const [regenerando, setRegenerando] = useState(false);
  const [condicionesPago, setCondicionesPago] = useState([]);
  const [selectedCondicion, setSelectedCondicion] = useState("");

  // Marcar como pagada
  const [confirmPagadaOpen, setConfirmPagadaOpen] = useState(false);
  const [marcandoPagada, setMarcandoPagada] = useState(false);

  // Notas de crédito del cliente
  const [ncList, setNcList] = useState([]);
  const [confirmNcConsumida, setConfirmNcConsumida] = useState(null); // nc | null
  const [marcandoNc, setMarcandoNc] = useState(false);

  const searchRef = useRef(null);
  const debounceRef = useRef(null);

  // ── Load client list ───────────────────────────────────────────────────────
  const loadClientes = useCallback(async () => {
    setLoadingList(true);
    try {
      const data = await getCuentasCobrarPorCliente();
      const list = Array.isArray(data) ? data : [];
      setAllClientes(list);
      // Sincronizar revisados desde BD
      setRevisados(new Set(list.filter((c) => c.revisado).map((c) => c.id)));
    } catch {
      toast.error("Error al cargar clientes");
    } finally {
      setLoadingList(false);
    }
  }, []);

  useEffect(() => {
    loadClientes();
  }, [loadClientes]);

  useEffect(() => {
    getCondicionesPago()
      .then((data) => setCondicionesPago(
        Array.isArray(data) ? data.filter((c) => !c.es_sistema && c.cod_condicion_venta !== 1) : []
      ))
      .catch(() => {});
  }, []);

  // ── Filtered + paginated list ──────────────────────────────────────────────
  const filteredClientes = useMemo(() => {
    let list = allClientes;
    if (soloMigrados) {
      list = list.filter((c) =>
        c.dinfadic?.includes?.("MIGRADO_ACCESS") ||
        c.ruc?.startsWith?.("SIN_RUC")
      );
    }
    if (searchQ.trim()) {
      const q = searchQ.toLowerCase();
      list = list.filter(
        (c) =>
          c.razon_social?.toLowerCase().includes(q) ||
          c.ruc?.toLowerCase().includes(q) ||
          c.nro_documento?.toLowerCase().includes(q) ||
          c.cod_cliente?.toLowerCase().includes(q)
      );
    }
    return list;
  }, [allClientes, searchQ, soloMigrados]);

  const totalPages = Math.max(1, Math.ceil(filteredClientes.length / PAGE_SIZE));
  const pageClientes = filteredClientes.slice(
    (page - 1) * PAGE_SIZE,
    page * PAGE_SIZE
  );

  const handleSearch = (val) => {
    clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => {
      setSearchQ(val);
      setPage(1);
    }, 250);
  };

  const [interesCuenta, setInteresCuenta] = useState([]);

  const recargarInteres = useCallback(async (facturaId) => {
    if (!facturaId) return setInteresCuenta([]);
    try {
      const cargos = await getInteresesManuales(facturaId);
      setInteresCuenta(Array.isArray(cargos) ? cargos : []);
    } catch {
      setInteresCuenta([]);
    }
  }, []);

  // ── Select client ──────────────────────────────────────────────────────────
  /** Número de factura legible de una cuenta (mismo formato que usa la ficha). */
  const numeroDeCuenta = useCallback((c) => {
    const f = c.factura_cab;
    if (!f) return "Sin factura";
    const nro = f.dnumdoc || f.dnumfac;
    if (!nro) return "Sin número";
    return `${f.dest || ""}-${f.dpunexp || ""}-${nro}`;
  }, []);

  const cuentasConSaldo = useMemo(
    () => cuentas.filter((c) => Number(c.saldo_pendiente ?? 0) > 0),
    [cuentas],
  );

  const cuentasVisibles = useMemo(() => {
    const texto = buscaCuenta.trim().toLowerCase();
    const base = verSaldadas ? cuentas : cuentasConSaldo;
    const filtradas = texto
      ? base.filter((c) => numeroDeCuenta(c).toLowerCase().includes(texto))
      : base;
    // Vencidas primero: es el orden en que un cobrador las mira.
    return [...filtradas].sort((a, b) => {
      const fa = a.fecha_vencimiento ?? "9999-12-31";
      const fb = b.fecha_vencimiento ?? "9999-12-31";
      return String(fa).localeCompare(String(fb));
    });
  }, [cuentas, cuentasConSaldo, verSaldadas, buscaCuenta, numeroDeCuenta]);

  const hoyISOFecha = useMemo(() => new Date().toISOString().slice(0, 10), []);

  const selectCliente = useCallback(async (cliente) => {
    setSelectedCliente(cliente);
    setSelectedCuentaId(null);
    setCuentas([]);
    setNcList([]);
    setLoadingCuentas(true);
    try {
      const [data, ncs] = await Promise.all([
        getCuentasCobrarByCliente(cliente.id),
        getNcDisponiblesCliente(cliente.id).catch(() => []),
      ]);
      setCuentas(Array.isArray(data) ? data : []);
      setNcList(Array.isArray(ncs) ? ncs : []);
      if (data?.length > 0) {
        // La primera con saldo, no la primera de la lista: es la que se va a editar.
        const inicial = data.find((c) => Number(c.saldo_pendiente ?? 0) > 0) ?? data[0];
        setBuscaCuenta("");
        setVerSaldadas(Number(inicial.saldo_pendiente ?? 0) <= 0);
        setSelectedCuentaId(inicial.id);
        initCuentaForm(inicial);
        recargarInteres(inicial.factura_cab?.id);
      }
    } catch {
      toast.error("Error al cargar cuentas del cliente");
    } finally {
      setLoadingCuentas(false);
    }
  }, [recargarInteres]);

  const handleMarcarNcConsumida = useCallback(async (nc) => {
    setMarcandoNc(true);
    try {
      await marcarNcConsumida(nc.id);
      setNcList((prev) => prev.filter((n) => n.id !== nc.id));
      setConfirmNcConsumida(null);
      toast.success(`NC ${nc.numero} marcada como consumida`);
    } catch {
      toast.error("No se pudo marcar la NC");
    } finally {
      setMarcandoNc(false);
    }
  }, []);

  // ── Interés manual por cuota ───────────────────────────────────────────────
  // Cargo que se cobra junto con la cuota. No toca su capital: las cuotas son el
  // desglose de la factura y su suma tiene que seguir dando el total facturado.
  const [cuotaConInteres, setCuotaConInteres] = useState(null); // id de la cuota en edición
  const [interesMonto, setInteresMonto] = useState(0);
  const [interesMotivo, setInteresMotivo] = useState("");
  const [guardandoInteres, setGuardandoInteres] = useState(false);

  const recargarCuentas = useCallback(async (clienteId) => {
    const data = await getCuentasCobrarByCliente(clienteId);
    setCuentas(Array.isArray(data) ? data : []);
  }, []);

  const abrirInteres = (cuota) => {
    setCuotaConInteres(cuota.id);
    setInteresMonto(0);
    setInteresMotivo("");
  };

  const handleGuardarInteresCuenta = async () => {
    const monto = Number(interesMonto) || 0;
    if (monto <= 0) {
      toast.error("Ingresá un monto de interés mayor a cero");
      return;
    }
    setGuardandoInteres(true);
    try {
      await crearInteresManual({
        factura_cab_id: selectedCuenta.factura_cab?.id,
        monto,
        ...(interesMotivo.trim() ? { motivo: interesMotivo.trim() } : {}),
      });
      await Promise.all([recargarCuentas(selectedCliente.id), recargarInteres(selectedCuenta.factura_cab?.id)]);
      setCuotaConInteres(null);
      setInteresMonto(0);
      setInteresMotivo("");
      toast.success("Interés cargado a la cuenta — se cobra en el próximo cobro");
    } catch (e) {
      toast.error(e?.response?.data?.message || "No se pudo cargar el interés");
    } finally {
      setGuardandoInteres(false);
    }
  };

  const handleAnularCargo = async (cargo) => {
    setGuardandoInteres(true);
    try {
      await anularInteresManual(cargo.id);
      await Promise.all([recargarCuentas(selectedCliente.id), recargarInteres(selectedCuenta.factura_cab?.id)]);
      toast.success("Interés quitado");
    } catch (e) {
      toast.error(e?.response?.data?.message || "No se pudo quitar el interés");
    } finally {
      setGuardandoInteres(false);
    }
  };

  const handleGuardarInteres = async (cuota) => {
    const monto = Number(interesMonto) || 0;
    if (monto <= 0) {
      toast.error("Ingresá un monto de interés mayor a cero");
      return;
    }
    setGuardandoInteres(true);
    try {
      await crearInteresManual({
        factura_cuota_id: cuota.id,
        monto,
        ...(interesMotivo.trim() ? { motivo: interesMotivo.trim() } : {}),
      });
      await Promise.all([recargarCuentas(selectedCliente.id), recargarInteres(selectedCuenta.factura_cab?.id)]);
      setCuotaConInteres(null);
      toast.success("Interés cargado — se va a cobrar junto con la cuota");
    } catch (e) {
      toast.error(e?.response?.data?.message || "No se pudo cargar el interés");
    } finally {
      setGuardandoInteres(false);
    }
  };

  const handleQuitarInteres = async (cuota) => {
    setGuardandoInteres(true);
    try {
      const cargos = await getInteresesManuales(selectedCuenta.factura_cab?.id);
      const pendientes = (cargos || []).filter(
        (c) => c.factura_cuota_id === cuota.id && c.estado === "pendiente"
      );
      for (const cargo of pendientes) await anularInteresManual(cargo.id);
      await Promise.all([recargarCuentas(selectedCliente.id), recargarInteres(selectedCuenta.factura_cab?.id)]);
      toast.success("Interés quitado");
    } catch (e) {
      toast.error(e?.response?.data?.message || "No se pudo quitar el interés");
    } finally {
      setGuardandoInteres(false);
    }
  };

  const initCuentaForm = (cuenta) => {
    const saldo = Number(cuenta.saldo_pendiente ?? 0);
    setSaldoInput(saldo);
    setFechaVencRef(cuenta.fecha_vencimiento ?? null);
    setFrecuencia("mensual");
    setSelectedCondicion("");
    setModoCuota("cantidad");
    // estimate next due date from last cuota or today
    const pendientes = (cuenta.factura_cuotas || []).filter(
      (c) => c.estado === "pendiente"
    );
    const cant = pendientes.length > 0 ? pendientes.length : 1;
    if (pendientes.length > 0) {
      setFechaPrimerVenc(formatDateInput(pendientes[0].dvenccuo));
    } else {
      setFechaPrimerVenc(formatDateInput(new Date()));
    }
    setNroCuotasInput(cant);
    setDuracionDias(cant * 30);
    setMontoCuotaInput(cant > 0 ? redondearCuota(saldo / cant) : 0);
  };

  const selectedCuenta = useMemo(
    () => cuentas.find((c) => c.id === selectedCuentaId) || null,
    [cuentas, selectedCuentaId]
  );

  // Moneda de la factura: los montos de la cuenta se formatean con ella.
  const monedaCuenta = selectedCuenta?.factura_cab?.moneda?.codigo;

  // Cargos de interés de la cuenta (los que no cuelgan de una cuota) que todavía
  // se pueden cobrar.
  const cargosPendientesCuenta = interesCuenta.filter(
    (c) => !c.factura_cuota_id && c.estado === "pendiente"
  );

  const handleSelectCuenta = (cuenta) => {
    setSelectedCuentaId(cuenta.id);
    initCuentaForm(cuenta);
    setCuotaConInteres(null);
    recargarInteres(cuenta.factura_cab?.id);
  };

  // ── Conversión de frecuencia: usa duracionDias como fuente de verdad ──────
  // Igual que NuevaSolicitudCredito: cambia frecuencia → recalcula cuotas manteniendo la duración total.
  useEffect(() => {
    const freq = FRECUENCIAS.find((f) => f.value === frecuencia);
    if (!freq || !duracionDias) return;
    setNroCuotasInput(Math.max(1, Math.ceil(duracionDias / freq.dias)));
    setFechaPrimerVenc(calcProximoVenc(freq.dias));
  }, [frecuencia]); // eslint-disable-line react-hooks/exhaustive-deps

  // ── Monto / cantidad bidireccional ─────────────────────────────────────────
  // En modo "cantidad": el usuario tipea cantidad, el monto se deriva con redondeo adaptativo.
  // En modo "monto":    el usuario tipea monto regular, la cantidad = ceil(saldo / monto).
  // En ambos casos la última cuota absorbe el resto: total exacto = saldo (nunca lo supera).
  const montoCuotaEfectivo = (() => {
    if (modoCuota === "monto") {
      if (montoCuotaInput <= 0) return 0;
      return Math.min(montoCuotaInput, saldoInput); // clamp si monto > saldo
    }
    return nroCuotasInput > 0 ? redondearCuota(saldoInput / nroCuotasInput) : 0;
  })();

  const ultimaCuotaMonto = (() => {
    if (montoCuotaEfectivo <= 0 || nroCuotasInput < 1) return saldoInput;
    const restante = saldoInput - montoCuotaEfectivo * (nroCuotasInput - 1);
    // La última cuota absorbe el residuo por redondeo (puede quedar > monto regular).
    // Coincide con el backend en regenerarCuotas.
    return Math.max(0, restante);
  })();

  // Re-derivar el lado dependiente cuando cambia el saldo o el modo
  useEffect(() => {
    if (saldoInput <= 0) return;
    if (modoCuota === "cantidad") {
      setMontoCuotaInput(nroCuotasInput > 0 ? redondearCuota(saldoInput / nroCuotasInput) : 0);
    } else {
      if (montoCuotaInput > 0) {
        const cant = Math.max(1, Math.ceil(saldoInput / montoCuotaInput));
        setNroCuotasInput(cant);
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [saldoInput, modoCuota]);

  // ── Preview calculation ────────────────────────────────────────────────────
  const preview = useMemo(() => {
    const freq = FRECUENCIAS.find((f) => f.value === frecuencia);
    if (!nroCuotasInput || !saldoInput || !freq || !fechaPrimerVenc) return null;
    const monto = montoCuotaEfectivo;
    const ultima = addDays(fechaPrimerVenc, (nroCuotasInput - 1) * freq.dias);
    return { total: nroCuotasInput, monto, ultimaMonto: ultimaCuotaMonto, ultima, saldo: saldoInput };
  }, [saldoInput, nroCuotasInput, montoCuotaEfectivo, ultimaCuotaMonto, frecuencia, fechaPrimerVenc]);

  // ── Save saldo ─────────────────────────────────────────────────────────────
  const handleGuardarSaldo = async (marcarRevisado = false) => {
    if (!selectedCuenta) return;
    const saldo = saldoInput;
    setSavingSaldo(true);
    try {
      await updateCuentaCobrar(selectedCuenta.id, { saldo_pendiente: saldo, revisado: marcarRevisado });
      toast.success(marcarRevisado ? "Guardado y marcado como revisado" : "Saldo actualizado");
      if (marcarRevisado) {
        setRevisados((prev) => new Set([...prev, selectedCliente.id]));
        // Actualizar también en allClientes para el indicador de la lista
        setAllClientes((prev) =>
          prev.map((c) => (c.id === selectedCliente.id ? { ...c, revisado: true } : c))
        );
      }
      const data = await getCuentasCobrarByCliente(selectedCliente.id);
      setCuentas(Array.isArray(data) ? data : []);
    } catch (e) {
      const msg = e?.response?.data?.message || e?.message || "Error al guardar saldo";
      toast.error(msg);
    } finally {
      setSavingSaldo(false);
    }
  };

  // ── Regenerar cuotas ───────────────────────────────────────────────────────
  const [confirmOpen, setConfirmOpen] = useState(false);

  const handleRegenerarClick = () => {
    if (!selectedCuenta) return;
    if (!nroCuotasInput || nroCuotasInput < 1 || !fechaPrimerVenc) {
      toast.error("Completá la cantidad de cuotas y la fecha de primer vencimiento");
      return;
    }
    setConfirmOpen(true);
  };

  const handleRegenerarConfirm = async () => {
    setConfirmOpen(false);
    const monto = montoCuotaEfectivo;
    setRegenerando(true);
    try {
      // Guardar saldo actualizado ANTES de regenerar para que el backend use el correcto
      await updateCuentaCobrar(selectedCuenta.id, { saldo_pendiente: saldoInput });

      await regenerarCuotas(selectedCuenta.id, {
        monto_cuota: monto,
        frecuencia,
        fecha_primer_vencimiento: fechaPrimerVenc,
        forzar: true,
      });
      const data = await getCuentasCobrarByCliente(selectedCliente.id);
      const cuentasActualizadas = Array.isArray(data) ? data : [];
      setCuentas(cuentasActualizadas);

      // Detectar si quedaron cuotas con cobros previos (saldo < monto = pagos parciales)
      const cuentaActual = cuentasActualizadas.find((c) => c.id === selectedCuenta.id);
      const cuotasConCobros = (cuentaActual?.factura_cuotas || []).filter(
        (c) => Number(c.saldo_pendiente ?? c.dmoncuota) < Number(c.dmoncuota)
      );
      if (cuotasConCobros.length > 0) {
        toast.warning(
          `Cuotas regeneradas. Atención: ${cuotasConCobros.length} cuota(s) con cobros registrados no fueron eliminadas y permanecen en la lista.`
        );
      } else {
        toast.success("Cuotas regeneradas correctamente");
      }
    } catch (e) {
      const msg = e?.response?.data?.message || e?.message || "Error al regenerar cuotas";
      toast.error(msg);
    } finally {
      setRegenerando(false);
    }
  };

  const handleSelectCondicion = (condicionId) => {
    setSelectedCondicion(condicionId);
    if (!condicionId) return;
    const cond = condicionesPago.find((c) => c.id === condicionId);
    if (!cond) return;
    const intervaloDias = cond.intervalo_dias ?? cond.dias_plazo ?? 30;
    const frec = frecuenciaDesdeIntervaloDias(intervaloDias);
    const cuotas = Math.max(1, cond.cuotas ?? 1);
    const freq = FRECUENCIAS.find((f) => f.value === frec);
    setFrecuencia(frec);
    setNroCuotasInput(cuotas);
    setDuracionDias(cuotas * (freq?.dias ?? 30));
    setFechaPrimerVenc(calcProximoVenc(intervaloDias));
  };

  const handleMarcarPagadaConfirm = async () => {
    if (!selectedCuenta) return;
    setConfirmPagadaOpen(false);
    setMarcandoPagada(true);
    try {
      await marcarCuentaComoPagada(selectedCuenta.id);
      toast.success("Cuenta marcada como pagada");
      const data = await getCuentasCobrarByCliente(selectedCliente.id);
      setCuentas(Array.isArray(data) ? data : []);
      // Actualizar saldo en lista de clientes
      setAllClientes((prev) =>
        prev.map((c) =>
          c.id === selectedCliente.id
            ? { ...c, saldo_total: Math.max(0, c.saldo_total - Number(selectedCuenta.saldo_pendiente)) }
            : c
        )
      );
    } catch (e) {
      toast.error(e?.response?.data?.message || e?.message || "Error al marcar como pagada");
    } finally {
      setMarcandoPagada(false);
    }
  };

  // ── Mark revisado + next ───────────────────────────────────────────────────
  const handleGuardarYSiguiente = async () => {
    await handleGuardarSaldo(true);
    goNext();
  };

  const goNext = () => {
    const idx = pageClientes.findIndex((c) => c.id === selectedCliente?.id);
    const next = pageClientes[idx + 1];
    if (next) selectCliente(next);
    else if (page < totalPages) {
      setPage((p) => p + 1);
    }
  };

  // ── Keyboard nav ──────────────────────────────────────────────────────────
  useEffect(() => {
    const handler = (e) => {
      if (document.activeElement === searchRef.current) return;
      const idx = pageClientes.findIndex((c) => c.id === selectedCliente?.id);
      if (e.key === "ArrowDown" && idx < pageClientes.length - 1) {
        selectCliente(pageClientes[idx + 1]);
      } else if (e.key === "ArrowUp" && idx > 0) {
        selectCliente(pageClientes[idx - 1]);
      }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [pageClientes, selectedCliente, selectCliente]);

  // ── Render ─────────────────────────────────────────────────────────────────
  const factura = selectedCuenta?.factura_cab;
  const nroFactura = factura
    ? `${factura.dest || ""}-${factura.dpunexp || ""}-${factura.dnumdoc || ""}`
    : "—";
  const articulo =
    factura?.factura_det?.[0]?.ddesproser || "—";

  return (
    <>
    <RevisionCxCGuia />
    <Layout>
      {/* ── Left panel ──────────────────────────────────────────── */}
      <LeftPanel>
        <PanelHeader>
          <h3>Clientes</h3>
          <SearchInput
            ref={searchRef}
            placeholder="Buscar por nombre o CI/RUC..."
            onChange={(e) => handleSearch(e.target.value)}
          />
          <CheckLabel>
            <input
              type="checkbox"
              checked={soloMigrados}
              onChange={(e) => {
                setSoloMigrados(e.target.checked);
                setPage(1);
              }}
            />
            Solo migrados desde Access
          </CheckLabel>
        </PanelHeader>

        <ClientList>
          {loadingList ? (
            <div style={{ padding: 20, textAlign: "center", fontSize: 13, opacity: 0.6 }}>
              Cargando...
            </div>
          ) : pageClientes.length === 0 ? (
            <div style={{ padding: 20, textAlign: "center", fontSize: 13, opacity: 0.6 }}>
              Sin resultados
            </div>
          ) : (
            pageClientes.map((c) => (
              <ClientItem
                key={c.id}
                selected={c.id === selectedCliente?.id}
                onClick={() => selectCliente(c)}
              >
                <ClientName>
                  {revisados.has(c.id) && (
                    <span className="check">✓</span>
                  )}
                  {c.razon_social || "Sin nombre"}
                </ClientName>
                <ClientMeta>
                  <span>{c.ruc || c.nro_documento || "—"}</span>
                  <span>{fmtGs(c.saldo_total)}</span>
                </ClientMeta>
              </ClientItem>
            ))
          )}
        </ClientList>

        <Pagination>
          <span>
            {filteredClientes.length > 0
              ? `${(page - 1) * PAGE_SIZE + 1}–${Math.min(page * PAGE_SIZE, filteredClientes.length)} de ${filteredClientes.length}`
              : "0 clientes"}
          </span>
          <div style={{ display: "flex", gap: 6 }}>
            <PagBtn disabled={page <= 1} onClick={() => setPage((p) => p - 1)}>
              ◀
            </PagBtn>
            <PagBtn
              disabled={page >= totalPages}
              onClick={() => setPage((p) => p + 1)}
            >
              ▶
            </PagBtn>
          </div>
        </Pagination>
      </LeftPanel>

      {/* ── Right panel ─────────────────────────────────────────── */}
      <RightPanel>
        {!selectedCliente ? (
          <EmptyState>
            <Icon icon="mdi:account-search" style={{ fontSize: 48, opacity: 0.3 }} />
            <span>Seleccioná un cliente para revisar sus cuentas</span>
          </EmptyState>
        ) : loadingCuentas ? (
          <EmptyState>
            <Icon icon="mdi:loading" style={{ fontSize: 32, opacity: 0.4, animation: "spin 1s linear infinite" }} />
            <span>Cargando cuentas...</span>
          </EmptyState>
        ) : (
          <DetailWrap>
            {/* Header */}
            <DetailHeader>
              <ClientNameBig>
                {selectedCliente.razon_social || "Sin nombre"}
                {revisados.has(selectedCliente.id) && (
                  <span style={{ color: "#22c55e", fontSize: 16, marginLeft: 10 }}>✓ Revisado</span>
                )}
              </ClientNameBig>
              <ClientRuc>
                RUC/CI: {selectedCliente.ruc || selectedCliente.nro_documento || "—"} &nbsp;|&nbsp;
                Saldo total: <strong>{fmtGs(selectedCliente.saldo_total)}</strong> &nbsp;|&nbsp;
                {selectedCliente.cuotas_totales} cuota(s) pendiente(s)
              </ClientRuc>
            </DetailHeader>

            {/* Selector de cuenta si hay más de 1 */}
            {cuentas.length > 1 && (
              <SectionCard>
                <SectionTitle>Cuenta a editar</SectionTitle>
                <SelectorBarra>
                  <BuscadorCuenta>
                    <Icon icon="lucide:search" width={14} />
                    <input
                      value={buscaCuenta}
                      onChange={(e) => setBuscaCuenta(e.target.value)}
                      placeholder="Buscar por nro. de factura..."
                      aria-label="Buscar cuenta por número de factura"
                    />
                    {buscaCuenta && (
                      <button type="button" onClick={() => setBuscaCuenta("")} aria-label="Limpiar búsqueda">
                        <Icon icon="lucide:x" width={13} />
                      </button>
                    )}
                  </BuscadorCuenta>
                  <Conteo>
                    <strong>{cuentasConSaldo.length}</strong> con saldo
                    {cuentas.length - cuentasConSaldo.length > 0 && (
                      <> · {cuentas.length - cuentasConSaldo.length} saldadas</>
                    )}
                  </Conteo>
                  {cuentas.length - cuentasConSaldo.length > 0 && (
                    <VerSaldadas>
                      <input
                        type="checkbox"
                        checked={verSaldadas}
                        onChange={(e) => setVerSaldadas(e.target.checked)}
                      />
                      Ver también las saldadas
                    </VerSaldadas>
                  )}
                </SelectorBarra>

                <ListaCuentas>
                  <ListaHead>
                    <span>Factura</span>
                    <span>Vencimiento</span>
                    <span className="num">Total</span>
                    <span className="num">Saldo</span>
                    <span>Estado</span>
                  </ListaHead>
                  <ListaBody>
                    {cuentasVisibles.length === 0 ? (
                      <SinResultados>
                        {buscaCuenta
                          ? "Ninguna factura coincide con la búsqueda."
                          : "Este cliente no tiene cuentas con saldo. Tildá \"Ver también las saldadas\" para revisarlas."}
                      </SinResultados>
                    ) : (
                      cuentasVisibles.map((c) => {
                        const saldo = Number(c.saldo_pendiente ?? 0);
                        const vencida = saldo > 0 && c.fecha_vencimiento && String(c.fecha_vencimiento).slice(0, 10) < hoyISOFecha;
                        return (
                          <FilaCuenta
                            key={c.id}
                            type="button"
                            $selected={c.id === selectedCuentaId}
                            $saldada={saldo <= 0}
                            onClick={() => handleSelectCuenta(c)}
                          >
                            <span className="nro">{numeroDeCuenta(c)}</span>
                            <span className={vencida ? "venc" : undefined}>
                              {fmtFecha(c.fecha_vencimiento)}
                              {vencida && " ⚠"}
                            </span>
                            {/* El total vive en la cuenta: `factura_cab.total_factura`
                                está vacío en la práctica. */}
                            <span className="num">{fmtGs(c.monto_total ?? c.factura_cab?.total_factura)}</span>
                            <span className="num saldo">{fmtGs(saldo)}</span>
                            <span>
                              <EstadoBadge estado={c.estado}>{c.estado || "—"}</EstadoBadge>
                            </span>
                          </FilaCuenta>
                        );
                      })
                    )}
                  </ListaBody>
                </ListaCuentas>
              </SectionCard>
            )}

            {cuentas.length === 0 && (
              <div style={{ padding: 20, textAlign: "center", opacity: 0.6, fontSize: 13 }}>
                Este cliente no tiene cuentas a cobrar registradas.
              </div>
            )}

            {ncList.length > 0 && (
              <SectionCard>
                <SectionTitle>
                  Notas de Crédito disponibles ({ncList.length})
                </SectionTitle>
                <SectionBody>
                  <div style={{ fontSize: 12, opacity: 0.7, marginBottom: 8 }}>
                    Si una NC ya fue usada fuera del sistema (cobro previo a la migración, aplicación manual, etc.), marcala como consumida
                    para que no aparezca disponible en recibos multi-factura.
                  </div>
                  {ncList.map((nc) => (
                    <div
                      key={nc.id}
                      style={{
                        display: "grid",
                        gridTemplateColumns: "1.4fr 1fr 1.2fr 1fr auto",
                        gap: 8,
                        alignItems: "center",
                        padding: "8px 0",
                        borderBottom: "1px solid var(--border, rgba(255,255,255,0.06))",
                      }}
                    >
                      <div>
                        <div style={{ fontWeight: 600 }}>{nc.numero}</div>
                        <div style={{ fontSize: 11, opacity: 0.6 }}>
                          {nc.factura_numero ? `Aplica a ${nc.factura_numero}` : "Sin factura asociada (FLEXIBLE)"}
                        </div>
                      </div>
                      <div style={{ fontSize: 12 }}>{fmtFecha(nc.fecha)}</div>
                      <div style={{ fontWeight: 600 }}>{fmtGs(nc.saldo_disponible)}</div>
                      <div style={{ fontSize: 11, opacity: 0.7 }}>{nc.rec_modo_aplicacion || "—"}</div>
                      <BtnDanger
                        type="button"
                        onClick={() => setConfirmNcConsumida(nc)}
                        style={{ padding: "4px 10px", fontSize: 12 }}
                      >
                        <Icon icon="mdi:check-decagram-outline" />
                        Marcar consumida
                      </BtnDanger>
                    </div>
                  ))}
                </SectionBody>
              </SectionCard>
            )}

            {selectedCuenta && (
              <>
                {/* Datos de la cuenta */}
                <SectionCard>
                  <SectionTitle>Datos de la Cuenta</SectionTitle>
                  <SectionBody>
                    <FieldRow cols="1fr 1fr 1fr">
                      <div>
                        <FieldLabel>Factura</FieldLabel>
                        <FieldValue>{nroFactura}</FieldValue>
                      </div>
                      <div>
                        <FieldLabel>Artículo</FieldLabel>
                        <FieldValue style={{ fontSize: 13 }}>{articulo}</FieldValue>
                      </div>
                      <div>
                        <FieldLabel>Total original</FieldLabel>
                        <FieldValue>
                          {fmtGs(factura?.total_factura)}
                        </FieldValue>
                      </div>
                    </FieldRow>
                    <FieldRow cols="1fr 1fr 1fr">
                      <div>
                        <FieldLabel>Estado</FieldLabel>
                        <FieldValue>
                          <EstadoBadge estado={selectedCuenta.estado}>
                            {selectedCuenta.estado}
                          </EstadoBadge>
                        </FieldValue>
                      </div>
                      <div>
                        <FieldLabel>Vencimiento</FieldLabel>
                        <FieldValue>{fmtFecha(selectedCuenta.fecha_vencimiento)}</FieldValue>
                      </div>
                      <div>
                        <FieldLabel>Total cuotas</FieldLabel>
                        <FieldValue>{factura?.dcuotas || "—"}</FieldValue>
                      </div>
                    </FieldRow>

                    <div>
                      <FieldLabel>Saldo pendiente (editable)</FieldLabel>
                      <MonedaInput
                        moneda="PYG"
                        value={saldoInput}
                        onChange={(v) => setSaldoInput(v)}
                        sx={{ maxWidth: 220 }}
                      />
                    </div>
                  </SectionBody>
                </SectionCard>

                {/* Regenerar cuotas */}
                <SectionCard>
                  <SectionTitle>Regenerar Cuotas Pendientes</SectionTitle>
                  <SectionBody>
                    <FieldRow cols="1fr" style={{ marginBottom: 16 }}>
                      <div>
                        <FieldLabel>Condición de pago (preset)</FieldLabel>
                        <StyledSelect
                          value={selectedCondicion}
                          onChange={(e) => handleSelectCondicion(e.target.value)}
                        >
                          <option value="">
                            {condicionesPago.length === 0 ? "— Sin condiciones cargadas —" : "— Seleccionar condición —"}
                          </option>
                          {condicionesPago.map((c) => (
                            <option key={c.id} value={c.id}>
                              {c.descripcion}
                              {c.cuotas ? ` · ${c.cuotas} cuota${c.cuotas !== 1 ? "s" : ""}` : ""}
                              {c.intervalo_dias ? ` · c/ ${c.intervalo_dias} días` : ""}
                            </option>
                          ))}
                        </StyledSelect>
                      </div>
                    </FieldRow>
                    <FieldRow cols="1.5fr 1fr 1fr 1.2fr">
                      <div>
                        <FieldLabel>Frecuencia</FieldLabel>
                        <StyledSelect
                          value={frecuencia}
                          onChange={(e) => setFrecuencia(e.target.value)}
                        >
                          {FRECUENCIAS.map((f) => (
                            <option key={f.value} value={f.value}>
                              {f.label}
                            </option>
                          ))}
                        </StyledSelect>
                      </div>
                      <div>
                        <FieldLabel>Cantidad de cuotas</FieldLabel>
                        <StyledInput
                          type="number"
                          min={1}
                          value={nroCuotasInput}
                          onChange={(e) => {
                            const n = Math.max(1, parseInt(e.target.value) || 1);
                            const freq = FRECUENCIAS.find((f) => f.value === frecuencia);
                            setModoCuota("cantidad");
                            setNroCuotasInput(n);
                            if (freq) setDuracionDias(n * freq.dias);
                            // Recalculá el monto inmediatamente para que el otro input refleje
                            if (saldoInput > 0) setMontoCuotaInput(redondearCuota(saldoInput / n));
                          }}
                          placeholder="1"
                        />
                        <span style={{ fontSize: 11, color: "#94a3b8", marginTop: 3, display: "block" }}>
                          Solo entero
                        </span>
                      </div>
                      <div>
                        <FieldLabel>Monto por cuota (Gs.)</FieldLabel>
                        <StyledInput
                          value={formatGsInput(montoCuotaInput)}
                          onChange={(e) => {
                            const v = parseGsInput(e.target.value);
                            setModoCuota("monto");
                            setMontoCuotaInput(v);
                            if (v > 0 && saldoInput > 0) {
                              const cant = Math.max(1, Math.ceil(saldoInput / Math.min(v, saldoInput)));
                              setNroCuotasInput(cant);
                              const freq = FRECUENCIAS.find((f) => f.value === frecuencia);
                              if (freq) setDuracionDias(cant * freq.dias);
                            }
                          }}
                          placeholder="0"
                        />
                        {ultimaCuotaMonto !== montoCuotaEfectivo && montoCuotaEfectivo > 0 && (
                          <span style={{ fontSize: 11, color: "#fbbf24", marginTop: 3, display: "block" }}>
                            Última cuota (prorrateada): {formatGsInput(ultimaCuotaMonto)}
                          </span>
                        )}
                      </div>
                      <div>
                        <FieldLabel>Primer vencimiento</FieldLabel>
                        <StyledInput
                          type="date"
                          value={fechaPrimerVenc}
                          onChange={(e) => setFechaPrimerVenc(e.target.value)}
                        />
                      </div>
                    </FieldRow>

                    {preview && (
                      <Preview style={{ marginBottom: 12 }}>
                        <span className="highlight">{preview.total} cuota(s)</span>
                        {" "}&times;{" "}
                        <span className="highlight">{fmtGs(preview.monto)}</span>
                        {preview.ultimaMonto !== preview.monto && (
                          <> (última: <span className="highlight">{fmtGs(preview.ultimaMonto)}</span>)</>
                        )}
                        {" "}—{" "}
                        Última: <span className="highlight">{fmtFecha(preview.ultima)}</span>
                        {" "}|{" "}
                        Saldo base: <span className="highlight">{fmtGs(preview.saldo)}</span>
                      </Preview>
                    )}

                    <BtnRow>
                      <BtnDanger onClick={handleRegenerarClick} disabled={regenerando}>
                        <Icon icon="mdi:refresh" />
                        {regenerando ? "Regenerando..." : "Regenerar Cuotas"}
                      </BtnDanger>
                      <span style={{ fontSize: 12, opacity: 0.6 }}>
                        Las cuotas con cobros registrados no serán afectadas.
                      </span>
                    </BtnRow>
                  </SectionBody>
                </SectionCard>

                {/* Interés de la cuenta */}
                <SectionCard>
                  <SectionTitle>Interés de la Cuenta</SectionTitle>
                  <SectionBody>
                    <Nota>
                      Se cobra en el próximo cobro de esta cuenta, junto con la cuota que se pague. Si el
                      cliente no puede pagarlo entero, el cobrador cobra una parte y el resto queda pendiente.
                      No cambia el capital ni el total de la factura.
                    </Nota>
                    {cargosPendientesCuenta.length > 0 && (
                      <CargosList>
                        {cargosPendientesCuenta.map((cargo) => {
                          const cobrado = Number(cargo.monto_cobrado || 0);
                          return (
                            <li key={cargo.id}>
                              <strong>{fmtMoneda(Number(cargo.monto) - cobrado, monedaCuenta)}</strong>
                              {cobrado > 0 && (
                                <span className="parcial">
                                  (cobrado {fmtMoneda(cobrado, monedaCuenta)} de {fmtMoneda(cargo.monto, monedaCuenta)})
                                </span>
                              )}
                              {cargo.motivo && <span className="motivo">{cargo.motivo}</span>}
                              {cobrado === 0 && (
                                <BtnMini onClick={() => handleAnularCargo(cargo)} disabled={guardandoInteres}>
                                  <Icon icon="mdi:close" /> Quitar
                                </BtnMini>
                              )}
                            </li>
                          );
                        })}
                      </CargosList>
                    )}
                    {cuotaConInteres === "cuenta" ? (
                      <InteresForm>
                        <div>
                          <label>Interés a cobrar</label>
                          <MonedaInput
                            value={interesMonto}
                            onChange={setInteresMonto}
                            moneda={monedaCuenta}
                            placeholder="0"
                          />
                        </div>
                        <div>
                          <label>Motivo (opcional)</label>
                          <FieldHint
                            value={interesMotivo}
                            onChange={(e) => setInteresMotivo(e.target.value)}
                            hint="Ej.: interés por atraso acordado con el cliente"
                            inputProps={{ maxLength: 255 }}
                            fullWidth
                          />
                        </div>
                        <BtnRow>
                          <BtnPrimary onClick={handleGuardarInteresCuenta} disabled={guardandoInteres}>
                            <Icon icon="mdi:content-save" />
                            {guardandoInteres ? "Guardando…" : "Cargar interés"}
                          </BtnPrimary>
                          <BtnSecondary onClick={() => setCuotaConInteres(null)}>Cancelar</BtnSecondary>
                        </BtnRow>
                      </InteresForm>
                    ) : (
                      <BtnRow>
                        <BtnSecondary
                          onClick={() => {
                            setCuotaConInteres("cuenta");
                            setInteresMonto(0);
                            setInteresMotivo("");
                          }}
                        >
                          <Icon icon="mdi:cash-plus" /> Agregar interés a la cuenta
                        </BtnSecondary>
                      </BtnRow>
                    )}
                  </SectionBody>
                </SectionCard>

                {/* Cuotas actuales */}
                <SectionCard>
                  <SectionTitle>
                    Cuotas Actuales ({selectedCuenta.factura_cuotas?.length || 0})
                  </SectionTitle>
                  {selectedCuenta.factura_cuotas?.length === 0 ? (
                    <SectionBody>
                      <div style={{ opacity: 0.6, fontSize: 13 }}>Sin cuotas registradas.</div>
                    </SectionBody>
                  ) : (
                    <CuotasTable>
                      <thead>
                        <tr>
                          <th>#</th>
                          <th>Monto</th>
                          <th>Vencimiento</th>
                          <th>Estado</th>
                          <th>Saldo</th>
                          <th>Interés</th>
                          <th />
                        </tr>
                      </thead>
                      <tbody>
                        {selectedCuenta.factura_cuotas.map((cuota) => {
                          const interes = Number(cuota.interes_manual || 0);
                          const cobrable = String(cuota.estado || "").toLowerCase() === "pendiente";
                          const editando = cuotaConInteres === cuota.id;
                          return (
                          <Fragment key={cuota.id}>
                          <tr>
                            <td>{cuota.nro_cuota}</td>
                            <td>{fmtMoneda(cuota.dmoncuota, monedaCuenta)}</td>
                            <td>{fmtFecha(cuota.dvenccuo)}</td>
                            <td>
                              <EstadoBadge estado={cuota.estado}>
                                {cuota.estado}
                              </EstadoBadge>
                            </td>
                            <td>{fmtMoneda(cuota.saldo_pendiente, monedaCuenta)}</td>
                            <td>
                              {interes > 0 ? (
                                <InteresBadge>{fmtMoneda(interes, monedaCuenta)}</InteresBadge>
                              ) : (
                                <span style={{ opacity: 0.4 }}>—</span>
                              )}
                            </td>
                            <td style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                              {cobrable && (
                                <>
                                  <BtnMini
                                    onClick={() => (editando ? setCuotaConInteres(null) : abrirInteres(cuota))}
                                    disabled={guardandoInteres}
                                    title="Cargar interés a esta cuota"
                                  >
                                    <Icon icon="mdi:cash-plus" /> Interés
                                  </BtnMini>
                                  {interes > 0 && (
                                    <BtnMini
                                      onClick={() => handleQuitarInteres(cuota)}
                                      disabled={guardandoInteres}
                                      title="Quitar el interés pendiente de esta cuota"
                                    >
                                      <Icon icon="mdi:close" />
                                    </BtnMini>
                                  )}
                                </>
                              )}
                            </td>
                          </tr>
                          {editando && (
                            <tr>
                              <td colSpan={7}>
                                <InteresForm>
                                  <div>
                                    <label>Interés a cobrar con la cuota {cuota.nro_cuota}</label>
                                    <MonedaInput
                                      value={interesMonto}
                                      onChange={setInteresMonto}
                                      moneda={monedaCuenta}
                                      placeholder="0"
                                    />
                                    <Nota>
                                      Se suma al cobro de esta cuota. No cambia el capital ni el total de la factura.
                                    </Nota>
                                  </div>
                                  <div>
                                    <label>Motivo (opcional)</label>
                                    <FieldHint
                                      value={interesMotivo}
                                      onChange={(e) => setInteresMotivo(e.target.value)}
                                      hint="Ej.: interés por atraso acordado con el cliente"
                                      inputProps={{ maxLength: 255 }}
                                      fullWidth
                                    />
                                  </div>
                                  <BtnRow>
                                    <BtnPrimary onClick={() => handleGuardarInteres(cuota)} disabled={guardandoInteres}>
                                      <Icon icon="mdi:content-save" />
                                      {guardandoInteres ? "Guardando…" : "Cargar interés"}
                                    </BtnPrimary>
                                    <BtnSecondary onClick={() => setCuotaConInteres(null)}>Cancelar</BtnSecondary>
                                  </BtnRow>
                                </InteresForm>
                              </td>
                            </tr>
                          )}
                          </Fragment>
                          );
                        })}
                      </tbody>
                    </CuotasTable>
                  )}
                </SectionCard>

                {/* Acciones finales */}
                <BtnRow>
                  <BtnPrimary
                    onClick={handleGuardarYSiguiente}
                    disabled={savingSaldo}
                  >
                    <Icon icon="mdi:content-save-check" />
                    Guardar y Siguiente →
                  </BtnPrimary>
                  <BtnSecondary onClick={goNext}>
                    Siguiente sin guardar →
                  </BtnSecondary>
                  {selectedCuenta?.estado !== "pagada" && (
                    <BtnSuccess
                      onClick={() => setConfirmPagadaOpen(true)}
                      disabled={marcandoPagada}
                    >
                      <Icon icon="mdi:check-circle-outline" />
                      {marcandoPagada ? "Procesando…" : "Marcar como Pagada"}
                    </BtnSuccess>
                  )}
                </BtnRow>
              </>
            )}
          </DetailWrap>
        )}
      </RightPanel>
    </Layout>
    {confirmPagadaOpen && (
      <ConfirmOverlay onClick={() => setConfirmPagadaOpen(false)}>
        <ConfirmBox onClick={(e) => e.stopPropagation()}>
          <h4>Marcar cuenta como pagada</h4>
          <p>
            Se marcará esta cuenta como <strong>pagada</strong> con saldo cero y todas sus cuotas pendientes
            quedarán liquidadas. <br /><br />
            Esta acción es un <strong>ajuste manual</strong> — no genera recibo de cobro ni
            registra movimiento de caja. Usá esta opción cuando el pago se realizó fuera del sistema.
          </p>
          {selectedCuenta && (
            <p>
              Saldo a cancelar: <strong>{fmtGs(selectedCuenta.saldo_pendiente)}</strong>
            </p>
          )}
          <div className="actions">
            <BtnSecondary onClick={() => setConfirmPagadaOpen(false)}>Cancelar</BtnSecondary>
            <BtnSuccess onClick={handleMarcarPagadaConfirm}>Confirmar</BtnSuccess>
          </div>
        </ConfirmBox>
      </ConfirmOverlay>
    )}
    {confirmNcConsumida && (
      <ConfirmOverlay onClick={() => setConfirmNcConsumida(null)}>
        <ConfirmBox onClick={(e) => e.stopPropagation()}>
          <h4>Marcar NC como consumida</h4>
          <p>
            La NC <strong>{confirmNcConsumida.numero}</strong> dejará de aparecer disponible para recibos
            multi-factura. Usá esta opción cuando la NC ya fue aplicada fuera del sistema o en un cobro
            no registrado.
          </p>
          <p>
            Saldo disponible a anular: <strong>{fmtGs(confirmNcConsumida.saldo_disponible)}</strong>
          </p>
          <div className="actions">
            <BtnSecondary onClick={() => setConfirmNcConsumida(null)}>Cancelar</BtnSecondary>
            <BtnDanger
              onClick={() => handleMarcarNcConsumida(confirmNcConsumida)}
              disabled={marcandoNc}
            >
              {marcandoNc ? "Procesando…" : "Confirmar"}
            </BtnDanger>
          </div>
        </ConfirmBox>
      </ConfirmOverlay>
    )}
    {confirmOpen && (
      <ConfirmOverlay onClick={() => setConfirmOpen(false)}>
        <ConfirmBox onClick={(e) => e.stopPropagation()}>
          <h4>Regenerar cuotas</h4>
          <p>
            Se eliminarán todas las cuotas <strong>pendientes</strong> de esta cuenta y se crearán nuevas con los parámetros indicados.
            Las cuotas con cobros registrados no serán afectadas.
          </p>
          {preview && (
            <p>
              Se crearán <strong>{preview.total} cuota(s)</strong> de{" "}
              <strong>{fmtGs(preview.monto)}</strong>
              {preview.ultimaMonto !== preview.monto && (
                <> (última: <strong>{fmtGs(preview.ultimaMonto)}</strong>)</>
              )}
              , última el <strong>{fmtFecha(preview.ultima)}</strong>.
            </p>
          )}
          <div className="actions">
            <BtnSecondary onClick={() => setConfirmOpen(false)}>Cancelar</BtnSecondary>
            <BtnDanger onClick={handleRegenerarConfirm}>Confirmar</BtnDanger>
          </div>
        </ConfirmBox>
      </ConfirmOverlay>
    )}
    </>
  );
}
