// =========================================================================
// CONTABILIDAD — vista de movimientos (ingresos/gastos) + tabla reutilizable
// Lee de DATA.MOVIMIENTOS (mapeado en supabase-client.jsx desde la tabla
// `movimientos`). Balances CALCULADOS, no escritos.
//
// CRITERIO emisor (CLAUDE.md §4): emisor_id SOLO aplica a INGRESOS (REVIN
// factura). En un gasto el que factura es el proveedor → NO se pinta emisor.
// El loader ya deja `emisor` a null en las filas de gasto; la tabla, además,
// solo lo muestra en ingresos.
// =========================================================================

const ESTADO_MOV_TONE = {
  pagado: "success", pendiente: "warning", parcial: "info",
  vencido: "danger", duplicado: "neutral", revisar: "danger",
};

const _fmtFechaMov = (d) =>
  d ? new Date(d).toLocaleDateString("es-ES", { day: "2-digit", month: "short", year: "2-digit" }) : "—";
const _capMov = (s) => (s ? s[0].toUpperCase() + s.slice(1) : s);

// Estilos mínimos propios (patrón de inyección ya usado en supabase-client.jsx)
const _injectContaStyles = () => {
  if (document.getElementById("conta-styles")) return;
  const s = document.createElement("style");
  s.id = "conta-styles";
  s.textContent = `
    .mov-sub { font-size: 11px; color: var(--fg-3); margin-top: 2px; }
    .mov-revisar td { background: var(--danger-bg); }
    .mov-revisar:hover td { background: var(--danger-bg); }
    .mov-revisar td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
    .mov-link { display: inline-flex; align-items: center; gap: 6px; }
    .mov-link.clickable { cursor: pointer; }
    .mov-link.clickable:hover { text-decoration: underline; }
    /* El COLOR de los importes ya no vive aquí: lo ponen las clases .fin-*
       (financial.css) a partir de los tokens de la escala financiera. Antes
       .mov-out usaba var(--danger) —el rojo de marca, el mismo del error— y un
       gasto normal se leía como una alarma. */
    .mov-op td { background: var(--bg-inset); }
    .mov-op td:first-child { box-shadow: inset 3px 0 0 var(--border-2); }
  `;
  document.head.appendChild(s);
};
_injectContaStyles();

// DemoDataBanner se fue a UI.jsx (B0.b), con los demás primitivos: lo montan tres
// módulos distintos y su CSS vive ahora en app.css, no inyectado desde aquí.

// ------------------------------------------------------------ TABLA (reutilizable)
// showVinculo=false en fichas de proyecto/inmueble (el vínculo es implícito).
const MovimientosTable = ({ rows, showVinculo = true, onOpenProperty, onOpenProject, empty = "Sin movimientos." }) => {
  const { fmtEUR, Pill } = window;

  const renderVinculo = (m) => {
    if (m.vinculoTipo === "proyecto") {
      const click = onOpenProject && m.proyectoId ? (e) => { e.stopPropagation(); onOpenProject(m.proyectoId); } : undefined;
      return (
        <span className={"mov-link" + (click ? " clickable" : "")} onClick={click}>
          <Pill tone="info">PROYECTO</Pill>
          <span>{m.vinculoLabel}</span>
        </span>
      );
    }
    if (m.vinculoTipo === "inmueble") {
      const click = onOpenProperty && m.inmuebleUiId ? (e) => { e.stopPropagation(); onOpenProperty(m.inmuebleUiId); } : undefined;
      return (
        <span className={"mov-link" + (click ? " clickable" : "")} onClick={click}>
          <Pill tone="neutral">INMUEBLE</Pill>
          <span>{m.vinculoLabel}</span>
        </span>
      );
    }
    return <span style={{ color: "var(--fg-4)" }}>Overhead empresa</span>;
  };

  const cols = showVinculo ? 7 : 6;

  return (
    <div className="tbl-wrap">
      <table className="tbl">
        <thead>
          <tr>
            <th>Fecha</th>
            <th>Concepto</th>
            <th>Categoría</th>
            {showVinculo && <th>Vínculo</th>}
            <th className="num">Importe</th>
            <th>Estado</th>
            <th>Pagado por</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((m) => {
            const esIngreso = m.tipo === "ingreso";
            // PLANO 1: dinero entre terceros. Se pinta en NEUTRO y sin signo —
            // un +8.000 € en verde se leería como ingreso de REVIN, y no lo es.
            const esOp = !!m.esOperacion;
            const partes = m.parteOrigen && m.parteDestino
              ? `${m.parteOrigen} → ${m.parteDestino}` : null;
            return (
              <tr key={m.id} className={(m.revisar ? "mov-revisar" : "") + (esOp ? " mov-op" : "")} style={{ cursor: "default" }}>
                <td className="mono" style={{ whiteSpace: "nowrap", color: "var(--fg-2)" }}>{_fmtFechaMov(m.fecha)}</td>
                <td>
                  <div style={{ fontWeight: 600, display: "flex", alignItems: "center", gap: 8 }}>
                    {esOp && <Pill tone="neutral">INFORMATIVO</Pill>}
                    <span>{m.concepto}</span>
                  </div>
                  {esOp && partes && <div className="mov-sub">{partes} · no pasa por REVIN</div>}
                  {!esOp && esIngreso && m.emisor && <div className="mov-sub">Emite: {m.emisor}</div>}
                  {/* Pagador del ingreso (p.ej. honorarios): puede haber varios
                      honorarios en un proyecto, cada uno a cargo de una parte. */}
                  {!esOp && esIngreso && m.parteOrigen && <div className="mov-sub">A cargo de: {m.parteOrigen}</div>}
                  {!esOp && !esIngreso && m.proveedorNombre && <div className="mov-sub">{m.proveedorNombre}</div>}
                  {!esOp && m.contrapartidaId && <div className="mov-sub">En custodia · netea contra su contrapartida</div>}
                </td>
                <td>{m.categoria ? <Pill tone="neutral">{m.categoria}</Pill> : <span style={{ color: "var(--fg-4)" }}>—</span>}</td>
                {showVinculo && <td>{renderVinculo(m)}</td>}
                {/* El Plano 1 va sin signo a propósito: un "+8.000 €" se leería
                    como ingreso de REVIN. Y un pendiente va en ámbar aunque sea
                    un ingreso — todavía no ha pasado por caja. */}
                <td className={"num mono fin-amt " + window.finMovCls(m)}>
                  {esOp ? "" : esIngreso ? "+" : "−"}{fmtEUR(m.importe)}
                </td>
                <td><Pill tone={ESTADO_MOV_TONE[m.estado] || "neutral"}>{_capMov(m.estado)}</Pill></td>
                <td style={{ fontSize: 12, color: "var(--fg-2)" }}>{esOp ? "—" : (m.pagadoPor || "—")}</td>
              </tr>
            );
          })}
          {rows.length === 0 && (
            <tr><td colSpan={cols} style={{ textAlign: "center", color: "var(--fg-3)", padding: 24 }}>{empty}</td></tr>
          )}
        </tbody>
      </table>
    </div>
  );
};

// ------------------------------------------------------------ VISTA CONTABILIDAD
const ContabilidadView = ({ onOpenProperty, onOpenProject }) => {
  const { DATA, PageHeader, Card, KPIRow, fmtEUR } = window;
  const all = DATA.MOVIMIENTOS || [];

  const [fTipo, setFTipo] = React.useState("todos");     // todos | ingreso | gasto
  const [fEstado, setFEstado] = React.useState("todos");
  const [fCat, setFCat] = React.useState("todos");        // "todos" | "__none__" | <categoria>
  const [fVinculo, setFVinculo] = React.useState("todos"); // todos | proyecto | inmueble | sin
  const [fEmisor, setFEmisor] = React.useState("todos");
  const [fPlano, setFPlano] = React.useState("todos"); // todos | contable | operacion
  const [q, setQ] = React.useState("");

  // Opciones de filtro derivadas de los datos
  const estados = React.useMemo(() => [...new Set(all.map(m => m.estado).filter(Boolean))].sort(), [all]);
  const cats = React.useMemo(() => [...new Set(all.map(m => m.categoria).filter(Boolean))].sort((a, b) => a.localeCompare(b, "es")), [all]);
  const emisores = React.useMemo(() => [...new Set(all.map(m => m.emisor).filter(Boolean))].sort(), [all]);
  const nIngresos = all.filter(m => m.tipo === "ingreso").length;
  const nGastos = all.filter(m => m.tipo === "gasto").length;

  // Filtrado
  let rows = all;
  if (fTipo !== "todos") rows = rows.filter(m => m.tipo === fTipo);
  if (fEstado !== "todos") rows = rows.filter(m => m.estado === fEstado);
  if (fCat === "__none__") rows = rows.filter(m => !m.categoria);
  else if (fCat !== "todos") rows = rows.filter(m => m.categoria === fCat);
  if (fVinculo !== "todos") rows = rows.filter(m => m.vinculoTipo === fVinculo);
  if (fEmisor !== "todos") rows = rows.filter(m => m.emisor === fEmisor);
  if (fPlano !== "todos") rows = rows.filter(m => (m.esOperacion ? "operacion" : "contable") === fPlano);
  if (q) {
    const s = q.toLowerCase();
    rows = rows.filter(m =>
      (m.concepto || "").toLowerCase().includes(s) ||
      (m.proveedorNombre || "").toLowerCase().includes(s) ||
      (m.clienteNombre || "").toLowerCase().includes(s) ||
      (m.vinculoLabel || "").toLowerCase().includes(s) ||
      (m.pagadoPor || "").toLowerCase().includes(s)
    );
  }
  rows = [...rows].sort((a, b) => {
    const av = a.fecha ? new Date(a.fecha).getTime() : 0;
    const bv = b.fecha ? new Date(b.fecha).getTime() : 0;
    return bv - av;
  });

  // KPIs — reflejan la selección actual (sin filtros = totales globales)
  const r2 = (x) => Math.round(x * 100) / 100;
  // Caja de la empresa: aquí SÍ entran capital y financiación (es flujo real de
  // la cuenta), pero NUNCA el PLANO 1 — ese dinero no es de REVIN.
  const cuenta = rows.filter(m => !m.esOperacion && m.estado !== "duplicado");
  const plano1 = rows.filter(m => m.esOperacion);
  const plano1Total = r2(plano1.reduce((s, m) => s + m.importe, 0));
  const ingresos = r2(cuenta.filter(m => m.tipo === "ingreso").reduce((s, m) => s + m.importe, 0));
  const gastos = r2(cuenta.filter(m => m.tipo === "gasto").reduce((s, m) => s + m.importe, 0));
  const balance = r2(ingresos - gastos);
  const nRevisar = rows.filter(m => m.revisar).length;

  // P&L por proyectos: los balances de proyecto ya son operativos (la vista y el
  // loader excluyen capital/deuda). Beneficio realizado = Σ balance de finalizados;
  // Invertido en curso = Σ gastos de proyectos aún en marcha.
  const proys = DATA.PROYECTOS || [];
  const proyFin = proys.filter(p => p.estado === "finalizado");
  const proyCurso = proys.filter(p => p.estado === "en_curso");
  const beneficioRealizado = r2(proyFin.reduce((s, p) => s + (p.balance || 0), 0));
  // Invertido en curso = gastos de proyectos en curso + flips en cartera viva
  // (inmuebles no vendidos). Sin doble conteo: por precedencia del loader, un
  // movimiento con proyecto_id NO expone inmuebleUiId; los que cuentan por el
  // lado inmueble son justo los que solo tienen inmueble_id.
  const invertidoProyectos = proyCurso.reduce((s, p) => s + (p.gastos == null ? 0 : Math.abs(p.gastos)), 0);
  const inmVivos = new Set((DATA.INMUEBLES || []).filter(i => !i.isVendido).map(i => i.id));
  const movInmVivos = (DATA.MOVIMIENTOS || []).filter(m =>
    m.tipo === "gasto" && computaEnBalance(m)
    && m.inmuebleUiId != null && inmVivos.has(m.inmuebleUiId));
  const invertidoInmuebles = movInmVivos.reduce((s, m) => s + m.importe, 0);
  const nInmVivosConGasto = new Set(movInmVivos.map(m => m.inmuebleUiId)).size;
  const invertidoEnCurso = r2(invertidoProyectos + invertidoInmuebles);

  const hayFiltro = fTipo !== "todos" || fEstado !== "todos" || fCat !== "todos" || fVinculo !== "todos" || fEmisor !== "todos" || fPlano !== "todos" || q;
  const selStyle = { width: "auto", padding: "6px 26px 6px 10px" };

  return (
    <>
      {window.DemoDataBanner && <window.DemoDataBanner/>}
      <PageHeader
        eyebrow={`${all.length} movimientos · ${hayFiltro ? `mostrando ${rows.length}` : "balances calculados"}`}
        title="CONTABILIDAD"
        sub="Ingresos y gastos como movimientos vinculados a proyectos e inmuebles. El balance se calcula, no se escribe a mano."
      />

      <KPIRow items={[
        { label: "Ingresos", value: fmtEUR(ingresos), fin: "fin-in" },
        { label: "Gastos", value: fmtEUR(gastos), fin: "fin-out" },
        { label: "Balance de caja", value: fmtEUR(balance), fin: window.finBalCls(balance), tone: balance < 0 ? "down" : "up", sub: "Entradas − salidas · incluye aportaciones y financiación · excluye Plano 1" },
        // Sin `fin`: es un recuento de filas sucias, no dinero. Pintarlo de
        // rojo financiero mezclaría las dos escalas.
        { label: "Pendiente de revisar", value: nRevisar, sub: "filas importadas sucias" },
      ]}/>

      {/* Resumen P&L por proyectos (rentabilidad operativa, sin capital) */}
      <Card title="P&L por proyectos" b={false} style={{ marginTop: 16 }}>
        <div style={{ display: "flex", gap: 48, padding: "14px 16px", flexWrap: "wrap" }}>
          <div>
            <div className="kv-lbl">Beneficio realizado <span style={{ color: "var(--fg-3)" }}>· {proyFin.length} finalizados</span></div>
            <div className={"mono " + window.finBalCls(beneficioRealizado)} style={{ fontSize: 20, fontWeight: 800 }}>{fmtEUR(beneficioRealizado)}</div>
            <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>suma de balances de proyectos finalizados</div>
          </div>
          <div>
            <div className="kv-lbl">Invertido en curso <span style={{ color: "var(--fg-3)" }}>· {proyCurso.length} proyectos + {nInmVivosConGasto} inmuebles</span></div>
            <div className="mono" style={{ fontSize: 20, fontWeight: 800, color: "var(--fg-1)" }}>{fmtEUR(invertidoEnCurso)}</div>
            <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>gastos de proyectos en curso + flips en cartera viva (no vendidos)</div>
          </div>
          {/* PLANO 1 — dinero entre terceros. Se enseña aparte y en gris para que
              nadie lo lea como caja de REVIN. Nunca entra en los KPIs de arriba. */}
          {plano1.length > 0 && (
            <div>
              <div className="kv-lbl">Datos de operación <span style={{ color: "var(--fg-3)" }}>· {plano1.length} apuntes</span></div>
              <div className="mono fin-info" style={{ fontSize: 20, fontWeight: 800 }}>{fmtEUR(plano1Total)}</div>
              <div style={{ fontSize: 11, color: "var(--fg-3)", marginTop: 2 }}>Plano 1 · dinero entre terceros — NO computa en el balance de REVIN</div>
            </div>
          )}
        </div>
      </Card>

      {/* Filtros: tipo por chips + selects para el resto */}
      <div className="fbar" style={{ marginTop: 20 }}>
        <button className={"fchip" + (fTipo === "todos" ? " active" : "")} onClick={() => setFTipo("todos")}>Todos · {all.length}</button>
        <button className={"fchip" + (fTipo === "ingreso" ? " active" : "")} onClick={() => setFTipo("ingreso")}>Ingresos · {nIngresos}</button>
        <button className={"fchip" + (fTipo === "gasto" ? " active" : "")} onClick={() => setFTipo("gasto")}>Gastos · {nGastos}</button>

        <div className="fbar-right">
          <select className="select" style={selStyle} value={fEstado} onChange={e => setFEstado(e.target.value)}>
            <option value="todos">Cualquier estado</option>
            {estados.map(s => <option key={s} value={s}>{_capMov(s)}</option>)}
          </select>
          <select className="select" style={selStyle} value={fCat} onChange={e => setFCat(e.target.value)}>
            <option value="todos">Cualquier categoría</option>
            <option value="__none__">(sin categoría)</option>
            {cats.map(c => <option key={c} value={c}>{c}</option>)}
          </select>
          <select className="select" style={selStyle} value={fVinculo} onChange={e => setFVinculo(e.target.value)}>
            <option value="todos">Cualquier vínculo</option>
            <option value="proyecto">Proyecto</option>
            <option value="inmueble">Inmueble</option>
            <option value="sin">Overhead (sin vínculo)</option>
          </select>
          <select className="select" style={selStyle} value={fEmisor} onChange={e => setFEmisor(e.target.value)}>
            <option value="todos">Cualquier emisor</option>
            {emisores.map(e => <option key={e} value={e}>{e}</option>)}
          </select>
          <select className="select" style={selStyle} value={fPlano} onChange={e => setFPlano(e.target.value)}>
            <option value="todos">Ambos planos</option>
            <option value="contable">Contable (computa)</option>
            <option value="operacion">Datos de operación</option>
          </select>
        </div>
      </div>

      <div style={{ marginBottom: 12 }}>
        <div className="tb-search" style={{ maxWidth: 380, padding: "7px 12px" }}>
          <window.Icons.Search/>
          <input
            value={q}
            onChange={e => setQ(e.target.value)}
            placeholder="Concepto, proveedor, cliente, quién pagó…"
            style={{ border: "none", background: "transparent", outline: "none", fontFamily: "inherit", fontSize: 13, color: "var(--fg-1)", flex: 1 }}
          />
        </div>
      </div>

      {all.length === 0 ? (
        <Card b={false}>
          <div className="empty" style={{ padding: 32 }}>
            <div className="icon-wrap"><window.Icons.Euro/></div>
            <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 6 }}>Sin movimientos cargados.</div>
            <div style={{ fontSize: 12, color: "var(--fg-3)", maxWidth: 420 }}>
              Conecta a Supabase para ver las transacciones reales. En modo de prueba se muestra un conjunto reducido de ejemplo.
            </div>
          </div>
        </Card>
      ) : (
        <Card title={`Movimientos · ${rows.length}`} b={false}>
          <MovimientosTable rows={rows} showVinculo onOpenProperty={onOpenProperty} onOpenProject={onOpenProject}/>
        </Card>
      )}
    </>
  );
};

// KPIs de balance para una ficha (proyecto/inmueble), calculados desde los
// movimientos filtrados. Reutilizable en Projects.jsx e Inventory.jsx.
// Balance de una operación (proyecto/inmueble): P&L real. Excluye 'duplicado'
// (anulados) y las categorías de CAPITAL/DEUDA (aportaciones de socio,
// financiación), que no son beneficio sino financiación de la operación.
const CATS_NO_PYL = ["Préstamo socio", "Financiación"];

// Filtro maestro de "esto es dinero de REVIN y cuenta". Un movimiento entra en
// un balance solo si es PLANO 2 (contable), no está anulado y no es capital.
// El PLANO 1 ('operacion') es dinero entre terceros: se muestra, nunca se suma.
// Debe aplicarse en TODA agregación en cliente — replica lo que hacen las
// vistas proyecto_balance / inmueble_balance en SQL.
const computaEnBalance = (m) =>
  !m.esOperacion && m.estado !== "duplicado" && !CATS_NO_PYL.includes(m.categoria);

const movBalance = (movs) => {
  const r2 = (x) => Math.round(x * 100) / 100;
  const cuenta = movs.filter(computaEnBalance);
  const cobrado = cuenta.filter(m => m.estado === "pagado" || m.estado === "parcial");
  const sum = (rows, tipo) => r2(rows.filter(m => m.tipo === tipo).reduce((s, m) => s + m.importe, 0));
  const ingresos = sum(cuenta, "ingreso");
  const gastos = sum(cuenta, "gasto");
  return {
    ingresos, gastos, balance: r2(ingresos - gastos),
    // Caja real: excluye lo devengado y no cobrado (honorarios pendientes).
    balanceCobrado: r2(sum(cobrado, "ingreso") - sum(cobrado, "gasto")),
    ingresosPendientes: r2(cuenta
      .filter(m => m.tipo === "ingreso" && (m.estado === "pendiente" || m.estado === "vencido"))
      .reduce((s, m) => s + m.importe, 0)),
    n: cuenta.length,
    // PLANO 1: se cuenta aparte, nunca dentro del balance.
    operacion: movs.filter(m => m.esOperacion),
  };
};

window.MovimientosTable = MovimientosTable;
window.ContabilidadView = ContabilidadView;
window.movBalance = movBalance;
