import { Icon } from "@iconify/react";
import { Alert, Box, Button, Chip, CircularProgress, Link, Paper, Stack, Tooltip, Typography } from "@mui/material";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo, useRef, useState } from "react";
import { Link as RouterLink, useNavigate, useParams } from "react-router-dom";
import { toast } from "sonner";
import {
  actualizarCuotaInm, desistirOperacionInm, getBoletoInm, getOperacionInm, previsualizarBoletoInm, quitarTitularInm,
} from "../../api/inmobiliario.service";
import { EmptyState, PrereqChecklist, ScreenGuia } from "../../components/_standards";
import { chipDe, fmtMoneda, INM_LEGAJO_ESTADOS, INM_OPERACION_ESTADOS, INM_UNIDAD_TIPOS } from "../../components/_standards/enums";
import { mensajeError } from "../../components/organismos/inmobiliario/inmUtils";
import { BoletoDialog } from "../../components/organismos/inmobiliario/operaciones/BoletoDialog";
import { CobroDialog } from "../../components/organismos/inmobiliario/operaciones/CobroDialog";
import { CobrosCard } from "../../components/organismos/inmobiliario/operaciones/CobrosCard";
import { CronogramaCard } from "../../components/organismos/inmobiliario/operaciones/CronogramaCard";
import { DebidaDiligenciaCard } from "../../components/organismos/inmobiliario/operaciones/DebidaDiligenciaCard";
import { EstadoCuentaCard } from "../../components/organismos/inmobiliario/operaciones/EstadoCuentaCard";
import { FirmarBoletoDialog } from "../../components/organismos/inmobiliario/operaciones/FirmarBoletoDialog";
import { LegajoCard } from "../../components/organismos/inmobiliario/operaciones/LegajoCard";
import { Dato, Seccion } from "../../components/organismos/inmobiliario/operaciones/OperacionUi";
import {
  cuotaCobrable, ESTADOS_CON_COBRO, fmtIndice, fmtPct, fmtPeriodo, INM_AJUSTE_TIPOS, num,
} from "../../components/organismos/inmobiliario/operaciones/operacionUtils";
import { ProrrogaDialog } from "../../components/organismos/inmobiliario/operaciones/ProrrogaDialog";
import { TitularDialog } from "../../components/organismos/inmobiliario/operaciones/TitularDialog";
import { TitularesCard } from "../../components/organismos/inmobiliario/operaciones/TitularesCard";
import { AdendasCard } from "../../components/organismos/inmobiliario/postventa/AdendasCard";
import { ComisionesVentaCard } from "../../components/organismos/inmobiliario/postventa/ComisionesVentaCard";
import { EntregaCard } from "../../components/organismos/inmobiliario/postventa/EntregaCard";
import { EscrituraCard } from "../../components/organismos/inmobiliario/postventa/EscrituraCard";
import {
  ESTADOS_BAJA, ESTADOS_CON_ADENDA, ESTADOS_POSTVENTA,
} from "../../components/organismos/inmobiliario/postventa/postventaUtils";
import { useConfirmDialog } from "../../hooks/useConfirmDialog";
import { usePermission } from "../../hooks/usePermission";
import { fmtFechaCalendario, hoyISO, toFechaCalendarioISO } from "../../utils/fecha";

const GUIA_PASOS = [
  {
    icon: "mdi:cash-check",
    color: "#60a5fa",
    titulo: "1. Cobrá la seña",
    desc: "Elegí la cuota de seña en el cronograma y cobrala: se emite su factura de contado.",
  },
  {
    icon: "mdi:folder-account-outline",
    color: "#fbbf24",
    titulo: "2. Legajo y titulares",
    desc: "Cargá el origen de los fondos, aprobá el legajo y dejá los titulares sumando 100 %. Cada comprador necesita además su legajo de debida diligencia (SEPRELAD) vigente.",
  },
  {
    icon: "mdi:file-sign",
    color: "#34d399",
    titulo: "3. Firmá el boleto",
    desc: "Revisá la vista previa con el cliente y firmalo: se genera el resto del cronograma y desde ahí se cobran las cuotas.",
  },
  {
    icon: "mdi:key-variant",
    color: "#a78bfa",
    titulo: "4. Postventa",
    desc: "Con el boleto firmado: adendas (cambio de plan o unidad, cesión, rescisión), escrituración, entrega con su acta y las comisiones de la venta.",
  },
];

export function InmOperacionDetalle() {
  const { id } = useParams();
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const { confirm, prompt, dialogs } = useConfirmDialog();
  const { can } = usePermission("INMOBILIARIO");
  const canRead = can("INM_OPE_OPERACION_VER");
  const canEditar = can("INM_OPE_OPERACION_EDITAR");
  const canProrrogar = can("INM_OPE_RESERVA_PRORROGAR");
  const canDesistir = can("INM_OPE_OPERACION_ANULAR");
  const canAprobarLegajo = can("INM_OPE_LEGAJO_APROBAR");
  const canFirmar = can("INM_OPE_BOLETO_CONFIRMAR");
  const canCobrarPerm = can("INM_COB_COBRO_CREAR");
  const canVerPostventa = can("INM_POS_POSTVENTA_VER");
  const canVerComisiones = can("INM_COM_COMISION_VER");
  const canVerLegajoCliente = can("INM_CUM_LEGAJO_VER");

  const titularesRef = useRef(null);
  const legajoRef = useRef(null);
  const cronogramaRef = useRef(null);
  const diligenciaRef = useRef(null);

  const [seleccion, setSeleccion] = useState([]);
  const [cobroAbierto, setCobroAbierto] = useState(false);
  const [firmarAbierto, setFirmarAbierto] = useState(false);
  const [prorrogaAbierta, setProrrogaAbierta] = useState(false);
  const [titularDialogo, setTitularDialogo] = useState(null); // null cerrado · {} nuevo · titular
  const [boleto, setBoleto] = useState(null); // { titulo, html, cargando, aviso }
  const [aviso, setAviso] = useState(null);

  const queryKey = ["inmobiliario", "operacion", id];
  const { data: op, isLoading, isError } = useQuery({
    queryKey,
    queryFn: () => getOperacionInm(id),
    enabled: canRead && Boolean(id),
    refetchOnWindowFocus: false,
  });

  const cuotas = useMemo(() => op?.estado_cuenta?.cuotas ?? [], [op]);
  // Sólo las elegidas que siguen siendo cobrables (tras un cobro o un refresco pueden dejar de serlo).
  const cuotasACobrar = useMemo(
    () => cuotas.filter((c) => seleccion.includes(c.id) && cuotaCobrable(c, op?.estado)),
    [cuotas, seleccion, op?.estado],
  );

  const refrescarListas = () => {
    queryClient.invalidateQueries({ queryKey: ["inmobiliario", "operaciones"] });
    queryClient.invalidateQueries({ queryKey: ["inmobiliario", "cartera"] });
    queryClient.invalidateQueries({ queryKey: ["inmobiliario", "comisiones"] });
    queryClient.invalidateQueries({ queryKey: ["inmobiliario", "unidades"] });
  };

  /** Reemplaza partes del detalle en caché (postventa devuelve sólo la escritura o la entrega). */
  const parchear = (campos) => queryClient.setQueryData(queryKey, (prev) => (prev ? { ...prev, ...campos } : prev));

  /** Vuelve a pedir el detalle: la acción cambió el estado de la venta o sus cuotas. */
  const recargar = () => {
    queryClient.invalidateQueries({ queryKey });
    refrescarListas();
  };

  /** Las acciones de operación devuelven el detalle actualizado: se usa sin volver a pedirlo. */
  const aplicar = (r) => {
    if (r?.id === id) queryClient.setQueryData(queryKey, r);
    else queryClient.invalidateQueries({ queryKey });
    refrescarListas();
  };

  const ejecutar = async (fn, exito) => {
    try {
      const r = await fn();
      if (exito) toast.success(exito);
      aplicar(r);
      return r;
    } catch (e) {
      toast.error(mensajeError(e, "No se pudo completar la acción"));
      return null;
    }
  };

  if (!canRead) return null;
  if (isLoading) {
    return (
      <Box sx={{ p: 4, display: "flex", justifyContent: "center" }}>
        <CircularProgress />
      </Box>
    );
  }
  if (isError || !op) {
    return (
      <Box sx={{ p: 3 }}>
        <EmptyState
          icon="mdi:file-question-outline"
          title="No encontramos la operación"
          description="Puede que pertenezca a otra empresa o que el enlace esté mal."
          ctaLabel="Volver a ventas"
          ctaIcon="mdi:arrow-left"
          onCta={() => navigate("/inmobiliario/operaciones")}
        />
      </Box>
    );
  }

  const moneda = op.moneda?.codigo ?? "PYG";
  const estado = chipDe(INM_OPERACION_ESTADOS, op.estado);
  const reservada = op.estado === "RESERVADA";
  const canCobrar = canCobrarPerm && ESTADOS_CON_COBRO.includes(op.estado);
  const unidad = op.inm_unidades;
  const tipoUnidad = INM_UNIDAD_TIPOS[unidad?.tipo];
  const deBaja = ESTADOS_BAJA.includes(op.estado);
  const verAdendas = ESTADOS_CON_ADENDA.includes(op.estado) || (op.inm_adendas ?? []).length > 0;
  const verEscritura = canVerPostventa && (ESTADOS_POSTVENTA.includes(op.estado) || Boolean(op.inm_escrituras));
  const verEntrega = canVerPostventa && (ESTADOS_POSTVENTA.includes(op.estado) || Boolean(op.inm_entregas));

  // ── Qué falta para firmar el boleto ─────────────────────────────────
  const senas = cuotas.filter((c) => c.tipo === "SENA");
  const senaCobrada = senas.length > 0 && senas.every((c) => c.estado === "PAGADA");
  const saldoSena = senas.reduce((a, c) => a + num(c.saldo), 0);
  const legajoAprobado = op.legajo_estado === "APROBADO";
  const sumaTitulares = (op.inm_operacion_titulares ?? []).reduce((a, t) => a + num(t.porcentaje), 0);
  const titularesOk = Math.abs(sumaTitulares - 100) < 0.001;
  // Debida diligencia SEPRELAD (F5): sólo cuenta como requisito si la configuración la exige.
  const diligencia = op.debida_diligencia;
  const diligenciaExigida = Boolean(diligencia?.exigido);
  const sinDiligencia = (diligencia?.clientes ?? []).filter((c) => !c.vigente);
  const diligenciaOk = !diligenciaExigida || sinDiligencia.length === 0;
  const listoParaFirmar = senaCobrada && legajoAprobado && titularesOk && diligenciaOk;
  const motivoNoFirmar = diligenciaOk
    ? null
    : `Falta el legajo de debida diligencia vigente de ${sinDiligencia.map((c) => c.cliente?.nombre ?? "un comprador").join(", ")}`;
  const botonFirmar = (props) => (
    <Tooltip title={motivoNoFirmar ?? ""} enterDelay={300}>
      <span>
        <Button startIcon={<Icon icon="mdi:file-sign" />} onClick={() => setFirmarAbierto(true)} disabled={Boolean(motivoNoFirmar)} {...props}>
          Firmar boleto
        </Button>
      </span>
    </Tooltip>
  );

  const irA = (ref) => ref.current?.scrollIntoView({ behavior: "smooth", block: "start" });

  const cobrarSena = () => {
    const ids = senas.filter((c) => cuotaCobrable(c, op.estado)).map((c) => c.id);
    if (!ids.length) return;
    setSeleccion(ids);
    setCobroAbierto(true);
  };

  // ── Reserva ─────────────────────────────────────────────────────────
  const venceISO = toFechaCalendarioISO(op.reserva_vence);
  const hoy = hoyISO();
  const reservaVencida = venceISO && venceISO < hoy;

  const desistir = async () => {
    const motivo = await prompt({
      title: `Registrar el desistimiento de ${op.numero}`,
      message: "El comprador desiste antes de firmar el boleto. Indicá el motivo: queda en el historial de la operación.",
      label: "Motivo",
      placeholder: "Ej: el comprador no consiguió el crédito",
      required: true,
      maxLength: 255,
      confirmText: "Continuar",
    });
    if (!motivo) return;
    const ok = await confirm({
      title: "¿Confirmás el desistimiento?",
      message: `La operación pasa a desistida, las cuotas pendientes se anulan y la unidad ${unidad?.codigo ?? ""} vuelve a quedar disponible para la venta. No se puede deshacer.`,
      confirmText: "Desistir",
      confirmColor: "error",
    });
    if (!ok) return;
    const r = await ejecutar(() => desistirOperacionInm(op.id, motivo), "Desistimiento registrado");
    if (r?.aviso) {
      setAviso(r.aviso);
      toast.warning(r.aviso, { duration: 10000 });
    }
  };

  // ── Titulares ───────────────────────────────────────────────────────
  const quitarTitular = async (t) => {
    const ok = await confirm({
      title: "Quitar titular",
      message: `${t.cliente?.nombre ?? "El cliente"} deja de ser titular de ${op.numero}. Revisá después que los porcentajes sumen 100 %.`,
      confirmText: "Quitar",
      confirmColor: "error",
    });
    if (ok) ejecutar(() => quitarTitularInm(op.id, t.id), "Titular quitado");
  };

  // ── Cuotas sin fecha ────────────────────────────────────────────────
  const fijarVencimiento = async (c) => {
    const fecha = await prompt({
      title: `Fijar el vencimiento de la cuota ${c.nro}`,
      message:
        c.tipo === "POSESION"
          ? "Cuando se conoce la fecha de entrega de la posesión, fijala acá: desde ese día la cuota es exigible."
          : "La cuota vence cuando se cumple su hito. Si ya se acordó una fecha, fijala acá.",
      label: "Vencimiento",
      type: "date",
      required: true,
      confirmText: "Fijar vencimiento",
    });
    if (!fecha) return;
    ejecutar(() => actualizarCuotaInm(op.id, c.id, { fecha_vencimiento: fecha }), "Vencimiento fijado");
  };

  // ── Boleto ──────────────────────────────────────────────────────────
  const abrirBoleto = async (titulo, fn, avisoBoleto) => {
    setBoleto({ titulo, html: null, cargando: true, aviso: avisoBoleto });
    try {
      const r = await fn();
      setBoleto({ titulo, html: r?.html ?? "", cargando: false, aviso: avisoBoleto });
    } catch (e) {
      setBoleto(null);
      toast.error(mensajeError(e, "No se pudo generar el boleto"));
    }
  };
  const vistaPrevia = (fecha) =>
    abrirBoleto(
      `Vista previa del boleto — ${op.numero}`,
      () => previsualizarBoletoInm(op.id, fecha),
      "Vista previa: el boleto no tiene validez hasta que se firma en el sistema. El cronograma usa la fecha de firma indicada.",
    );
  const verBoleto = () => abrirBoleto(`Boleto de compraventa — ${op.numero}`, () => getBoletoInm(op.id));

  return (
    <Box sx={{ p: { xs: 2, md: 3 } }}>
      <Stack spacing={2}>
        <Box>
          <Button size="small" startIcon={<Icon icon="mdi:arrow-left" />} onClick={() => navigate("/inmobiliario/operaciones")}>
            Volver a ventas
          </Button>
        </Box>

        <ScreenGuia
          moduloPadre="Inmobiliario"
          titulo="¿Cómo sigue esta venta?"
          intro="Desde acá se lleva la venta completa: reserva, legajo, boleto, cobro de cuotas con su factura y el estado de cuenta."
          pasos={GUIA_PASOS}
        />

        {deBaja && (
          <Alert severity={op.estado === "RESCINDIDA" ? "error" : "warning"} icon={<Icon icon="mdi:lock-outline" />}>
            La venta está {estado.label.toLowerCase()}
            {op.motivo_baja ? `: ${op.motivo_baja}` : "."} Se muestra sólo para consulta: no admite cambios.
          </Alert>
        )}

        {/* ── Cabecera ── */}
        <Paper variant="outlined" sx={{ p: 2 }}>
          <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap>
            <Icon icon={tipoUnidad?.icon ?? "mdi:home-outline"} width={26} />
            <Typography variant="h5" sx={{ mr: 0.5 }}>
              {op.numero}
            </Typography>
            <Chip size="small" color={estado.color} label={estado.label} />
            {op.inm_cotizaciones && (
              <Chip
                size="small"
                variant="outlined"
                icon={<Icon icon="mdi:calculator-variant-outline" width={14} />}
                label={`Desde ${op.inm_cotizaciones.numero}`}
                component={RouterLink}
                to={`/inmobiliario/cotizaciones/${op.inm_cotizaciones.id}`}
                clickable
              />
            )}
            <Box sx={{ flex: 1 }} />
            {reservada && canFirmar && botonFirmar({ variant: "contained" })}
          </Stack>

          <Box
            sx={{
              mt: 2,
              display: "grid",
              gridTemplateColumns: "repeat(auto-fill, minmax(min(100%, 200px), 1fr))",
              gap: 2,
            }}
          >
            <Dato label="Proyecto">
              {op.inm_proyectos ? (
                <Link component={RouterLink} to={`/inmobiliario/proyectos/${op.inm_proyectos.id}`} underline="hover">
                  {op.inm_proyectos.nombre}
                </Link>
              ) : null}
            </Dato>
            <Dato label="Unidad">{unidad ? `${unidad.codigo}${tipoUnidad ? ` · ${tipoUnidad.label}` : ""}` : null}</Dato>
            <Dato label="Comprador">
              {op.cliente ? (
                <>
                  {op.cliente.nombre}
                  <Typography variant="caption" color="text.secondary" component="div">
                    {[op.cliente.documento, op.cliente.telefono].filter(Boolean).join(" · ")}
                  </Typography>
                </>
              ) : null}
            </Dato>
            <Dato label="Precio final">
              <Typography variant="body1" fontWeight={700} component="span">
                {fmtMoneda(num(op.precio_final), moneda)}
              </Typography>
              {num(op.descuento_pct) > 0 && (
                <Typography variant="caption" color="text.secondary" component="div">
                  Lista {fmtMoneda(num(op.precio_lista), moneda)} · descuento {fmtPct(op.descuento_pct)}
                </Typography>
              )}
            </Dato>
            <Dato label="Moneda">{moneda}</Dato>
            <Dato label="Ajuste">
              {INM_AJUSTE_TIPOS[op.ajuste]?.label ?? op.ajuste}
              {op.ajuste === "IPC_BCP" && (
                <Typography variant="caption" color="text.secondary" component="div">
                  {op.indice_firma_valor != null
                    ? `IPC de firma ${fmtIndice(op.indice_firma_valor)} (${fmtPeriodo(toFechaCalendarioISO(op.indice_firma_periodo))})`
                    : "El IPC de firma se fija al firmar el boleto"}
                </Typography>
              )}
            </Dato>
            <Dato label="Reserva">{fmtFechaCalendario(op.fecha_reserva)}</Dato>
            <Dato label="Boleto">{op.fecha_boleto ? fmtFechaCalendario(op.fecha_boleto) : "Sin firmar"}</Dato>
            {op.fecha_escritura && <Dato label="Escritura">{fmtFechaCalendario(op.fecha_escritura)}</Dato>}
            {op.fecha_entrega && <Dato label="Entrega">{fmtFechaCalendario(op.fecha_entrega)}</Dato>}
          </Box>

          {op.motivo_baja && !deBaja && (
            <Alert severity="info" sx={{ mt: 2 }}>
              Motivo de la baja: {op.motivo_baja}
            </Alert>
          )}
          {op.notas && (
            <Typography variant="body2" color="text.secondary" sx={{ mt: 1.5, whiteSpace: "pre-wrap" }}>
              {op.notas}
            </Typography>
          )}
        </Paper>

        {aviso && (
          <Alert severity="warning" onClose={() => setAviso(null)}>
            {aviso}
          </Alert>
        )}

        {/* ── Qué sigue (sólo con la reserva viva) ── */}
        {reservada && (
          <Stack spacing={1}>
            <PrereqChecklist
              title="Qué falta para firmar el boleto"
              items={[
                {
                  key: "sena",
                  label: "Seña cobrada",
                  ok: senaCobrada,
                  hint: senaCobrada ? undefined : `falta cobrar ${fmtMoneda(saldoSena, moneda)}`,
                  ctaLabel: canCobrar ? "Cobrar la seña" : "Ver cronograma",
                  onGoTo: canCobrar ? cobrarSena : () => irA(cronogramaRef),
                },
                {
                  key: "legajo",
                  label: "Legajo del comprador aprobado",
                  ok: legajoAprobado,
                  hint: legajoAprobado ? undefined : `hoy está ${chipDe(INM_LEGAJO_ESTADOS, op.legajo_estado).label.toLowerCase()}`,
                  ctaLabel: "Ir al legajo",
                  onGoTo: () => irA(legajoRef),
                },
                {
                  key: "titulares",
                  label: "Titulares que suman 100 %",
                  ok: titularesOk,
                  hint: titularesOk ? undefined : `hoy suman ${fmtPct(sumaTitulares)}`,
                  ctaLabel: "Ir a titulares",
                  onGoTo: () => irA(titularesRef),
                },
                ...(diligenciaExigida
                  ? [
                      {
                        key: "diligencia",
                        label: "Legajo de debida diligencia vigente de cada comprador (SEPRELAD)",
                        ok: diligenciaOk,
                        hint: diligenciaOk
                          ? undefined
                          : `falta: ${sinDiligencia.map((c) => c.cliente?.nombre ?? "un comprador").join(", ")}`,
                        ctaLabel: "Ver compradores",
                        onGoTo: () => irA(diligenciaRef),
                      },
                    ]
                  : []),
              ]}
            />
            <DebidaDiligenciaCard ref={diligenciaRef} debidaDiligencia={diligencia} puedeVerLegajo={canVerLegajoCliente} />
            {listoParaFirmar && canFirmar && (
              <Alert
                severity="success"
                action={
                  <Button color="inherit" size="small" onClick={() => setFirmarAbierto(true)}>
                    Firmar boleto
                  </Button>
                }
              >
                Todo listo: ya se puede firmar el boleto.
              </Alert>
            )}
          </Stack>
        )}

        {/* ── Reserva ── */}
        {reservada && (
          <Seccion
            titulo="Reserva"
            icon="mdi:bookmark-check-outline"
            acciones={
              <Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
                {canProrrogar && (
                  <Button size="small" variant="outlined" startIcon={<Icon icon="mdi:calendar-plus" />} onClick={() => setProrrogaAbierta(true)}>
                    Prorrogar
                  </Button>
                )}
                {canDesistir && (
                  <Button size="small" color="error" startIcon={<Icon icon="mdi:close-circle-outline" />} onClick={desistir}>
                    Desistir
                  </Button>
                )}
              </Stack>
            }
          >
            <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap>
              <Typography variant="body2">
                {reservaVencida ? "Venció el" : "Vence el"} <strong>{fmtFechaCalendario(op.reserva_vence)}</strong>
              </Typography>
              {reservaVencida && <Chip size="small" color="error" label="Vencida" />}
              <Typography variant="caption" color="text.secondary">
                {op.reserva_prorrogas > 0
                  ? `Prorrogada ${op.reserva_prorrogas} ${op.reserva_prorrogas === 1 ? "vez" : "veces"}`
                  : "Sin prórrogas"}
              </Typography>
            </Stack>
            <Typography variant="caption" color="text.secondary" component="div" sx={{ mt: 0.5 }}>
              Si vence sin la seña cobrada, la reserva se libera sola a la madrugada siguiente. Con la seña cobrada queda una alerta
              para firmar, prorrogar o registrar el desistimiento.
            </Typography>
          </Seccion>
        )}

        <EstadoCuentaCard operacion={op} />

        <CronogramaCard
          ref={cronogramaRef}
          operacion={op}
          seleccion={seleccion}
          onSeleccion={setSeleccion}
          puedeCobrar={canCobrar}
          puedeFijarFecha={canEditar && op.estado !== "DESISTIDA" && op.estado !== "RESCINDIDA"}
          onCobrar={() => setCobroAbierto(true)}
          onFijarVencimiento={fijarVencimiento}
        />

        <Box
          sx={{
            display: "grid",
            gridTemplateColumns: "repeat(auto-fill, minmax(min(100%, 380px), 1fr))",
            gap: 2,
            alignItems: "start",
          }}
        >
          <TitularesCard
            ref={titularesRef}
            operacion={op}
            editable={reservada && canEditar}
            onAgregar={() => setTitularDialogo({})}
            onEditar={(t) => setTitularDialogo(t)}
            onQuitar={quitarTitular}
          />
          <LegajoCard
            ref={legajoRef}
            operacion={op}
            editable={reservada && canEditar}
            puedeAprobar={reservada && canAprobarLegajo}
            onActualizado={aplicar}
          />
        </Box>

        {/* ── Boleto ── */}
        <Seccion
          titulo="Boleto de compraventa"
          icon="mdi:file-sign"
          acciones={
            <Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
              {op.tiene_boleto ? (
                <Button size="small" variant="outlined" startIcon={<Icon icon="mdi:printer-outline" />} onClick={verBoleto}>
                  Ver / imprimir boleto
                </Button>
              ) : (
                reservada && (
                  <>
                    <Button size="small" variant="outlined" startIcon={<Icon icon="mdi:file-eye-outline" />} onClick={() => vistaPrevia()}>
                      Vista previa
                    </Button>
                    {canFirmar && botonFirmar({ size: "small", variant: "contained" })}
                  </>
                )
              )}
            </Stack>
          }
        >
          <Typography variant="body2" color="text.secondary">
            {op.tiene_boleto
              ? `Firmado el ${fmtFechaCalendario(op.fecha_boleto)}. Se muestra el texto tal como quedó guardado al firmar.`
              : reservada
                ? "Todavía no se firmó. La vista previa usa la plantilla de Configuración inmobiliaria con los datos de esta venta."
                : "Esta operación no tiene boleto firmado."}
          </Typography>
        </Seccion>

        {/* ── Postventa ── */}
        {verAdendas && (
          <AdendasCard
            operacion={op}
            soloLectura={deBaja}
            onOperacion={aplicar}
            onAdendas={(inm_adendas) => parchear({ inm_adendas })}
          />
        )}
        {verEscritura && (
          <EscrituraCard
            operacion={op}
            soloLectura={deBaja}
            onEscritura={(inm_escrituras) => parchear({ inm_escrituras })}
            onRecargar={recargar}
          />
        )}
        {verEntrega && (
          <EntregaCard
            operacion={op}
            soloLectura={deBaja}
            onEntrega={(inm_entregas) => parchear({ inm_entregas })}
            onRecargar={recargar}
          />
        )}
        {canVerComisiones && <ComisionesVentaCard operacion={op} soloLectura={deBaja} onRecargar={recargar} />}

        <CobrosCard operacion={op} />
      </Stack>

      <CobroDialog
        open={cobroAbierto}
        onClose={() => setCobroAbierto(false)}
        operacion={op}
        cuotas={cuotasACobrar}
        onCobrado={() => {
          setSeleccion([]);
          queryClient.invalidateQueries({ queryKey });
          refrescarListas();
        }}
      />
      <FirmarBoletoDialog
        open={firmarAbierto}
        onClose={() => setFirmarAbierto(false)}
        operacion={op}
        onFirmado={aplicar}
        onVistaPrevia={vistaPrevia}
      />
      <ProrrogaDialog open={prorrogaAbierta} onClose={() => setProrrogaAbierta(false)} operacion={op} onGuardado={aplicar} />
      <TitularDialog
        open={titularDialogo !== null}
        onClose={() => setTitularDialogo(null)}
        operacion={op}
        titular={titularDialogo?.id ? titularDialogo : null}
        onGuardado={aplicar}
      />
      <BoletoDialog
        open={Boolean(boleto)}
        onClose={() => setBoleto(null)}
        titulo={boleto?.titulo ?? "Boleto"}
        html={boleto?.html}
        cargando={boleto?.cargando}
        aviso={boleto?.aviso}
      />
      {dialogs}
    </Box>
  );
}

export default InmOperacionDetalle;
