// =========================================================================
// CLIENTES
// =========================================================================

const ClientsView = ({ onOpenClient, kind = "clientes" }) => {
  const { Icons, DATA, PageHeader, fmtEUR } = window;
  const ui = window.RevinSupabase?.ui || {};

  // Subset según vista (clientes vs proveedores)
  const PROV_TYPES = new Set(["proveedor"]);
  const CLI_TYPES_VIEW = new Set([
    "comprador","vendedor","inversor","propietario","arrendatario",
    "inmobiliaria","colaborador","arquitecto","gestoria","notaria",
    "consultora","organismo_publico","otro",
  ]);
  const subset = (kind === "proveedores")
    ? DATA.CLIENTS.filter(c => PROV_TYPES.has(c.typeKey))
    : DATA.CLIENTS.filter(c => CLI_TYPES_VIEW.has(c.typeKey) || (!c.typeKey && kind !== "proveedores"));

  // Filtros y orden
  const [search, setSearch] = React.useState("");
  const [tipoFilter, setTipoFilter] = React.useState("all"); // all | comprador | inversor | propietario | ...
  const [estadoFilter, setEstadoFilter] = React.useState("all");
  const [provFilter, setProvFilter] = React.useState("all"); // solo proveedores
  const [responsableFilter, setResponsableFilter] = React.useState("all");
  const [sortBy, setSortBy] = React.useState("alphabetical");

  // ---- Alta de contacto (cliente O proveedor, según la vista `kind`; misma
  // tabla, mismo modal). Mismo patrón que "Nueva tarea": modal + validación +
  // manejo de error visible. Refresco reload-less (recarga window.DATA y
  // re-render local, SIN subir dataVersion) para no expulsar al dashboard. ----
  const esProveedor = kind === "proveedores";
  const isLive = !!(window.RevinData && window.RevinData.isLive);
  const [ver, setVer] = React.useState(0); // fuerza re-lectura de window.DATA
  const [showNew, setShowNew] = React.useState(false);
  const NUEVO_VACIO = { nombre: "", apellidos: "", tipo: "comprador", telefono: "", email: "", zona: "", tipo_proveedor: "" };
  const [nuevo, setNuevo] = React.useState(NUEVO_VACIO);
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState(null);
  const setN = (k) => (e) => setNuevo(n => ({ ...n, [k]: e.target.value }));

  // ---- CHEQUEO DE DUPLICADOS (§5.4 de la spec) ----------------------------
  // `distintos` guarda los contactos sobre los que el usuario ya dijo «es otra
  // persona». Se mandan a crearCliente() como `distinto_de`, que marca las
  // identidades compartidas y registra el par en contacto_no_duplicados.
  const [dups, setDups] = React.useState([]);
  const [chequeando, setChequeando] = React.useState(false);
  const [distintos, setDistintos] = React.useState([]);

  const closeNew = () => {
    setShowNew(false); setNuevo(NUEVO_VACIO); setError(null);
    setDups([]); setDistintos([]);
  };

  // Debounce: 400 ms desde la última tecla. Sin él se dispara una consulta por
  // carácter y el aviso parpadea mientras se escribe el teléfono.
  React.useEffect(() => {
    if (!showNew || !isLive) { setDups([]); return; }
    const tel = nuevo.telefono.trim(), mail = nuevo.email.trim();
    const nom = nuevo.nombre.trim(), ape = nuevo.apellidos.trim();
    if (!tel && !mail && !(nom && ape)) { setDups([]); return; }
    let cancelado = false;
    setChequeando(true);
    const t = setTimeout(async () => {
      try {
        const r = await window.RevinSupabase.buscarDuplicados({
          telefono: tel, email: mail, nombre: nom, apellidos: ape,
        });
        if (!cancelado) setDups(r);
      } catch (e) {
        // Un fallo del chequeo NO puede bloquear el alta: se avisa y se deja pasar.
        console.error("chequeo de duplicados:", e);
        if (!cancelado) setDups([]);
      } finally {
        if (!cancelado) setChequeando(false);
      }
    }, 400);
    return () => { cancelado = true; clearTimeout(t); setChequeando(false); clearTimeout(t); };
  }, [showNew, isLive, nuevo.telefono, nuevo.email, nuevo.nombre, nuevo.apellidos]);

  const pendientes = dups.filter(d => d.confianza !== "blanda" && !distintos.includes(d.cliente_id));
  const blandas    = dups.filter(d => d.confianza === "blanda" && !distintos.includes(d.cliente_id));
  const bloqueado  = pendientes.length > 0;
  const submitNew = async (e) => {
    e.preventDefault();
    if (!nuevo.nombre.trim()) { setError("El nombre es obligatorio."); return; }
    // Apellidos obligatorios en altas manuales. La excepción del setter
    // (identidad_incompleta) es para la ingesta automática, no para este modal:
    // quien da de alta a mano tiene delante a la persona y puede preguntarlo.
    if (!nuevo.apellidos.trim()) { setError("Los apellidos son obligatorios."); return; }
    // Defensa en profundidad: el botón ya está deshabilitado, pero un submit
    // por Enter no pasa por el botón.
    if (bloqueado) { setError("Resuelve antes el aviso de contacto duplicado."); return; }
    setSaving(true); setError(null);
    try {
      await window.RevinSupabase.crearCliente({
        nombre: nuevo.nombre.trim(),
        apellidos: nuevo.apellidos.trim(),
        distinto_de: distintos,
        // En la vista de proveedores el tipo es fijo; en la de clientes lo elige el usuario.
        tipo: esProveedor ? "proveedor" : nuevo.tipo,
        tipo_proveedor: esProveedor ? (nuevo.tipo_proveedor || null) : null,
        telefono: nuevo.telefono.trim() || null,
        email: nuevo.email.trim() || null,
        zona: nuevo.zona.trim() || null,
      });
      const data = await window.RevinSupabase.loadDataFromSupabase();
      window.DATA = data;
      setVer(v => v + 1);
      closeNew();
    } catch (err) {
      // `crearCliente()` ya devuelve el mensaje traducido y ha dejado el
      // técnico en consola (errorAmable). El fallback cubre el caso de que
      // llegue algo que no pasó por ahí.
      setError(err.message || (esProveedor ? "No se ha podido crear el proveedor." : "No se ha podido crear el cliente."));
    } finally {
      setSaving(false);
    }
  };

  // Lista de tipos disponibles en este subset
  const tipos = [...new Set(subset.map(c => c.typeKey).filter(Boolean))];
  const provTipos = [...new Set(subset.map(c => c.supabase?.tipo_proveedor).filter(Boolean))];
  const estados = [...new Set(subset.map(c => c.supabase?.estado).filter(Boolean))];
  const responsables = [...new Set(subset.map(c => c.supabase?.responsable_raw).filter(Boolean))];

  // Aplicar filtros
  let list = subset;
  if (search) {
    const s = search.toLowerCase();
    list = list.filter(c =>
      // displayName cubre nombre + apellidos, así que buscar "Aguilar" o
      // "Carmen Aguilar" encuentra igual. Se deja `name` aparte por si algún
      // día displayName dejara de contenerlo.
      (c.displayName || c.name || "").toLowerCase().includes(s) ||
      c.name.toLowerCase().includes(s) ||
      // El alias entra en el buscador: si el desambiguador sale del nombre,
      // buscar "contenedores" tiene que seguir encontrando a Juanma.
      (c.alias || "").toLowerCase().includes(s) ||
      (c.phone || "").toLowerCase().includes(s) ||
      (c.email || "").toLowerCase().includes(s) ||
      (c.zone || "").toLowerCase().includes(s) ||
      (c.supabase?.dni_cif || "").toLowerCase().includes(s)
    );
  }
  if (tipoFilter !== "all") list = list.filter(c => c.typeKey === tipoFilter);
  if (estadoFilter !== "all") list = list.filter(c => c.supabase?.estado === estadoFilter);
  if (provFilter !== "all") list = list.filter(c => c.supabase?.tipo_proveedor === provFilter);
  if (responsableFilter !== "all") list = list.filter(c => c.supabase?.responsable_raw === responsableFilter);

  // Orden
  list = [...list].sort((a, b) => {
    if (sortBy === "alphabetical") return a.name.localeCompare(b.name, "es");
    if (sortBy === "lastContact") return new Date(b.lastContactRaw || 0) - new Date(a.lastContactRaw || 0);
    if (sortBy === "budget") return b.budgetMax - a.budgetMax;
    if (sortBy === "daysInPipeline") return (b.supabase?.dias_en_pipeline || 0) - (a.supabase?.dias_en_pipeline || 0);
    if (sortBy === "tipo") return (a.typeKey || "").localeCompare(b.typeKey || "");
    return 0;
  });

  const toneByType = (t) => ({
    proveedor: "warning", inversor: "warning", comprador: "info",
    propietario: "brand", vendedor: "neutral", arquitecto: "info",
    notaria: "neutral", gestoria: "neutral", colaborador: "brand",
    inmobiliaria: "brand", organismo_publico: "neutral",
  }[t] || "neutral");

  return (
    <>
      <PageHeader
        eyebrow={`${subset.length} ${kind === "proveedores" ? "proveedores" : "contactos"} · mostrando ${list.length}`}
        title={kind === "proveedores" ? "PROVEEDORES" : "CLIENTES / LEADS"}
        sub={kind === "proveedores"
          ? "Gremios y especialistas. Filtra por especialidad y zona de actuación."
          : "Compradores, vendedores, inversores, propietarios y colaboradores. Buscador + filtros multi-eje."
        }
        actions={<>
          <button className="btn secondary"><Icons.Download/> Exportar CSV</button>
          <button className="btn primary" onClick={() => setShowNew(true)}><Icons.Plus/> {esProveedor ? "Nuevo proveedor" : "Nuevo cliente"}</button>
        </>}
      />

      {/* Búsqueda + Filtros */}
      <div style={{display: "flex", gap: 8, marginBottom: 12, flexWrap: "wrap", alignItems: "center"}}>
        <div className="tb-search" style={{width: 340, padding: "7px 12px"}}>
          <Icons.Search/>
          <input
            value={search}
            onChange={e => setSearch(e.target.value)}
            placeholder="Nombre, teléfono, email, DNI, zona…"
            style={{border: "none", background: "transparent", outline: "none", fontFamily: "inherit", fontSize: 13, color: "var(--fg-1)", flex: 1}}
          />
        </div>

        {kind === "proveedores" ? (
          <>
            <select className="select" style={{width: "auto", padding: "6px 26px 6px 10px"}} value={provFilter} onChange={e => setProvFilter(e.target.value)}>
              <option value="all">Todas las especialidades</option>
              {provTipos.map(t => (
                <option key={t} value={t}>{ui.provType ? ui.provType(t) : t}</option>
              ))}
            </select>
          </>
        ) : (
          <>
            <select className="select" style={{width: "auto", padding: "6px 26px 6px 10px"}} value={tipoFilter} onChange={e => setTipoFilter(e.target.value)}>
              <option value="all">Todos los tipos</option>
              {tipos.map(t => (
                <option key={t} value={t}>{ui.clienteType ? ui.clienteType(t) : t}</option>
              ))}
            </select>
            <select className="select" style={{width: "auto", padding: "6px 26px 6px 10px"}} value={estadoFilter} onChange={e => setEstadoFilter(e.target.value)}>
              <option value="all">Todos los estados</option>
              {estados.map(s => (
                <option key={s} value={s}>{ui.estado ? ui.estado(s) : s}</option>
              ))}
            </select>
          </>
        )}

        <select className="select" style={{width: "auto", padding: "6px 26px 6px 10px"}} value={responsableFilter} onChange={e => setResponsableFilter(e.target.value)}>
          <option value="all">Cualquier responsable</option>
          {responsables.map(r => (
            <option key={r} value={r}>{r}</option>
          ))}
        </select>

        <div style={{marginLeft: "auto", display: "flex", gap: 8, alignItems: "center"}}>
          <span style={{fontSize: 11, color: "var(--fg-3)", textTransform: "uppercase", letterSpacing: ".08em"}}>Orden:</span>
          <select className="select" style={{width: "auto", padding: "6px 26px 6px 10px"}} value={sortBy} onChange={e => setSortBy(e.target.value)}>
            <option value="alphabetical">A–Z (alfabético)</option>
            <option value="lastContact">Último contacto</option>
            <option value="daysInPipeline">Días en pipeline</option>
            {kind !== "proveedores" && <option value="budget">Presupuesto máximo</option>}
            <option value="tipo">Tipo</option>
          </select>
        </div>
      </div>

      {/* Tabla */}
      <div className="tbl-wrap">
        <table className="tbl">
          <thead>
            <tr>
              <th></th>
              <th>Nombre</th>
              <th>Tipo</th>
              {kind === "proveedores" && <th>Especialidad</th>}
              {kind !== "proveedores" && <th>Estado</th>}
              <th>Teléfono</th>
              <th>Email</th>
              {kind !== "proveedores" && <th className="num">Presupuesto</th>}
              <th>Zona</th>
              <th>Responsable</th>
              <th>Último contacto</th>
            </tr>
          </thead>
          <tbody>
            {list.map(c => (
              <tr key={c.id} onClick={() => onOpenClient(c.id)} className={c.cooling ? "cooling" : ""}>
                <td style={{width: 44}}>
                  <div className="sb-avatar" style={{
                    width: 32, height: 32, fontSize: 11,
                    background: c.cooling ? "var(--revin-red)" : (c.typeKey === "proveedor" ? "var(--warning)" : "var(--neutral-700)"),
                  }}>
                    {(c.displayName || c.name).split(" ").map(p => p[0]).slice(0,2).join("").toUpperCase()}
                  </div>
                </td>
                <td>
                  <div style={{fontWeight: 600}}>
                    {c.displayName || c.name}
                    {c.alias && (
                      <span style={{fontWeight: 400, color: "var(--fg-3)"}}> · {c.alias}</span>
                    )}
                  </div>
                  {c.supabase?.dni_cif && <div style={{fontSize: 10, color: "var(--fg-3)", fontFamily: "var(--font-mono)"}}>{c.supabase.dni_cif}</div>}
                </td>
                <td><Pill tone={toneByType(c.typeKey)}>{ui.clienteType ? ui.clienteType(c.typeKey) : c.type}</Pill></td>
                {kind === "proveedores" && (
                  <td>
                    {c.supabase?.tipo_proveedor
                      ? <Pill tone="warning">{ui.provType ? ui.provType(c.supabase.tipo_proveedor) : c.supabase.tipo_proveedor}</Pill>
                      : <span style={{color: "var(--fg-4)"}}>—</span>}
                  </td>
                )}
                {kind !== "proveedores" && (
                  <td>
                    {c.supabase?.estado ? (
                      <Pill tone={c.cooling ? "danger" : c.supabase.estado === "cierre" ? "success" : "info"}>
                        {ui.estado ? ui.estado(c.supabase.estado) : c.supabase.estado}
                      </Pill>
                    ) : <span style={{color: "var(--fg-4)"}}>—</span>}
                  </td>
                )}
                <td style={{fontFamily: "var(--font-mono)", fontSize: 11}}>{c.phone || <span style={{color: "var(--fg-4)"}}>—</span>}</td>
                <td style={{fontFamily: "var(--font-mono)", fontSize: 11}}>{c.email || <span style={{color: "var(--fg-4)"}}>—</span>}</td>
                {kind !== "proveedores" && (
                  <td className="num">
                    {c.budgetMax > 0
                      ? <><b>{fmtEUR(c.budgetMax)}</b>{c.budget > 0 && c.budget !== c.budgetMax && <div style={{fontSize: 10, color: "var(--fg-3)"}}>desde {fmtEUR(c.budget)}</div>}</>
                      : <span style={{color: "var(--fg-4)"}}>—</span>}
                  </td>
                )}
                <td style={{fontSize: 12}}>{c.zone || <span style={{color: "var(--fg-4)"}}>—</span>}</td>
                <td style={{fontSize: 12}}>
                  {c.supabase?.responsable_raw
                    ? c.supabase.responsable_raw.split(",")[0].trim()
                    : <span style={{color: "var(--fg-4)"}}>—</span>}
                </td>
                <td style={{fontSize: 12, color: c.cooling ? "var(--revin-red)" : "var(--fg-2)"}}>
                  {c.lastContact !== "—" ? c.lastContact : <span style={{color: "var(--fg-4)"}}>—</span>}
                </td>
              </tr>
            ))}
            {list.length === 0 && (
              <tr><td colSpan={kind === "proveedores" ? 9 : 10}>
                <div className="empty">
                  <div className="icon-wrap"><Icons.Users/></div>
                  {/* Distinguir "no encuentro nada con estos filtros" de "no tienes
                      nada": para una comercial nueva, sin cartera asignada, el consejo
                      de ajustar los filtros manda a toquetear unos filtros que no ha
                      puesto, y deja creyendo que la lista está mal. */}
                  {subset.length === 0
                    ? "Todavía no tienes clientes asignados. Cuando te asignen alguno, aparecerá aquí."
                    : "Sin resultados. Ajusta los filtros o limpia la búsqueda."}
                </div>
              </td></tr>
            )}
          </tbody>
        </table>
      </div>

      {showNew && (
        <div className="sb-login-back" onClick={closeNew}>
          <form className="sb-login" onClick={(e) => e.stopPropagation()} onSubmit={submitNew}>
            <div className="sb-login-h">
              <h2 className="sb-login-title" style={{margin: 0}}>{esProveedor ? "Nuevo proveedor" : "Nuevo cliente"}</h2>
            </div>
            <p className="sb-login-sub">
              {esProveedor
                ? "Se añade como proveedor (gremio / especialista) en Supabase."
                : "Se añade a la base de contactos en Supabase."}
            </p>

            <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12}}>
              <div className="field">
                <label className="field-lbl">Nombre *</label>
                <input className="input" value={nuevo.nombre} onChange={setN("nombre")} placeholder={esProveedor ? "Ej: Fontanería Martos" : "Ej: Carmen"} autoFocus/>
              </div>
              <div className="field">
                <label className="field-lbl">Apellidos *</label>
                <input className="input" value={nuevo.apellidos} onChange={setN("apellidos")} placeholder={esProveedor ? "Ej: SL (razón social)" : "Ej: Aguilar Ruiz"}/>
              </div>
            </div>
            <div className="field">
              <label className="field-lbl">{esProveedor ? "Especialidad" : "Tipo"}</label>
              {esProveedor ? (
                <select className="input" value={nuevo.tipo_proveedor} onChange={setN("tipo_proveedor")}>
                  <option value="">— Sin especificar —</option>
                  <option value="fontaneria">Fontanería</option>
                  <option value="electricidad">Electricidad</option>
                  <option value="albañileria">Albañilería</option>
                  <option value="carpinteria">Carpintería</option>
                  <option value="cocinas">Cocinas</option>
                  <option value="pintura">Pintura</option>
                  <option value="multigremio">Multigremio</option>
                  <option value="copisteria">Copistería</option>
                  <option value="otro">Otro</option>
                </select>
              ) : (
                <select className="input" value={nuevo.tipo} onChange={setN("tipo")}>
                  <option value="comprador">Comprador</option>
                  <option value="vendedor">Vendedor</option>
                  <option value="inversor">Inversor</option>
                  <option value="propietario">Propietario</option>
                  <option value="arrendatario">Arrendatario</option>
                  <option value="colaborador">Colaborador</option>
                  <option value="otro">Otro</option>
                </select>
              )}
            </div>
            <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12}}>
              <div className="field">
                <label className="field-lbl">Teléfono (opcional)</label>
                <input className="input" value={nuevo.telefono} onChange={setN("telefono")} placeholder="6XX XXX XXX"/>
              </div>
              <div className="field">
                <label className="field-lbl">Zona (opcional)</label>
                <input className="input" value={nuevo.zona} onChange={setN("zona")} placeholder="Jaén capital, Linares…"/>
              </div>
            </div>
            <div className="field">
              <label className="field-lbl">Email (opcional)</label>
              <input className="input" type="email" value={nuevo.email} onChange={setN("email")} placeholder="nombre@email.com"/>
            </div>

            {/* ---- AVISO DE DUPLICADO --------------------------------------
                MEDIA (teléfono/email) BLOQUEA y lo resuelve una persona: de las
                6 colisiones reales de esta base, 2 eran personas distintas
                compartiendo móvil. DURA (DNI validado) bloquea igual pero es
                casi seguro la misma persona. BLANDA (nombre) solo avisa: hay
                cuatro «Juanma» y varios «José». */}
            {chequeando && dups.length === 0 && (
              <div style={{fontSize: 11, color: "var(--fg-3)", marginBottom: 8}}>Comprobando duplicados…</div>
            )}

            {pendientes.length > 0 && (
              <div style={{
                border: "1px solid var(--border-2)", borderLeft: "3px solid var(--color-pendiente)",
                background: "var(--bg-inset)", padding: "12px 14px", marginBottom: 12, borderRadius: 2,
              }}>
                <div style={{fontSize: 11, fontWeight: 700, letterSpacing: ".06em",
                             textTransform: "uppercase", color: "var(--color-pendiente)", marginBottom: 8}}>
                  {pendientes.length === 1 ? "Ya existe un contacto con estos datos" : `Ya existen ${pendientes.length} contactos con estos datos`}
                </div>
                {pendientes.map(d => (
                  <div key={d.cliente_id + d.tipo} style={{marginBottom: 10}}>
                    <div style={{fontSize: 13, fontWeight: 600}}>
                      {d.nombre_completo || "(sin nombre)"}
                      {d.alias && <span style={{fontWeight: 400, color: "var(--fg-3)"}}> · {d.alias}</span>}
                    </div>
                    <div style={{fontSize: 11, color: "var(--fg-2)", fontFamily: "var(--font-mono)", marginTop: 2}}>
                      {d.tipo === "telefono" ? "Mismo teléfono" : d.tipo === "email" ? "Mismo email" : "Mismo DNI"}: {d.valor}
                    </div>
                    <div style={{display: "flex", gap: 6, marginTop: 6, flexWrap: "wrap"}}>
                      <button type="button" className="btn secondary xs"
                              onClick={() => { closeNew(); onOpenClient(d.cliente_id); }}>
                        Es la misma persona · abrir su ficha
                      </button>
                      <button type="button" className="btn ghost xs"
                              onClick={() => setDistintos(x => [...x, d.cliente_id])}>
                        Es otra persona {d.tipo === "email" ? "(comparten email)" : d.tipo === "telefono" ? "(comparten teléfono)" : ""}
                      </button>
                    </div>
                  </div>
                ))}
              </div>
            )}

            {distintos.length > 0 && (
              <div style={{fontSize: 11, color: "var(--fg-3)", marginBottom: 8}}>
                {distintos.length === 1 ? "1 contacto marcado" : `${distintos.length} contactos marcados`} como persona distinta.
                Al guardar, el dato compartido quedará registrado como legítimo y el aviso no volverá.
              </div>
            )}

            {blandas.length > 0 && (
              <div style={{fontSize: 12, color: "var(--fg-2)", marginBottom: 8}}>
                Hay {blandas.length === 1 ? "un contacto" : `${blandas.length} contactos`} con un nombre parecido
                ({blandas.slice(0, 3).map(b => b.nombre_completo).join(", ")}
                {blandas.length > 3 ? "…" : ""}). Puedes guardar igualmente: el nombre nunca basta para decidir.
              </div>
            )}

            {!isLive && (
              <div style={{fontSize: 12, color: "var(--warning)", marginBottom: 8}}>
                Conéctate a Supabase para guardar {esProveedor ? "el proveedor" : "el cliente"} (ahora estás en datos de prueba).
              </div>
            )}
            {error && <div style={{fontSize: 12, color: "var(--revin-red)", marginBottom: 8}}>{error}</div>}

            <div style={{display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4}}>
              <button type="button" className="btn secondary" onClick={closeNew}>Cancelar</button>
              <button type="submit" className="btn primary" disabled={saving || !isLive || bloqueado}>
                {saving ? "Guardando…" : (esProveedor ? "Crear proveedor" : "Crear cliente")}
              </button>
            </div>
          </form>
        </div>
      )}
    </>
  );
};

window.ClientsView = ClientsView;

// =========================================================================
// AGENDA / CALENDARIO
// =========================================================================

const AgendaView = () => {
  const { Icons, PageHeader, Card, DATA } = window;

  // ---- Alta de evento. Persiste como TAREA con fecha (no hay tabla de eventos;
  // la agenda es hoy mock). org_id lo inyecta crearTarea vía conOrg. Como el
  // calendario no lee datos reales, confirmamos en el propio modal y dejamos
  // constancia de que el evento vive en el módulo Tareas. ----
  const isLive = !!(window.RevinData && window.RevinData.isLive);
  const inmuebles = DATA?.INMUEBLES || [];
  const [showNew, setShowNew] = React.useState(false);
  const [ev, setEv] = React.useState({ titulo: "", inmuebleId: "", fecha: "" });
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [okMsg, setOkMsg] = React.useState(null);
  const setE = (k) => (e) => setEv(s => ({ ...s, [k]: e.target.value }));
  const closeNew = () => { setShowNew(false); setEv({ titulo: "", inmuebleId: "", fecha: "" }); setError(null); setOkMsg(null); };
  const submitNew = async (e) => {
    e.preventDefault();
    if (!ev.titulo.trim()) { setError("El título es obligatorio."); return; }
    if (!ev.fecha) { setError("La fecha y hora son obligatorias."); return; }
    setSaving(true); setError(null); setOkMsg(null);
    try {
      await window.RevinSupabase.crearTarea({
        titulo: ev.titulo.trim(),
        inmueble_id: ev.inmuebleId || null,
        fecha_vencimiento: new Date(ev.fecha).toISOString(),
      });
      // Recarga datos (el evento aparece en Tareas). No subimos dataVersion.
      const data = await window.RevinSupabase.loadDataFromSupabase();
      window.DATA = data;
      setOkMsg(`✓ Evento "${ev.titulo.trim()}" creado. Aparece en el módulo Tareas.`);
      setEv({ titulo: "", inmuebleId: "", fecha: "" });
    } catch (err) {
      console.error(err);
      setError(err.message || "No se pudo crear el evento.");
    } finally {
      setSaving(false);
    }
  };

  const days = [
    { lbl: "Lun", num: 17 }, { lbl: "Mar", num: 18, today: true }, { lbl: "Mié", num: 19 },
    { lbl: "Jue", num: 20 }, { lbl: "Vie", num: 21 }, { lbl: "Sáb", num: 22 }, { lbl: "Dom", num: 23 },
  ];
  const hours = ["09:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00"];
  const events = [
    { day: 1, hour: 0, dur: 1, title: "Llamada · Carmen Aguilar", tone: "info", time: "09:00 – 09:30" },
    { day: 1, hour: 1, dur: 1, title: "Visita · Andalucía 12", tone: "", time: "10:30 – 11:30" },
    { day: 1, hour: 3, dur: 1, title: "Firma reserva · Bernabé Soriano 5", tone: "success", time: "12:00 – 13:00" },
    { day: 1, hour: 7, dur: 1, title: "Visita · Plaza San Francisco 6", tone: "", time: "16:00 – 17:00" },
    { day: 1, hour: 9, dur: 1, title: "Captación · Núñez 14", tone: "warn", time: "19:00 – 20:00" },
    { day: 0, hour: 2, dur: 1, title: "Llamada Diego Ojeda", tone: "info", time: "11:00 – 11:30" },
    { day: 2, hour: 1, dur: 1, title: "Reunión equipo (Marta)", tone: "warn", time: "10:00 – 11:00" },
    { day: 2, hour: 4, dur: 1, title: "Visita Cervantes 8", tone: "", time: "13:00 – 14:00" },
    { day: 3, hour: 2, dur: 1, title: "Notario · firma Bernabé", tone: "success", time: "11:00 – 13:00" },
    { day: 4, hour: 6, dur: 1, title: "Visita · Real 24", tone: "", time: "15:00 – 16:00" },
    { day: 5, hour: 1, dur: 1, title: "Visita 2ª · Real 24 (Carmen + marido)", tone: "info", time: "10:00 – 11:30" },
  ];

  return (
    <>
      <PageHeader
        eyebrow="Esta semana · 17–23 mar"
        title="AGENDA"
        sub="Visitas, llamadas, captaciones y citas con notaría. Sincronizada con Google Calendar."
        actions={<>
          <div className="view-tabs">
            <button className="active">Semana</button>
            <button>Mes</button>
            <button>Día</button>
          </div>
          <button className="btn primary" onClick={() => setShowNew(true)}><Icons.Plus/> Nuevo evento</button>
        </>}
      />
      <window.DemoBlockMark bloque="agenda-semana">
        Agenda de ejemplo. Los eventos no son reales.
      </window.DemoBlockMark>
      <div className="cal-grid">
        <div className="cal-header">
          <div className="cal-h-cell">SEM 12</div>
          {days.map((d, i) => (
            <div key={i} className={"cal-h-cell" + (d.today ? " today" : "")}>
              {d.lbl}<div className="day-num">{d.num}</div>
            </div>
          ))}
        </div>
        <div className="cal-body">
          {hours.map((h, hi) => (
            <React.Fragment key={hi}>
              <div className="cal-time">{h}</div>
              {days.map((d, di) => (
                <div className={"cal-cell" + (d.today ? " today" : "")} key={di}>
                  {events.filter(e => e.day === di && e.hour === hi).map((e, k) => (
                    <div className={"cal-event " + (e.tone || "")} key={k}>
                      <div className="ev-time">{e.time}</div>
                      <div className="ev-title">{e.title}</div>
                    </div>
                  ))}
                </div>
              ))}
            </React.Fragment>
          ))}
        </div>
      </div>

      {showNew && (
        <div className="sb-login-back" onClick={closeNew}>
          <form className="sb-login" onClick={(e) => e.stopPropagation()} onSubmit={submitNew}>
            <div className="sb-login-h">
              <h2 className="sb-login-title" style={{margin: 0}}>Nuevo evento</h2>
            </div>
            <p className="sb-login-sub">Se guarda como tarea con fecha en Supabase y aparece en el módulo Tareas (la agenda visual aún no está conectada a datos reales).</p>

            <div className="field">
              <label className="field-lbl">Título *</label>
              <input className="input" value={ev.titulo} onChange={setE("titulo")} placeholder="Ej: Visita · Bernabé Soriano 5" autoFocus/>
            </div>
            <div className="field">
              <label className="field-lbl">Inmueble (opcional)</label>
              <select className="input" value={ev.inmuebleId} onChange={setE("inmuebleId")}>
                <option value="">— Sin inmueble —</option>
                {inmuebles.map(i => (
                  <option key={i._uid || i.id} value={i._uid || ""}>{(i.ref ? i.ref + " · " : "") + (i.addr || "")}</option>
                ))}
              </select>
            </div>
            <div className="field">
              <label className="field-lbl">Fecha y hora *</label>
              <input className="input" type="datetime-local" value={ev.fecha} onChange={setE("fecha")}/>
            </div>

            {!isLive && (
              <div style={{fontSize: 12, color: "var(--warning)", marginBottom: 8}}>
                Conéctate a Supabase para guardar el evento (ahora estás en datos de prueba).
              </div>
            )}
            {error && <div style={{fontSize: 12, color: "var(--revin-red)", marginBottom: 8}}>{error}</div>}
            {okMsg && <div style={{fontSize: 12, color: "var(--success)", marginBottom: 8}}>{okMsg}</div>}

            <div style={{display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4}}>
              <button type="button" className="btn secondary" onClick={closeNew}>{okMsg ? "Cerrar" : "Cancelar"}</button>
              <button type="submit" className="btn primary" disabled={saving || !isLive}>
                {saving ? "Guardando…" : "Crear evento"}
              </button>
            </div>
          </form>
        </div>
      )}
    </>
  );
};

// =========================================================================
// TAREAS
// =========================================================================

const TasksView = () => {
  const { Icons, DATA, PageHeader, Card } = window;
  const tasks = DATA?.TASKS || [];
  const inmuebles = DATA?.INMUEBLES || [];
  const isLive = !!(window.RevinData && window.RevinData.isLive);

  const [showNew, setShowNew] = React.useState(false);
  const [titulo, setTitulo] = React.useState("");
  const [inmuebleId, setInmuebleId] = React.useState("");
  const [fecha, setFecha] = React.useState("");
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState(null);

  const closeModal = () => { setShowNew(false); setTitulo(""); setInmuebleId(""); setFecha(""); setError(null); };

  const submit = async (e) => {
    e.preventDefault();
    if (!titulo.trim()) { setError("El título es obligatorio."); return; }
    setSaving(true); setError(null);
    try {
      await window.RevinSupabase.crearTarea({
        titulo: titulo.trim(),
        inmueble_id: inmuebleId || null,
        fecha_vencimiento: fecha ? new Date(fecha).toISOString() : null,
      });
      await window.RevinData.reload();
      closeModal();
    } catch (err) {
      console.error(err);
      setError(err.message || "No se pudo crear la tarea.");
    } finally {
      setSaving(false);
    }
  };

  const pendientes = tasks.filter(t => !t.done);
  const completadas = tasks.filter(t => t.done);

  return (
    <>
      <PageHeader
        eyebrow={`${pendientes.length} pendientes · ${pendientes.filter(t => t.urgent).length} urgentes`}
        title="TAREAS"
        sub="Generadas automáticamente desde el Playbook + las que añadas manualmente."
        actions={<>
          <button className="btn secondary"><Icons.Filter/> Por inmueble</button>
          <button className="btn primary" onClick={() => setShowNew(true)}><Icons.Plus/> Nueva tarea</button>
        </>}
      />
      <div className="fbar">
        <button className="fchip active">Hoy · {pendientes.filter(t => (t.due || "").startsWith("Hoy")).length}</button>
        <button className="fchip">Esta semana</button>
        <button className="fchip">Vencidas</button>
        <button className="fchip"><Icons.Check/> Completadas · {completadas.length}</button>
      </div>

      <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20}}>
        <Card title={`Pendientes · ${pendientes.length}`} b={false}>
          {pendientes.length === 0 && (
            <div className="empty" style={{padding: 16}}><span style={{fontSize: 12}}>No hay tareas pendientes.</span></div>
          )}
          {pendientes.map(t => (
            <div className="task" key={t.id}>
              <div className="chk"/>
              <div className="task-body">
                <div className="task-title">{t.title}</div>
                <div className="task-meta">
                  <span className="code" style={{textTransform: "uppercase", fontFamily: "var(--font-mono)"}}>{t.prop}</span>
                  <Pill tone="neutral">{t.phase}</Pill>
                  <span className={"due" + (t.urgent ? " urgent" : "")}><Icons.Clock/> {t.due}</span>
                </div>
              </div>
              <button className="btn xs ghost"><Icons.MoreH/></button>
            </div>
          ))}
        </Card>
        <Card title={`Completadas · ${completadas.length}`} b={false}>
          {completadas.length === 0 && (
            <div className="empty" style={{padding: 16}}><span style={{fontSize: 12}}>Aún no hay tareas completadas.</span></div>
          )}
          {completadas.map(t => (
            <div className="task done" key={t.id}>
              <div className="chk done"/>
              <div className="task-body">
                <div className="task-title">{t.title}</div>
                <div className="task-meta">
                  <span className="code">{t.prop}</span>
                  <span>{t.due}</span>
                </div>
              </div>
            </div>
          ))}
        </Card>
      </div>

      {showNew && (
        <div className="sb-login-back" onClick={closeModal}>
          <form className="sb-login" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
            <div className="sb-login-h">
              <h2 className="sb-login-title" style={{margin: 0}}>Nueva tarea</h2>
            </div>
            <p className="sb-login-sub">Se añade al listado de tareas del equipo en Supabase.</p>

            <div className="field">
              <label className="field-lbl">Título</label>
              <input className="input" value={titulo} onChange={(e) => setTitulo(e.target.value)} placeholder="Ej: Recibir presupuesto cocina" autoFocus/>
            </div>
            <div className="field">
              <label className="field-lbl">Inmueble (opcional)</label>
              <select className="input" value={inmuebleId} onChange={(e) => setInmuebleId(e.target.value)}>
                <option value="">— Sin inmueble —</option>
                {inmuebles.map(i => (
                  <option key={i._uid || i.id} value={i._uid || ""}>{(i.ref ? i.ref + " · " : "") + (i.addr || "")}</option>
                ))}
              </select>
            </div>
            <div className="field">
              <label className="field-lbl">Vencimiento (opcional)</label>
              <input className="input" type="datetime-local" value={fecha} onChange={(e) => setFecha(e.target.value)}/>
            </div>

            {!isLive && (
              <div style={{fontSize: 12, color: "var(--warning)", marginBottom: 8}}>
                Conéctate a Supabase para guardar la tarea (ahora estás en datos de prueba).
              </div>
            )}
            {error && <div style={{fontSize: 12, color: "var(--revin-red)", marginBottom: 8}}>{error}</div>}

            <div style={{display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 4}}>
              <button type="button" className="btn secondary" onClick={closeModal}>Cancelar</button>
              <button type="submit" className="btn primary" disabled={saving || !isLive}>
                {saving ? "Guardando…" : "Crear tarea"}
              </button>
            </div>
          </form>
        </div>
      )}
    </>
  );
};

// =========================================================================
// DOCUMENTS REPOSITORY
// =========================================================================

const DocumentsView = () => {
  const { Icons, DATA, PageHeader, Card } = window;
  // ⚠️ Aquí se concatenaban 4 PDFs INVENTADOS en el propio código (Cervantes 8,
  // Real 24, Bernabé 5, Andalucía 12 — inmuebles que no existen). La pantalla
  // mostraba "4 documentos" aunque la tabla estuviera vacía, aislada o rota, así
  // que no servía para verificar nada. Eliminados el 03/08 al conectar Storage:
  // desde ahora esta lista es SOLO lo que hay de verdad en `documentos`.
  const all = Object.values(DATA.DOCS_BY_PROPERTY || {}).flat()
    .concat(Object.values(DATA.DOCS_BY_CLIENT || {}).flat());
  const [subiendo, setSubiendo] = React.useState(false);
  const [, setRefresh] = React.useState(0);
  const [errDoc, setErrDoc] = React.useState("");
  const descargar = async (d) => {
    setErrDoc("");
    try { await window.RevinSupabase.descargarDocumento(d); }
    catch (e) { setErrDoc((e && e.message) || "No se ha podido abrir el documento."); }
  };
  const trasSubir = async () => {
    try {
      window.DATA = await window.RevinSupabase.loadDataFromSupabase();
      setRefresh(r => r + 1);
    } catch (e) { console.error(e); }
  };

  return (
    <>
      <PageHeader
        eyebrow={`${all.length} documentos · Supabase Storage`}
        title="DOCUMENTOS"
        sub="Repositorio global. Cada documento sigue nomenclatura REVIN y enlaza al inmueble correspondiente."
        actions={<>
          <button className="btn secondary"><Icons.Sparkles/> Generar nuevo contrato</button>
          <button className="btn primary" onClick={() => setSubiendo(true)}><Icons.Upload/> Subir documento</button>
        </>}
      />
      {subiendo && window.SubirDocumentoModal && (
        <window.SubirDocumentoModal onClose={() => setSubiendo(false)} onSubido={trasSubir}/>
      )}
      <div className="fbar">
        <button className="fchip active">Todos · {all.length}</button>
        <button className="fchip">Notas simples</button>
        <button className="fchip">Contratos</button>
        <button className="fchip">Reservas / Arras</button>
        <button className="fchip">Facturas</button>
        <div className="fbar-right">
          <div className="tb-search" style={{width: 280}}>
            <Icons.Search/>
            <span className="ph">Buscar por nombre, inmueble, fecha…</span>
          </div>
        </div>
      </div>

      <Card title="Plantillas auto-generables" right={<Pill tone="brand">PDF</Pill>} b={false} style={{marginBottom: 20}}>
        <div style={{display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12, padding: 16}}>
          {["Hoja de encargo", "Contrato de reserva", "Contrato de arras", "Factura comercial"].map((t, i) => (
            <button key={i} className="prop-card" style={{padding: 14, cursor: "pointer", textAlign: "left", border: "1px solid var(--border-1)"}}>
              <Icons.Sparkles size={18}/>
              <div style={{fontSize: 13, fontWeight: 700, marginTop: 8}}>{t}</div>
              <div style={{fontSize: 11, color: "var(--fg-3)", marginTop: 2}}>Genera desde datos del inmueble</div>
            </button>
          ))}
        </div>
      </Card>

      <Card title={`Todos los documentos · ${all.length}`} b={false}>
        {errDoc && (
          <div className="sb-login-err" style={{ margin: "10px 16px" }}><span>{errDoc}</span></div>
        )}
        <table className="tbl">
          <thead><tr><th></th><th>Nombre</th><th>Inmueble</th><th>Tipo</th><th className="num">Tamaño</th><th>Fecha</th><th></th></tr></thead>
          <tbody>
            {all.slice(0, 12).map((d, i) => (
              <tr key={i}>
                <td style={{width: 40}}><div className="ico-doc pdf" style={{width: 24, height: 30}}>{d.kind}</div></td>
                <td><span className="doc-code">{d.name}</span></td>
                <td>{d.prop || "—"}</td>
                <td><Pill tone="neutral">{d.kind}</Pill></td>
                <td className="num">{d.size}</td>
                <td>{d.date}</td>
                <td><button className="btn xs ghost" title="Descargar" onClick={() => descargar(d)}><Icons.Download/></button></td>
              </tr>
            ))}
          </tbody>
        </table>
      </Card>
    </>
  );
};

// =========================================================================
// COMISIONES / FACTURACIÓN
// =========================================================================

// ÚNICA FUENTE DE VERDAD de los totales de comisiones al equipo (TIPO 2).
// La consumen FinanceView ("Facturación global") y el KPI "Comisiones al equipo"
// del dashboard admin: si hay que cambiar el cálculo, se cambia AQUÍ y solo aquí.
//
// FUENTE: `DATA.COMMISSIONS`, que el loader construye desde la vista
// `proyecto_comisiones_equipo` (no ya desde la tabla `comisiones`, deprecada).
//
// REGLA CLAVE: una comisión PREVISTA no es un gasto. `total` cuenta solo las
// MATERIALIZADAS (las que tienen movimiento contable real); las previsiones van
// aparte en `previsto`. Mezclarlas inflaría el coste del equipo con dinero que
// todavía no ha salido. Es el escalón que va por debajo de devengo/caja.
const RevinComisiones = {
  // Todas las comisiones de equipo: materializadas + previsiones.
  all: () => (window.DATA?.COMMISSIONS || []),

  // Parte de una persona (por iniciales) en una comisión: captador + comercializador.
  parteDe: (c, iniciales) =>
    (c.comercializador === iniciales ? (c.com || 0) : 0) +
    (c.captador === iniciales ? (c.capt || 0) : 0),

  // Totales. Sin argumento → global del equipo (SIN filtrar por persona).
  // Con `iniciales` → solo la parte de esa persona.
  totales(iniciales) {
    // Con `iniciales`, los CONTADORES también son suyos. Antes los euros se
    // filtraban y los recuentos no, así que la pantalla se contradecía sola:
    // "Mi comisión total 0 €" encima de "Operaciones con comisión: 14", que
    // eran las del equipo entero. Un cero y un catorce sobre el mismo hecho.
    const comms = iniciales
      ? this.all().filter(c => c.comercializador === iniciales || c.captador === iniciales)
      : this.all();
    const valOf = iniciales ? (c) => this.parteDe(c, iniciales) : (c) => (c.total || 0);
    // Gasto REAL vs previsión. `materializada` viene de la vista del motor.
    const materializadas = comms.filter(c => c.materializada);
    const previstas = comms.filter(c => !c.materializada);
    const suma = (arr) => arr.reduce((s, c) => s + valOf(c), 0);
    return {
      // Coste real del equipo: solo lo que tiene movimiento contable detrás.
      total: suma(materializadas),
      previsto: suma(previstas),
      // Alias en el vocabulario que ya usa FinanceView.
      cobrado: suma(materializadas),
      pendiente: suma(previstas),
      nCierres: comms.length,
      nMaterializadas: materializadas.length,
      nCobradas: materializadas.length,
      nPendientes: previstas.length,
      nPrevistas: previstas.length,
    };
  },
};

const FinanceView = ({ isAdmin }) => {
  const { Icons, DATA, PageHeader, Card, KPI, Pill, fmtEUR } = window;
  // Usuario actual (bug #11): sumar la parte del logueado, no del mock "EM".
  const me = DATA.USER?.initials || "??";
  // La tabla enseñaba TODAS las comisiones del equipo bajo el título "Mis
  // comisiones": cada fila con la operación, la base de honorarios que cobró
  // REVIN y lo que se llevó cada compañera, con nombre. Los KPIs sí estaban
  // filtrados (`totales(isAdmin ? null : me)`), así que la pantalla llegaba a
  // contradecirse sola: "Mi comisión total 0 €" encima de una tabla con las de
  // los demás.
  //
  // Se filtra en el front ADEMÁS de en RLS. Hoy la base ya solo le devuelve las
  // suyas, pero un front que depende de eso para no enseñar de más se rompe en
  // silencio el día que alguien afloje una política.
  const todas = RevinComisiones.all();
  // Ojo: `parteDe` devuelve 0 cuando la comisión no es tuya, nunca null, así
  // que un `!= null` dejaría pasar la lista entera. Lo que decide es si tu
  // nombre está en alguno de los dos papeles.
  const esMia = (c) => c.comercializador === me || c.captador === me;
  const comms = isAdmin ? todas : todas.filter(esMia);
  const myPart = (c) => RevinComisiones.parteDe(c, me);

  // Totales REALES desde la tabla comisiones (helper compartido).
  // Admin ve el global del equipo; comercial ve solo su parte.
  const t = RevinComisiones.totales(isAdmin ? null : me);
  const facturado = t.total;
  const cobrado = t.cobrado;
  const pendiente = t.pendiente;
  const nCierres = t.nCierres;

  const _eur0 = (n) => fmtEUR(n).replace(" €", "");

  return (
    <>
      <PageHeader
        eyebrow={isAdmin ? "Comisiones y facturación del equipo" : "Histórico de comisiones"}
        title={isAdmin ? "FACTURACIÓN GLOBAL" : "MIS COMISIONES"}
        sub={isAdmin
          ? "Comisiones reales por cierre. Splits captador/comercializador por inmueble."
          : "Detalle de captador vs comercializador por inmueble. Splits configurados por dirección."}
        actions={<button className="btn secondary"><Icons.Download/> Descargar PDF</button>}
      />
      <div className="kpi-row">
        {/* Las etiquetas dependen de QUIÉN mira: para el admin esto es un COSTE
            que REVIN paga; para el comercial es dinero que él cobra. Mismo dato,
            dirección opuesta — "Cobrado" a secas sería falso en la vista admin. */}
        <KPI accent label={isAdmin ? "Comisiones al equipo" : "Mi comisión total"} value={_eur0(facturado)} unit="€" sub={`${t.nMaterializadas} operaci${t.nMaterializadas === 1 ? "ón" : "ones"} con comisión`} icon={Icons.Trophy}/>
        {/* Pagado = gasto real ya materializado · En previsión = todavía no
            existe en contabilidad, de ahí el ámbar. El KPI `accent` de arriba
            va sobre negro y mantiene su blanco (los tokens se rebindan pero no
            se le pasa clase: es el titular, no un estado). */}
        <KPI label={isAdmin ? "Pagado" : "Cobrado"} value={_eur0(cobrado)} unit="€" fin={isAdmin ? "fin-out" : "fin-in"} sub={`${t.nCobradas} materializada${t.nCobradas === 1 ? "" : "s"}`} icon={Icons.Check}/>
        <KPI label="En previsión" value={_eur0(pendiente)} unit="€" fin="fin-pend" sub={`${t.nPrevistas} sin materializar`} icon={Icons.Clock}/>
        <KPI label="Operaciones con comisión" value={String(nCierres)} sub="previsiones + pagadas" icon={Icons.Home}/>
      </div>

      <Card title={`Detalle de comisiones · ${nCierres}`} b={false}>
        {nCierres === 0 ? (
          <div className="empty" style={{padding: 24}}><span style={{fontSize: 12}}>Sin comisiones de equipo. Las ventas directas, sin comercial asignado, no generan comisión: no es un error.</span></div>
        ) : (
        <table className="tbl">
          <thead><tr>
            {/* "Base (honorarios)" y no "Precio venta": el % del comercial se
                aplica sobre los honorarios que cobra REVIN en la operación. El
                precio de venta no interviene en el cálculo. */}
            <th>Inmueble</th><th>Tipo</th><th className="num">Base (honorarios)</th><th className="num">Comisión</th><th>Captador</th><th>Comercializador</th><th className={isAdmin ? "" : "num"}>{isAdmin ? "Estado" : "Mi parte"}</th>
          </tr></thead>
          <tbody>
            {comms.map((c, i) => (
              <tr key={i}>
                <td>
                  <div style={{fontWeight: 600}}>{c.prop}</div>
                  <div className="code">{c.ref}</div>
                </td>
                <td><Pill tone="brand">VENTA</Pill></td>
                {/* Base + % aplicado. Los históricos nacieron como movimiento sin
                    pasar por el motor: no tienen base registrada → "—", nunca un
                    0 € que se leería como "base cero". */}
                <td className="num">
                  {c.base == null ? <span style={{color: "var(--fg-3)"}}>—</span> : fmtEUR(c.base)}
                  {c.pct != null && <div className="code" style={{fontSize: 11}}>{c.pct}%</div>}
                </td>
                {/* La comisión es un GASTO de REVIN, pero solo cuando existe de
                    verdad: mientras es previsión va en ámbar («aún no»), y solo
                    al materializarse pasa al rojo óxido del gasto. Es el mismo
                    criterio de la ficha de proyecto. */}
                <td className={"num fin-amt " + (c.materializada ? "fin-out" : c.estadoMotor === "cancelada" ? "fin-zero" : "fin-pend")}>
                  {fmtEUR(c.total)}
                </td>
                {/* CASO BORDE: operación sin comercial asignado. No es un error
                    ni un 0 € — simplemente no genera comisión de equipo. Se
                    pinta en neutro, nunca en rojo ni con importe falso. */}
                <td>
                  {c.captador === "—" ? (
                    <span style={{color: "var(--fg-3)"}}>—</span>
                  ) : (
                    <span className="split">
                      <span className="ava">{c.captador}</span>{fmtEUR(c.capt)}
                    </span>
                  )}
                </td>
                <td>
                  {c.comercializador === "—" || !c.comercialNombre ? (
                    <span style={{color: "var(--fg-3)"}}>Sin comercial asignado</span>
                  ) : (
                    <span className="split">
                      <span className="ava" title={c.comercialNombre}>{c.comercializador}</span>{fmtEUR(c.com)}
                    </span>
                  )}
                </td>
                {isAdmin ? (
                  /* Estado del MOTOR (pagada/prevista/aprobada), no "cobrada":
                     esto es dinero que REVIN PAGA, no que cobra. */
                  <td><Pill tone={c.materializada ? "success" : c.estadoMotor === "cancelada" ? "neutral" : "warning"}>{(c.estadoMotor || "—").toUpperCase()}</Pill></td>
                ) : (
                  /* "Mi parte" es dinero del COMERCIAL, no de los libros de
                     REVIN: no es ingreso ni gasto de la empresa, así que va en
                     color de texto normal. Antes usaba --revin-red, el rojo de
                     error, que es justo lo que esta paleta viene a evitar.
                     (Comentario JS: dentro de una rama de ternario las llaves
                     serían un objeto literal, no un comentario JSX.) */
                  <td className="num fin-amt">{fmtEUR(myPart(c))}</td>
                )}
              </tr>
            ))}
          </tbody>
        </table>
        )}
      </Card>

      <div style={{marginTop: 20}}>
        <Card title="Facturas a clientes" right={<Pill tone="neutral">Sin migrar</Pill>} b={false}>
          <div className="empty" style={{padding: 24, textAlign: "center"}}>
            <span style={{fontSize: 12}}>
              Sin facturas cargadas. La facturación aún no se ha migrado a Supabase (no existe la tabla <code style={{fontFamily: "var(--font-mono)"}}>facturas</code>);
              las comisiones de arriba sí son datos reales.
            </span>
          </div>
        </Card>
      </div>
    </>
  );
};

window.ClientsView = ClientsView;
window.AgendaView = AgendaView;
window.TasksView = TasksView;
window.DocumentsView = DocumentsView;
window.FinanceView = FinanceView;
window.RevinComisiones = RevinComisiones;
