// ============ PRISMA · PANEL CLIENTE — app shell ============
const { useState, useEffect, useRef, useMemo } = React;
const clClone = (o) => JSON.parse(JSON.stringify(o));
const ACCENT_KEYS = ['magenta', 'azul', 'indigo', 'teal'];

// ---- mappers ----
function mapBuilding(row, i) {
  const b = row.edificios || {};
  return {
    id:      row.edificio_id,
    slug:    b.slug    || '',
    name:    b.nombre  || '—',
    accent:  ACCENT_KEYS[i % ACCENT_KEYS.length],
    address: b.ciudad  || '',
    company: '',   // se llena desde el perfil del usuario si hace falta
  };
}

function sortFloor(label) {
  if (!label) return 999;
  const l = label.toUpperCase();
  if (l === 'PB') return 0;
  if (l === 'PA') return -1;
  const n = parseInt(l.replace(/\D/g, ''), 10);
  return isNaN(n) ? 999 : n;
}

function buildType(u) {
  const parts = [];
  if (u.dorm !== null && u.dorm !== undefined) {
    parts.push(u.dorm === 0 ? 'Monoamb.' : u.dorm + ' dorm');
  }
  if (u.tipo) parts.push(u.tipo);
  return parts.join(' · ') || '—';
}

// Carga disponibilidad de un edificio desde Supabase
async function loadBuildingAvail(buildingId) {
  const { data, error } = await window._sb
    .from('unidades')
    .select('id, title, tipo, dorm, disponibilidad(id, label, status)')
    .eq('edificio_id', buildingId)
    .order('title');
  if (error || !data) return [];
  return data.map(u => ({
    id:    u.id,
    name:  u.title || '—',
    type:  buildType(u),
    floors: (u.disponibilidad || [])
      .slice()
      .sort((a, b) => sortFloor(a.label) - sortFloor(b.label))
      .map(d => ({
        dispId: d.id,
        label:  d.label,
        status: window.DB_TO_DISPLAY[d.status] || 'disponible',
      })),
  }));
}

// ---- helpers mobile ----
const useIsMobile = () => {
  const [mobile, setMobile] = React.useState(() => window.innerWidth <= 680);
  React.useEffect(() => {
    const fn = () => setMobile(window.innerWidth <= 680);
    window.addEventListener('resize', fn);
    return () => window.removeEventListener('resize', fn);
  }, []);
  return mobile;
};

const CiMenu = (p) => (
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
       strokeWidth="1.8" strokeLinecap="round" {...p}>
    <path d="M3 6h18M3 12h18M3 18h18"/>
  </svg>
);

// ---- CL_NAV ----
const CL_NAV = [
  { id: 'dashboard',      label: 'Dashboard',      icon: window.CiDashboard },
  { id: 'disponibilidad', label: 'Disponibilidad', icon: window.CiAvail     },
  { id: 'metricas',       label: 'Métricas',       icon: window.CiChart     },
];
const CL_TITLES = { dashboard: 'Dashboard', disponibilidad: 'Disponibilidad', metricas: 'Métricas' };

// ---- Building selector ----
const ClBuildingSelector = ({ active, buildings, onPick }) => {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const fn = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', fn);
    return () => document.removeEventListener('mousedown', fn);
  }, []);
  const ac = window.acHex(active.accent);

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button onClick={() => setOpen(o => !o)} style={{
        display: 'flex', alignItems: 'center', gap: 9, width: '100%', textAlign: 'left',
        background: 'var(--bg-2)', border: '1px solid var(--line)', color: 'var(--txt)',
        borderRadius: 'var(--r-md)', padding: '9px 11px', transition: 'border-color .12s',
      }}>
        <span style={{ width: 8, height: 8, borderRadius: 999, background: ac, flex: '0 0 auto' }} />
        <span style={{ minWidth: 0, flex: 1 }}>
          <span style={{ display: 'block', fontSize: 13, fontWeight: 600,
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{active.name}</span>
          <span style={{ display: 'block', fontSize: 10.5, color: 'var(--txt-dim)', marginTop: 1 }}>Edificio activo</span>
        </span>
        <span style={{ display: 'grid', color: 'var(--txt-dim)', transition: 'transform .15s',
          transform: open ? 'rotate(180deg)' : 'none', flex: '0 0 auto' }}>
          <window.CiChevD size={15} />
        </span>
      </button>
      {open && (
        <div className="pop" style={{
          position: 'absolute', top: 'calc(100% + 6px)', left: 0, right: 0, zIndex: 50,
          background: 'var(--bg-1)', border: '1px solid var(--line-strong)', borderRadius: 'var(--r-md)',
          boxShadow: '0 16px 40px rgba(0,0,0,.55)', padding: 5,
        }}>
          <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: '0.07em',
            textTransform: 'uppercase', color: 'var(--txt-dim)', padding: '6px 8px 6px' }}>Tus edificios</div>
          {buildings.map(b => {
            const isA = b.id === active.id;
            const bac = window.acHex(b.accent);
            return (
              <button key={b.id} onClick={() => { onPick(b.id); setOpen(false); }} style={{
                display: 'flex', alignItems: 'center', gap: 10, width: '100%', textAlign: 'left',
                border: 'none', borderRadius: 7, padding: '9px 8px', cursor: 'pointer',
                background: isA ? 'var(--bg-2)' : 'transparent', color: 'var(--txt)',
              }}
                onMouseEnter={e => { if (!isA) e.currentTarget.style.background = 'var(--bg-2)'; }}
                onMouseLeave={e => { if (!isA) e.currentTarget.style.background = 'transparent'; }}>
                <span style={{ width: 9, height: 9, borderRadius: 999, background: bac, flex: '0 0 auto' }} />
                <span style={{ minWidth: 0, flex: 1 }}>
                  <span style={{ display: 'block', fontSize: 13, fontWeight: 600,
                    whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{b.name}</span>
                  <span style={{ display: 'block', fontSize: 11, color: 'var(--txt-mut)', marginTop: 1,
                    whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{b.address}</span>
                </span>
                {isA && <span style={{ color: bac, display: 'grid', flex: '0 0 auto' }}><window.CiCheck size={15} /></span>}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
};

// ---- Sidebar ----
const ClSidebar = ({ screen, setScreen, active, buildings, userName, onPickBuilding, onLogout, open, onClose }) => {
  const initials = userName ? userName.slice(0, 2).toUpperCase() : 'PS';
  const isMobile = useIsMobile();
  if (isMobile && !open) return null;
  return (
    <>
      {isMobile && <div className="cl-sidebar-overlay" onClick={onClose} />}
      <aside style={{
        width: 'var(--sidebar-w)', flex: isMobile ? 'none' : '0 0 var(--sidebar-w)',
        background: 'var(--bg-1)', borderRight: '1px solid var(--line)',
        display: 'flex', flexDirection: 'column', height: '100%',
        ...(isMobile ? { position: 'fixed', left: 0, top: 0, bottom: 0, zIndex: 50,
          boxShadow: '4px 0 24px rgba(0,0,0,.5)' } : {}),
      }}>
      <div style={{ padding: '18px 16px 12px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
          <img src="assets/logo-cropped.png" alt="Prisma" style={{ height: 26, display: 'block' }} />
          <span style={{ fontSize: 14, fontWeight: 700, letterSpacing: '-0.01em' }}>Prisma Studios</span>
        </div>
      </div>

      {buildings.length > 0 && (
        <div style={{ padding: '0 12px' }}>
          <ClBuildingSelector active={active} buildings={buildings} onPick={onPickBuilding} />
        </div>
      )}

      <div style={{ height: 1, background: 'var(--line)', margin: '14px 12px 8px' }} />

      <nav style={{ flex: 1, overflowY: 'auto', padding: '2px 10px' }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
          {CL_NAV.map(it => {
            const isActive = screen === it.id;
            const ac = window.acHex(active.accent);
            const Icon = it.icon;
            return (
              <button key={it.id} onClick={() => setScreen(it.id)} style={{
                position: 'relative', display: 'flex', alignItems: 'center', gap: 10, width: '100%',
                textAlign: 'left', border: 'none', borderRadius: 8, padding: '10px 12px', fontSize: 13.5,
                fontWeight: isActive ? 600 : 500, transition: 'background .12s, color .12s',
                background: isActive ? 'var(--bg-2)' : 'transparent',
                color: isActive ? 'var(--txt)' : 'var(--txt-mut)',
              }}
                onMouseEnter={e => { if (!isActive) { e.currentTarget.style.background = 'var(--bg-2)'; e.currentTarget.style.color = 'var(--txt)'; } }}
                onMouseLeave={e => { if (!isActive) { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'var(--txt-mut)'; } }}>
                {isActive && <span style={{ position: 'absolute', left: 0, top: 7, bottom: 7, width: 3, borderRadius: 3, background: ac }} />}
                <Icon size={17} style={{ flex: '0 0 auto' }} />
                {it.label}
              </button>
            );
          })}
        </div>
      </nav>

      <div style={{ borderTop: '1px solid var(--line)', padding: '12px 12px 10px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{
            width: 30, height: 30, borderRadius: 999, flex: '0 0 auto',
            background: window.hexA(window.acHex('magenta'), 0.18),
            border: '1px solid var(--line)', display: 'grid', placeItems: 'center',
            fontSize: 11.5, fontWeight: 700, color: window.acHex('magenta'),
          }}>{initials}</span>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
              {userName || 'Cliente'}
            </div>
            <div style={{ fontSize: 11, color: 'var(--txt-dim)' }}>Portal cliente</div>
          </div>
          <button onClick={onLogout} title="Cerrar sesión" style={{
            background: 'transparent', border: 'none', color: 'var(--txt-mut)',
            padding: 6, borderRadius: 7, display: 'grid', placeItems: 'center' }}
            onMouseEnter={e => { e.currentTarget.style.background = 'var(--bg-2)'; e.currentTarget.style.color = 'var(--txt)'; }}
            onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'var(--txt-mut)'; }}>
            <window.CiLogout size={16} />
          </button>
        </div>
        <div style={{ fontSize: 10.5, color: 'var(--txt-dim)', textAlign: 'center', marginTop: 11, letterSpacing: '0.02em' }}>
          Powered by Prisma Studios
        </div>
      </div>
      </aside>
    </>
  );
};

// ---- Topbar ----
const ClTopbar = ({ screen, active, onMenuClick }) => {
  const ac = window.acHex(active.accent);
  const isMobile = useIsMobile();
  const showroomUrl = active.slug
    ? `https://${active.slug.replace(/-/g, '')}.prismastudios3d.net`
    : null;
  return (
    <header style={{
      position: 'sticky', top: 0, zIndex: 30, height: 'var(--topbar-h)', flex: '0 0 var(--topbar-h)',
      background: 'var(--bg-1)', borderBottom: '1px solid var(--line)',
      display: 'flex', alignItems: 'center', gap: 8, padding: '0 14px',
      overflow: 'hidden',
    }}>
      {isMobile && (
        <button onClick={onMenuClick} style={{
          background: 'transparent', border: 'none', color: 'var(--txt-mut)', flexShrink: 0,
          padding: '6px 8px', borderRadius: 7, display: 'grid', placeItems: 'center',
        }}>
          <CiMenu />
        </button>
      )}
      <div style={{ fontSize: 15, fontWeight: 600, flexShrink: 0 }}>{CL_TITLES[screen]}</div>
      <span style={{
        display: 'inline-flex', alignItems: 'center', gap: 5, flexShrink: 0,
        background: window.hexA(ac, 0.13), color: ac,
        borderRadius: 999, padding: '4px 10px', fontSize: 11, fontWeight: 600,
      }}>
        <span style={{ width: 6, height: 6, borderRadius: 999, background: ac }} />
        {active.name}
      </span>
      {showroomUrl && !isMobile && (
        <a href={showroomUrl} target="_blank" rel="noopener noreferrer" style={{
          marginLeft: 'auto', flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 6,
          background: 'transparent', border: '1px solid var(--line)',
          borderRadius: 8, padding: '6px 12px', fontSize: 12, fontWeight: 600,
          color: 'var(--txt-mut)', textDecoration: 'none', transition: 'color .12s, border-color .12s',
        }}
          onMouseEnter={e => { e.currentTarget.style.color = 'var(--txt)'; e.currentTarget.style.borderColor = 'var(--line-strong)'; }}
          onMouseLeave={e => { e.currentTarget.style.color = 'var(--txt-mut)'; e.currentTarget.style.borderColor = 'var(--line)'; }}>
          Ver showroom
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="M15 3h6v6"/><path d="M10 14L21 3"/>
          </svg>
        </a>
      )}
    </header>
  );
};

// ---- Save Banner ----
const ClSaveBanner = ({ count, justSaved, onSave, onDiscard, saving, error }) => {
  if (!count && !justSaved && !error) return null;
  const isError = !!error;
  const bg    = isError ? window.hexA('#EF4444', 0.08) : justSaved ? window.hexA('#22C55E', 0.08) : 'var(--bg-2)';
  const border = isError ? window.hexA('#EF4444', 0.4)  : justSaved ? window.hexA('#22C55E', 0.4)  : 'var(--line-strong)';
  return (
    <div style={{
      position: 'fixed', left: 0, right: 0, bottom: 0, zIndex: 60,
      padding: '12px 28px 20px',
      background: 'linear-gradient(to top, var(--bg-0) 45%, rgba(17,17,17,0))',
      pointerEvents: 'none',
    }}>
      <div className="slide-up" style={{
        maxWidth: 1024, margin: '0 auto', pointerEvents: 'auto',
        display: 'flex', alignItems: 'center', gap: 14,
        background: bg, border: '1px solid ' + border,
        borderRadius: 12, padding: '12px 14px 12px 18px',
        boxShadow: '0 18px 50px rgba(0,0,0,.55)',
      }}>
        {isError ? (
          <>
            <span style={{ color: 'var(--bad)', display: 'grid', flex: '0 0 auto' }}><window.CiWarn size={19} /></span>
            <span style={{ fontSize: 13.5, fontWeight: 500, flex: 1, color: '#fca5a5' }}>{error}</span>
            <button onClick={onSave} disabled={saving} style={{
              background: '#fff', color: '#000', border: 'none', borderRadius: 8,
              padding: '8px 16px', fontSize: 13, fontWeight: 700,
              display: 'inline-flex', alignItems: 'center', gap: 6 }}>
              Reintentar
            </button>
          </>
        ) : justSaved ? (
          <>
            <span style={{ width: 24, height: 24, borderRadius: 999, background: '#22C55E', color: '#000',
              display: 'grid', placeItems: 'center', flex: '0 0 auto' }}>
              <window.CiCheck size={14} sw={2.5} />
            </span>
            <span style={{ fontSize: 14, fontWeight: 600, color: '#4ade80' }}>Cambios guardados</span>
          </>
        ) : (
          <>
            <span style={{ color: 'var(--pending)', display: 'grid', flex: '0 0 auto' }}><window.CiInfo size={19} /></span>
            <span style={{ fontSize: 14, fontWeight: 500, flex: 1 }}>
              <strong>{count}</strong> {count === 1 ? 'cambio sin guardar' : 'cambios sin guardar'}
            </span>
            <button onClick={onDiscard} disabled={saving} style={{
              background: 'transparent', border: '1px solid var(--line-strong)',
              borderRadius: 8, padding: '8px 14px', fontSize: 13, fontWeight: 600, color: 'var(--txt-mut)' }}
              onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--bad)'; e.currentTarget.style.color = 'var(--bad)'; }}
              onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--line-strong)'; e.currentTarget.style.color = 'var(--txt-mut)'; }}>
              Descartar
            </button>
            <button onClick={onSave} disabled={saving} style={{
              background: '#fff', color: '#000', border: 'none',
              borderRadius: 8, padding: '8px 16px', fontSize: 13, fontWeight: 700,
              display: 'inline-flex', alignItems: 'center', gap: 6, opacity: saving ? 0.6 : 1,
              transition: 'opacity .12s' }}
              onMouseEnter={e => { if (!saving) e.currentTarget.style.opacity = '0.88'; }}
              onMouseLeave={e => { if (!saving) e.currentTarget.style.opacity = '1'; }}>
              {saving ? <><window.CiSpinner size={14} /> Guardando…</> : <><window.CiCheck size={14} /> Guardar</>}
            </button>
          </>
        )}
      </div>
    </div>
  );
};

// ---- Switch-building confirm modal ----
const ClSwitchConfirm = ({ fromName, onCancel, onConfirm }) => (
  <div style={{ position: 'fixed', inset: 0, zIndex: 90, background: 'rgba(0,0,0,0.6)',
    display: 'grid', placeItems: 'center', padding: 24 }}
    onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel(); }}>
    <div className="pop" style={{ width: 400, maxWidth: '100%', background: 'var(--bg-1)',
      border: '1px solid var(--line-strong)', borderRadius: 14,
      boxShadow: '0 30px 80px rgba(0,0,0,.7)', padding: 24 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <span style={{ width: 36, height: 36, borderRadius: 9,
          background: window.hexA('#F97316', 0.14), color: 'var(--pending)',
          display: 'grid', placeItems: 'center', flex: '0 0 auto' }}>
          <window.CiWarn size={19} />
        </span>
        <div style={{ fontSize: 16, fontWeight: 700 }}>Cambios sin guardar</div>
      </div>
      <p style={{ fontSize: 13.5, color: 'var(--txt-mut)', marginTop: 14, lineHeight: 1.55 }}>
        Hiciste cambios en <strong style={{ color: 'var(--txt)' }}>{fromName}</strong> que todavía no guardaste.
        Si cambiás de edificio, esos cambios se descartan.
      </p>
      <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10, marginTop: 20 }}>
        <button onClick={onCancel} style={{
          background: 'transparent', border: '1px solid var(--line-strong)', borderRadius: 8,
          padding: '9px 16px', fontSize: 13.5, fontWeight: 600, color: 'var(--txt)' }}>
          Seguir editando
        </button>
        <button onClick={onConfirm} style={{
          background: 'var(--bad)', color: '#fff', border: 'none', borderRadius: 8,
          padding: '9px 16px', fontSize: 13.5, fontWeight: 700 }}>
          Descartar y cambiar
        </button>
      </div>
    </div>
  </div>
);

// ---- Boot spinner ----
const ClBootSpinner = () => (
  <div style={{ display: 'grid', placeItems: 'center', height: '100%', background: 'var(--bg-0)' }}>
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 20 }}>
      <img src="assets/logo-cropped.png" alt="Prisma" style={{ height: 32, opacity: 0.7 }} />
      <window.CiSpinner size={22} />
    </div>
  </div>
);

// ---- Loading panel ----
const ClLoadingPanel = () => (
  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12,
    padding: '80px 0', color: 'var(--txt-dim)' }}>
    <window.CiSpinner size={22} />
    <span>Cargando unidades…</span>
  </div>
);

// ---- No buildings ----
const ClNoBuildings = ({ onLogout }) => (
  <div style={{ display: 'grid', placeItems: 'center', height: '100%', background: 'var(--bg-0)' }}>
    <div style={{ textAlign: 'center', padding: 24 }}>
      <div style={{ fontSize: 16, fontWeight: 600, marginBottom: 8 }}>Sin edificios asignados</div>
      <div style={{ fontSize: 13, color: 'var(--txt-mut)', marginBottom: 20 }}>
        Tu cuenta no tiene edificios vinculados. Contactá a Prisma Studios.
      </div>
      <button onClick={onLogout} style={{
        background: 'var(--bg-2)', border: '1px solid var(--line)', color: 'var(--txt)',
        borderRadius: 8, padding: '9px 18px', fontSize: 13, fontWeight: 600 }}>
        Cerrar sesión
      </button>
    </div>
  </div>
);

// ---- App root ----
const ClApp = () => {
  const [booting,       setBooting]       = useState(true);
  const [logged,        setLogged]        = useState(false);
  const [loginError,    setLoginError]    = useState('');
  const [user,          setUser]          = useState(null);
  const [buildings,     setBuildings]     = useState([]);
  const [activeId,      setActiveId]      = useState(null);
  const [screen,        setScreen]        = useState(() => localStorage.getItem('cl_screen') || 'dashboard');
  const [sidebarOpen,   setSidebarOpen]   = useState(false);
  const [period,        setPeriod]        = useState('hoy');

  // disponibilidad
  const [work,          setWork]          = useState({});   // { buildingId: rows[] }
  const [saved,         setSaved]         = useState({});   // { buildingId: rows[] }
  const [loadingAvail,  setLoadingAvail]  = useState(false);
  const [saving,        setSaving]        = useState(false);
  const [saveError,     setSaveError]     = useState('');
  const [justSaved,     setJustSaved]     = useState(false);
  const [pendingSwitch, setPendingSwitch] = useState(null);

  const scrollRef = useRef(null);

  // persistir sección activa
  useEffect(() => { localStorage.setItem('cl_screen', screen); }, [screen]);

  // reset scroll al cambiar sección o edificio
  useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = 0;
  }, [screen, activeId]);

  // verificar sesión al arrancar
  useEffect(() => {
    window._sb.auth.getSession().then(({ data: { session } }) => {
      if (session) {
        setUser(session.user);
        doLoadBuildings(session.user.id).then(bs => {
          if (bs.length) setActiveId(bs[0].id);
          setLogged(true);
        });
      } else {
        setBooting(false);
      }
    });
  }, []);

  const doLoadBuildings = async (userId) => {
    const { data } = await window._sb
      .from('usuarios_clientes')
      .select('edificio_id, edificios(id, slug, nombre, ciudad)')
      .eq('user_id', userId);
    const bs = (data || []).map(mapBuilding);
    setBuildings(bs);
    setBooting(false);
    return bs;
  };

  // cargar disponibilidad de un edificio (lazy, una sola vez por sesión)
  const ensureAvail = async (buildingId) => {
    if (work[buildingId] !== undefined) return;
    setLoadingAvail(true);
    const rows = await loadBuildingAvail(buildingId);
    setWork(prev  => ({ ...prev,  [buildingId]: rows }));
    setSaved(prev => ({ ...prev,  [buildingId]: clClone(rows) }));
    setLoadingAvail(false);
  };

  // precargar disponibilidad apenas hay edificio activo (para que Dashboard muestre contadores)
  useEffect(() => {
    if (activeId) ensureAvail(activeId);
  }, [activeId]);

  const handleLogin = async (email, password) => {
    setLoginError('');
    const { data, error } = await window._sb.auth.signInWithPassword({ email, password });
    if (error) { setLoginError(error.message); return; }
    setUser(data.user);
    const bs = await doLoadBuildings(data.user.id);
    if (bs.length) setActiveId(bs[0].id);
    setLogged(true);
  };

  const handleLogout = async () => {
    await window._sb.auth.signOut();
    setLogged(false);
    setUser(null);
    setBuildings([]);
    setActiveId(null);
    setWork({});
    setSaved({});
    setScreen('dashboard');
    setLoginError('');
  };

  // rotar estado de un chip
  const handleCycle = (ri, fi) => {
    setJustSaved(false);
    setWork(prev => {
      const next = clClone(prev);
      const cur = next[activeId][ri].floors[fi].status;
      const idx = window.CL_STATUS_CYCLE.indexOf(cur);
      next[activeId][ri].floors[fi].status = window.CL_STATUS_CYCLE[(idx + 1) % window.CL_STATUS_CYCLE.length];
      return next;
    });
  };

  // cantidad de cambios pendientes
  const pendingCount = useMemo(() => {
    if (!work[activeId] || !saved[activeId]) return 0;
    let n = 0;
    work[activeId].forEach((row, ri) => {
      (row.floors || []).forEach((f, fi) => {
        const s = saved[activeId][ri];
        if (s && f.status !== s.floors[fi]?.status) n++;
      });
    });
    return n;
  }, [work, saved, activeId]);

  // batch save: solo filas modificadas
  const handleSave = async () => {
    if (!work[activeId] || !saved[activeId]) return;
    setSaving(true);
    setSaveError('');
    const now = new Date().toISOString();
    const promises = [];
    work[activeId].forEach((row, ri) => {
      (row.floors || []).forEach((f, fi) => {
        const s = saved[activeId][ri];
        if (s && f.status !== s.floors[fi]?.status) {
          promises.push(
            window._sb.from('disponibilidad')
              .update({ status: window.DISPLAY_TO_DB[f.status], updated_at: now })
              .eq('id', f.dispId)
          );
        }
      });
    });
    const results = await Promise.all(promises);
    const failed = results.filter(r => r.error);
    setSaving(false);
    if (failed.length) {
      setSaveError('Error al guardar ' + failed.length + ' cambio(s). Verificá tu conexión y reintentá.');
      return;
    }
    setSaved(prev => ({ ...prev, [activeId]: clClone(work[activeId]) }));
    setJustSaved(true);
    setTimeout(() => setJustSaved(false), 2200);
  };

  // forzar recarga de disponibilidad (botón Recargar)
  const handleRefreshAvail = async () => {
    if (!activeId) return;
    setLoadingAvail(true);
    setSaveError('');
    const rows = await loadBuildingAvail(activeId);
    setWork(prev  => ({ ...prev, [activeId]: rows }));
    setSaved(prev => ({ ...prev, [activeId]: clClone(rows) }));
    setLoadingAvail(false);
  };

  const handleDiscard = () => {
    if (!saved[activeId]) return;
    setWork(prev => ({ ...prev, [activeId]: clClone(saved[activeId]) }));
    setJustSaved(false);
  };

  // solicitar cambio de edificio (puede tener cambios pendientes)
  const requestSwitch = (id) => {
    if (id === activeId) return;
    if (pendingCount > 0) { setPendingSwitch(id); return; }
    setActiveId(id);
  };

  const confirmSwitch = () => {
    handleDiscard();
    const id = pendingSwitch;
    setActiveId(id);
    setPendingSwitch(null);
    setJustSaved(false);
    if (screen === 'disponibilidad') ensureAvail(id);
  };

  // estados de arranque
  if (booting) return <ClBootSpinner />;
  if (!logged) return <window.ClScreenLogin onLogin={handleLogin} error={loginError} />;

  const active = buildings.find(b => b.id === activeId) || buildings[0];
  if (!active) return <ClNoBuildings onLogout={handleLogout} />;

  const savedRows = saved[activeId] || [];
  const counts = window.clCountStatuses(savedRows);

  return (
    <div style={{ display: 'flex', height: '100%', overflow: 'hidden', maxWidth: '100vw' }}>
      <ClSidebar
        screen={screen} setScreen={(s) => { setScreen(s); setSidebarOpen(false); }}
        active={active} buildings={buildings}
        userName={user?.email?.split('@')[0] || 'Cliente'}
        onPickBuilding={(id) => { requestSwitch(id); setSidebarOpen(false); }}
        onLogout={handleLogout}
        open={sidebarOpen} onClose={() => setSidebarOpen(false)}
      />
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, height: '100%', overflowX: 'hidden' }}>
        <ClTopbar screen={screen} active={active} onMenuClick={() => setSidebarOpen(o => !o)} />
        <main ref={scrollRef} style={{ flex: 1, overflowY: 'auto', position: 'relative' }}>
          {screen === 'dashboard'      && <window.ClScreenDashboard building={active} counts={counts} />}
          {screen === 'disponibilidad' && (
            loadingAvail
              ? <ClLoadingPanel />
              : <window.ClScreenDisponibilidad
                  building={active}
                  work={work[activeId] || []}
                  saved={savedRows}
                  onCycle={handleCycle}
                  onRefresh={handleRefreshAvail}
                />
          )}
          {screen === 'metricas' && <window.ClScreenMetricas building={active} period={period} setPeriod={setPeriod} />}
        </main>
      </div>

      {screen === 'disponibilidad' && (
        <ClSaveBanner
          count={pendingCount} justSaved={justSaved} saving={saving}
          error={saveError} onSave={handleSave} onDiscard={handleDiscard}
        />
      )}

      {pendingSwitch && (
        <ClSwitchConfirm
          fromName={active.name}
          onCancel={() => setPendingSwitch(null)}
          onConfirm={confirmSwitch}
        />
      )}
    </div>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(<ClApp />);
