/* Admin panel: shell + dashboard + modules */

/* Sidebar */
const ADMIN_MENU_PRINCIPAL = [
  { id: 'dashboard', l: 'Dashboard', icon: 'chart' },
  { id: 'alumnos', l: 'Alumnos', icon: 'users' },
  { id: 'pagos', l: 'Ventas y pagos', icon: 'money' },
  { id: 'comprobantes', l: 'Verificar comprobantes', icon: 'doc' },
  { id: 'historial', l: 'Historial', icon: 'history' },
];

const ADMIN_MENU_GESTION = [
  { id: 'whatsapp', l: 'WhatsApp', icon: 'wa', sub: [
    { id: 'wa-dashboard', l: 'Dashboard' },
    { id: 'wa-recordatorios', l: 'Recordatorios' },
    { id: 'wa-pendientes', l: 'Pendientes' },
    { id: 'wa-urgentes', l: 'Urgentes' },
    { id: 'wa-campanas', l: 'Campañas' },
    { id: 'wa-plantillas', l: 'Plantillas' },
    { id: 'wa-historial', l: 'Historial' },
    { id: 'wa-programar', l: 'Programar' },
  ]},
  { id: 'anuncios', l: 'Anuncios', icon: 'megaphone' },
  { id: 'eventos', l: 'Eventos', icon: 'cal' },
  { id: 'inventario', l: 'Inventario', icon: 'box' },
  { id: 'tienda-admin', l: 'Tienda online', icon: 'cart' },
  { id: 'nomina-gastos', l: 'Nómina y gastos', icon: 'money' },
  { id: 'galeria-admin', l: 'Galería', icon: 'image' },
  { id: 'entrenadores-admin', l: 'Entrenadores', icon: 'whistle' },
  { id: 'ubicacion', l: 'Ubicación', icon: 'pin' },
  { id: 'interesados', l: 'Interesados', icon: 'form' },
  { id: 'config', l: 'Configuración', icon: 'settings' },
];

const ADMIN_MENU = [...ADMIN_MENU_PRINCIPAL, ...ADMIN_MENU_GESTION];

function renderAdminNavItems (items, { theme, current, onNav, compact, openSub, setOpenSub, highlightIds = [] }) {
  return items.map(item => {
    const isActive = current === item.id || (item.sub && item.sub.find(s => s.id === current));
    const hasSub = item.sub && item.sub.length > 0;
    const isOpen = openSub === item.id;
    const isHighlight = highlightIds.includes(item.id);
    return (
      <React.Fragment key={item.id}>
        <button
          type="button"
          className="tecos-admin-nav-btn"
          onClick={() => {
          if (hasSub) { setOpenSub(isOpen ? '' : item.id); onNav(item.sub[0].id); }
          else onNav(item.id);
        }}
          style={{
            display: 'flex', alignItems: 'center', gap: 12,
            padding: compact ? '8px 10px' : '10px 12px', borderRadius: 8,
            background: isActive && !hasSub ? `${theme.primary}22` : isHighlight ? `${theme.warning}22` : 'transparent',
            color: isActive ? theme.primary : isHighlight ? theme.warning : theme.textDim,
            border: isHighlight ? `1px solid ${theme.warning}88` : 'none',
            boxShadow: isHighlight ? `0 0 12px ${theme.warning}55` : 'none',
            cursor: 'pointer', width: '100%',
            fontSize: compact ? 12 : 13, fontWeight: 600, fontFamily: 'Inter, sans-serif',
            letterSpacing: 0.3, position: 'relative',
            transition: 'all .2s',
            animation: isHighlight ? 'tecos-pulse 1.5s ease-in-out infinite' : 'none',
          }}
          onMouseEnter={e => !isActive && (e.currentTarget.style.background = theme.bgInput)}
          onMouseLeave={e => !isActive && (e.currentTarget.style.background = 'transparent')}
        >
          {isActive && !hasSub && <div style={{ position: 'absolute', left: -14, top: '50%', transform: 'translateY(-50%)', width: 3, height: 20, borderRadius: 2, background: theme.primary }}></div>}
          <Icon name={item.icon} size={compact ? 16 : 18} />
          <span style={{ flex: 1, textAlign: 'left' }}>{item.l}</span>
          {hasSub && <Icon name="chev" size={12} style={{ transform: isOpen ? 'rotate(90deg)' : 'none', transition: 'transform .2s' }} />}
        </button>
        {hasSub && isOpen && (
          <div style={{ display: 'grid', gap: 1, paddingLeft: 12, marginBottom: 4, marginTop: 2 }}>
            {item.sub.map(s => (
              <button key={s.id} type="button" className="tecos-admin-nav-sub-btn" onClick={() => onNav(s.id)} style={{
                padding: compact ? '5px 12px 5px 22px' : '7px 12px 7px 28px',
                borderRadius: 6, border: 'none',
                background: current === s.id ? `${theme.primary}22` : 'transparent',
                color: current === s.id ? theme.primary : theme.textMute,
                textAlign: 'left', fontSize: 12, cursor: 'pointer',
                fontFamily: 'Inter, sans-serif', fontWeight: 500,
                display: 'flex', alignItems: 'center', gap: 8,
              }}>
                <span style={{ width: 4, height: 4, borderRadius: '50%', background: current === s.id ? theme.primary : theme.textMute }}></span>
                {s.l}
              </button>
            ))}
          </div>
        )}
      </React.Fragment>
    );
  });
}

const Sidebar = ({ theme, current, onNav, variant = 'expanded', onLogout, mobileOpen = false, onMobileClose, highlightModuleIds = [] }) => {
  const isMobile = useIsMobile();
  const effectiveVariant = isMobile ? 'expanded' : variant;
  const [openSub, setOpenSub] = React.useState('');
  React.useEffect(() => {
    setOpenSub(isMobile ? '' : 'whatsapp');
  }, [isMobile]);
  React.useEffect(() => {
    if (!isMobile || !mobileOpen) return undefined;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = prev; };
  }, [isMobile, mobileOpen]);
  const [adminLabel, setAdminLabel] = React.useState('Administrador');
  const [highlightIds, setHighlightIds] = React.useState(highlightModuleIds);

  React.useEffect(() => {
    setHighlightIds(highlightModuleIds);
  }, [highlightModuleIds]);

  React.useEffect(() => {
    const sync = () => {
      if (window.__tecosNominaHighlight) setHighlightIds((ids) => [...new Set([...ids, 'nomina-gastos'])]);
    };
    sync();
    window.addEventListener('tecos:finance-changed', sync);
    return () => window.removeEventListener('tecos:finance-changed', sync);
  }, []);

  const navTo = React.useCallback((id) => {
    onNav(id);
    if (isMobile) onMobileClose?.();
  }, [onNav, isMobile, onMobileClose]);

  const loadAdminLabel = React.useCallback(async () => {
    if (typeof fetchMyProfile !== 'function' || !isSupabaseReady()) return;
    try {
      const p = await fetchMyProfile();
      if (p?.full_name) setAdminLabel(p.full_name);
    } catch (_) { /* ignore */ }
  }, []);

  React.useEffect(() => {
    loadAdminLabel();
    const onProfile = () => loadAdminLabel();
    window.addEventListener('tecos:admin-profile-changed', onProfile);
    return () => window.removeEventListener('tecos:admin-profile-changed', onProfile);
  }, [loadAdminLabel]);

  if (effectiveVariant === 'icons') {
    return (
      <aside style={{
        width: 72, background: theme.bgElev,
        borderRight: `1px solid ${theme.border}`,
        display: 'flex', flexDirection: 'column', padding: '20px 0',
        position: 'sticky', top: 0, height: '100vh', overflowY: 'auto',
      }}>
        <div style={{ padding: '0 12px 20px', display: 'grid', placeItems: 'center' }}>
          <Logo size={40} withText={false} theme={theme} />
        </div>
        <nav style={{ display: 'grid', gap: 4, padding: '0 12px', flex: 1 }}>
          {ADMIN_MENU.map(item => (
            <button key={item.id} onClick={() => navTo(item.id)}
              title={item.l}
              style={{
                width: 48, height: 48, borderRadius: 10, border: 'none',
                background: current === item.id || (item.sub && item.sub.find(s => s.id === current)) ? `${theme.primary}22` : 'transparent',
                color: current === item.id || (item.sub && item.sub.find(s => s.id === current)) ? theme.primary : theme.textDim,
                display: 'grid', placeItems: 'center', cursor: 'pointer',
                transition: 'all .2s',
              }}
              onMouseEnter={e => e.currentTarget.style.background = theme.bgInput}
              onMouseLeave={e => e.currentTarget.style.background =
                current === item.id || (item.sub && item.sub.find(s => s.id === current)) ? `${theme.primary}22` : 'transparent'}
            ><Icon name={item.icon} size={20} /></button>
          ))}
        </nav>
        <div style={{ padding: '12px' }}>
          <button onClick={onLogout} title="Salir" style={{
            width: 48, height: 48, borderRadius: 10, border: 'none',
            background: theme.bgInput, color: theme.danger,
            display: 'grid', placeItems: 'center', cursor: 'pointer',
          }}><Icon name="arrowLeft" size={20} /></button>
        </div>
      </aside>
    );
  }

  const compact = effectiveVariant === 'compact';
  const width = compact ? 200 : 248;

  const asideStyle = {
    width: isMobile ? 'min(100vw, 320px)' : width,
    background: theme.bgElev,
    borderRight: `1px solid ${theme.border}`,
    display: 'flex', flexDirection: 'column',
    height: isMobile ? '100dvh' : '100vh',
    maxHeight: isMobile ? '100dvh' : '100vh',
    overflow: 'hidden',
    ...(isMobile ? {
      position: 'fixed',
      top: 0,
      left: 0,
      zIndex: 200,
      transform: mobileOpen ? 'translateX(0)' : 'translateX(-110%)',
      transition: 'transform 0.25s ease',
      boxShadow: mobileOpen ? '4px 0 24px rgba(0,0,0,0.35)' : 'none',
    } : {
      position: 'sticky',
      top: 0,
      overflowY: 'auto',
    }),
  };

  return (
    <>
      {isMobile && <DrawerBackdrop open={mobileOpen} onClose={onMobileClose} />}
      <aside className={isMobile ? 'tecos-sidebar-drawer tecos-admin-sidebar-drawer' : 'tecos-sidebar'} style={asideStyle}>
      <div className="tecos-sidebar-drawer__head" style={{ padding: '16px 18px', borderBottom: `1px solid ${theme.border}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, flexShrink: 0 }}>
        <Logo size={compact ? 40 : 48} theme={theme} />
        {isMobile && <MobileMenuButton theme={theme} open onClick={onMobileClose} />}
      </div>
      <div className="tecos-sidebar-drawer__body" style={{ padding: '12px 14px 16px', flex: 1, minHeight: 0, overflowY: 'auto', WebkitOverflowScrolling: 'touch' }}>
        <div style={{ fontSize: 10, color: theme.textMute, letterSpacing: 1.5, fontWeight: 700, textTransform: 'uppercase', padding: '8px 12px', fontFamily: 'Inter, sans-serif' }}>Principal</div>
        <nav style={{ display: 'grid', gap: 4 }}>
          {renderAdminNavItems(ADMIN_MENU_PRINCIPAL, { theme, current, onNav: navTo, compact, openSub, setOpenSub, highlightIds })}
        </nav>
        <div style={{ fontSize: 10, color: theme.textMute, letterSpacing: 1.5, fontWeight: 700, textTransform: 'uppercase', padding: '16px 12px 8px', fontFamily: 'Inter, sans-serif' }}>Gestión</div>
        <nav style={{ display: 'grid', gap: 4 }}>
          {renderAdminNavItems(ADMIN_MENU_GESTION, { theme, current, onNav: navTo, compact, openSub, setOpenSub, highlightIds })}
        </nav>
      </div>
      <div className="tecos-sidebar-drawer__foot" style={{ padding: 14, borderTop: `1px solid ${theme.border}`, display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0 }}>
        <Avatar name={adminLabel} size={36} color={theme.primary} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ color: theme.text, fontSize: 12, fontWeight: 700, fontFamily: 'Inter, sans-serif', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{adminLabel}</div>
          <div style={{ color: theme.textMute, fontSize: 10, fontFamily: 'Inter, sans-serif' }}>Administrador</div>
        </div>
        <button onClick={onLogout} title="Salir" style={{
          width: 32, height: 32, borderRadius: 8, background: theme.bgInput,
          border: `1px solid ${theme.border}`, color: theme.text, cursor: 'pointer',
          display: 'grid', placeItems: 'center',
        }}><Icon name="arrowLeft" size={14} /></button>
      </div>
    </aside>
    </>
  );
};

const SEARCH_KIND_META = {
  student: { label: 'Alumnos', icon: 'users' },
  payment: { label: 'Pagos', icon: 'money' },
  order: { label: 'Órdenes', icon: 'cart' },
};

const AdminGlobalSearch = ({ theme, onNavigate, enabled = true }) => {
  const [query, setQuery] = React.useState('');
  const [open, setOpen] = React.useState(false);
  const [loading, setLoading] = React.useState(false);
  const [results, setResults] = React.useState({ students: [], payments: [], orders: [] });
  const ref = React.useRef(null);
  const debounceRef = React.useRef(null);

  const flatResults = React.useMemo(() => (
    [...(results.students || []), ...(results.payments || []), ...(results.orders || [])]
  ), [results]);

  React.useEffect(() => {
    if (!enabled) return undefined;
    const q = query.trim();
    if (debounceRef.current) clearTimeout(debounceRef.current);
    if (q.length < 2) {
      setResults({ students: [], payments: [], orders: [] });
      setLoading(false);
      return undefined;
    }
    setLoading(true);
    debounceRef.current = setTimeout(async () => {
      try {
        const data = await searchAdminGlobal(q);
        setResults(data);
        setOpen(true);
      } catch (e) {
        console.error('[Tecos] search', e);
        setResults({ students: [], payments: [], orders: [] });
      }
      setLoading(false);
    }, 320);
    return () => { if (debounceRef.current) clearTimeout(debounceRef.current); };
  }, [query, enabled]);

  React.useEffect(() => {
    const close = (e) => {
      if (ref.current && !ref.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, []);

  const pickResult = (item) => {
    if (!item?.nav || typeof onNavigate !== 'function') return;
    onNavigate(item.nav);
    setQuery('');
    setOpen(false);
    setResults({ students: [], payments: [], orders: [] });
  };

  const onKeyDown = (e) => {
    if (e.key === 'Enter' && flatResults[0]) {
      e.preventDefault();
      pickResult(flatResults[0]);
    }
    if (e.key === 'Escape') setOpen(false);
  };

  if (!enabled) {
    return (
      <div style={{ width: 280, opacity: 0.5 }}>
        <Input theme={theme} placeholder="Búsqueda en panel admin" icon="search" disabled />
      </div>
    );
  }

  return (
    <div ref={ref} className="tecos-admin-search" style={{ position: 'relative', width: 300, maxWidth: '100%' }}>
      <Input
        theme={theme}
        placeholder="Buscar alumnos, pagos, órdenes..."
        icon="search"
        value={query}
        onChange={(e) => { setQuery(e.target.value); setOpen(true); }}
        onFocus={() => { if (query.trim().length >= 2) setOpen(true); }}
        onKeyDown={onKeyDown}
      />
      {open && query.trim().length >= 2 && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 6px)', left: 0, right: 0,
          background: theme.bgElev, border: `1px solid ${theme.border}`,
          borderRadius: 12, boxShadow: '0 16px 40px rgba(0,0,0,0.25)',
          zIndex: 220, maxHeight: 360, overflowY: 'auto',
        }}>
          {loading ? (
            <div style={{ padding: 16, fontSize: 13, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>Buscando…</div>
          ) : flatResults.length === 0 ? (
            <div style={{ padding: 16, fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>Sin resultados</div>
          ) : (
            flatResults.map((item) => {
              const meta = SEARCH_KIND_META[item.kind] || { label: 'Resultado', icon: 'search' };
              return (
                <button
                  key={`${item.kind}-${item.id}`}
                  type="button"
                  onClick={() => pickResult(item)}
                  style={{
                    display: 'flex', gap: 10, alignItems: 'center', width: '100%',
                    padding: '10px 12px', border: 'none', borderBottom: `1px solid ${theme.border}`,
                    background: 'transparent', cursor: 'pointer', textAlign: 'left',
                  }}
                >
                  <div style={{
                    width: 32, height: 32, borderRadius: 8, flexShrink: 0,
                    background: `${theme.primary}22`, color: theme.primary,
                    display: 'grid', placeItems: 'center',
                  }}>
                    <Icon name={meta.icon} size={14} />
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: theme.text, fontFamily: 'Inter, sans-serif',
                      whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.title}</div>
                    <div style={{ fontSize: 11, color: theme.textMute, fontFamily: 'Inter, sans-serif',
                      whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.subtitle}</div>
                  </div>
                  <span style={{ fontSize: 10, color: theme.primary, fontWeight: 700, flexShrink: 0 }}>{meta.label}</span>
                </button>
              );
            })
          )}
        </div>
      )}
    </div>
  );
};

/* Top bar */
const AdminTopbar = ({
  theme, title, subtitle, panelLabel = 'Panel administrador',
  notificationAudience = 'admin', studentCode, onAdminNavigate, onStudentNavigate,
  themeKey = 'light', onThemeToggle, onMenuToggle,
}) => {
  const isMobile = useIsMobile();
  const searchEnabled = notificationAudience === 'admin';
  const bell = (
    <NotificationBell theme={theme} audience={notificationAudience} studentCode={studentCode}
      onAdminNavigate={onAdminNavigate} onStudentNavigate={onStudentNavigate} />
  );
  const themeBtn = <ThemeToggleButton theme={theme} themeKey={themeKey} onToggle={onThemeToggle} />;
  const search = searchEnabled
    ? <AdminGlobalSearch theme={theme} enabled onNavigate={onAdminNavigate} />
    : null;

  return (
    <div className={`tecos-admin-topbar tecos-admin-topbar--responsive${isMobile ? ' tecos-admin-topbar--mobile' : ''}`} style={{
      display: 'flex',
      justifyContent: 'space-between',
      borderBottom: `1px solid ${theme.border}`,
      background: theme.bg, position: 'sticky', top: 0, zIndex: 50, backdropFilter: 'blur(10px)',
    }}>
      <div className="tecos-admin-topbar__primary" style={{
        display: 'flex', alignItems: 'flex-start', gap: 10, flex: 1, minWidth: 0,
      }}>
        {onMenuToggle && (
          <MobileMenuButton
            theme={theme}
            onClick={onMenuToggle}
            className="tecos-admin-topbar__menu-btn"
            style={{ marginTop: 2, flexShrink: 0 }}
          />
        )}
        <div className="tecos-admin-topbar__title" style={{ minWidth: 0, flex: 1 }}>
          <div className="tecos-admin-topbar__panel" style={{
            fontSize: 11, color: theme.textMute, letterSpacing: 2, fontWeight: 700,
            textTransform: 'uppercase', fontFamily: 'Inter, sans-serif',
          }}>{panelLabel}</div>
          <h1 className="tecos-admin-topbar__heading" style={{
            fontFamily: 'Bebas Neue, sans-serif',
            letterSpacing: 1, color: theme.text, margin: '4px 0 0', fontWeight: 400,
            lineHeight: 1.05,
          }}>{title}</h1>
          {subtitle && (
            <div className="tecos-admin-topbar__subtitle" style={{
              color: theme.textDim, fontSize: 13, marginTop: 2, fontFamily: 'Inter, sans-serif',
            }}>{subtitle}</div>
          )}
        </div>
        {isMobile && (
          <div className="tecos-admin-topbar__icons tecos-admin-topbar__icons--mobile" style={{ display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0 }}>
            {bell}
            {themeBtn}
          </div>
        )}
      </div>
      {isMobile && searchEnabled && (
        <div className="tecos-admin-topbar__search-row">
          {search}
        </div>
      )}
      {!isMobile && (
        <div className="tecos-admin-topbar__actions tecos-admin-topbar__actions--desktop" style={{ display: 'flex', gap: 10, alignItems: 'center', flexShrink: 0 }}>
          {search}
          {bell}
          {themeBtn}
        </div>
      )}
    </div>
  );
};

const iconBtn = (theme) => ({
  width: 40, height: 40, borderRadius: 10,
  background: theme.bgInput, border: `1px solid ${theme.border}`,
  color: theme.text, cursor: 'pointer', position: 'relative',
  display: 'grid', placeItems: 'center',
});

const dashboardNavigate = (onNav, target) => {
  if (!onNav || !target) return;
  const view = typeof target === 'string' ? target : target.nav;
  if (!view) return;
  onNav(view);
  const focusDetail = typeof target === 'object' && target
    ? {
      view,
      ...(target.comprobantesTab ? { comprobantesTab: target.comprobantesTab } : {}),
      ...(target.alumnosFilter || target.statusFilter
        ? { view: 'alumnos', statusFilter: target.alumnosFilter || target.statusFilter }
        : {}),
    }
    : null;
  if (focusDetail && (focusDetail.comprobantesTab || focusDetail.statusFilter)) {
    window.setTimeout(() => {
      window.dispatchEvent(new CustomEvent('tecos:admin-focus-entity', { detail: focusDetail }));
    }, 80);
  }
};

function calcDashboardTrend (allMonths, filtered) {
  if (!filtered?.length || !allMonths?.length) return null;
  const cur = sumDashboardMonths(filtered);
  const first = filtered[0].month;
  const len = filtered.length;
  const prev = allMonths.filter(m => m.month >= first - len && m.month < first);
  const prevSum = sumDashboardMonths(prev.length ? prev : []);
  if (!prevSum) return cur > 0 ? '+100%' : '0%';
  const pct = ((cur - prevSum) / prevSum) * 100;
  return `${pct >= 0 ? '+' : ''}${pct.toFixed(1)}%`;
}

const DashboardMonthFilter = ({
  theme, year, onYearChange, mode, onModeChange,
  rangeFrom, rangeTo, onRangeFrom, onRangeTo,
  selectedMonths, onToggleMonth, onSelectAllMonths, onClearMonths,
  onPresetRange,
}) => {
  const labels = typeof DASHBOARD_MONTH_LABELS !== 'undefined'
    ? DASHBOARD_MONTH_LABELS
    : ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'];
  const years = [new Date().getFullYear(), new Date().getFullYear() - 1];
  const monthOpts = labels.map((l, i) => ({ v: i + 1, l }));

  const pill = (active) => ({
    padding: '6px 12px', borderRadius: 8, border: `1px solid ${active ? theme.primary : theme.border}`,
    background: active ? `${theme.primary}22` : theme.bgInput,
    color: active ? theme.primary : theme.textDim,
    fontSize: 12, fontWeight: 600, cursor: 'pointer', fontFamily: 'Inter, sans-serif',
  });

  return (
    <div className="tecos-dash-month-filter" style={{ display: 'grid', gap: 12, marginBottom: 16 }}>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' }}>
        <span style={{ fontSize: 11, color: theme.textMute, fontWeight: 700, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>Año</span>
        <select value={year} onChange={e => onYearChange(Number(e.target.value))} style={{
          padding: '6px 10px', borderRadius: 8, border: `1px solid ${theme.border}`,
          background: theme.bgInput, color: theme.text, fontSize: 12, fontFamily: 'Inter, sans-serif',
        }}>
          {years.map(y => <option key={y} value={y}>{y}</option>)}
        </select>
        <button type="button" style={pill(mode === 'rango')} onClick={() => onModeChange('rango')}>Rango de meses</button>
        <button type="button" style={pill(mode === 'meses')} onClick={() => onModeChange('meses')}>Meses sueltos</button>
      </div>
      {mode === 'rango' ? (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' }}>
          {onPresetRange && [
            { l: 'Ene–Mar', f: 1, t: 3 }, { l: 'Abr–Jun', f: 4, t: 6 },
            { l: 'Jul–Sep', f: 7, t: 9 }, { l: 'Oct–Dic', f: 10, t: 12 },
            { l: 'Año completo', f: 1, t: 12 },
          ].map((p) => (
            <button key={p.l} type="button" onClick={() => onPresetRange(p.f, p.t)} style={{
              padding: '4px 10px', borderRadius: 6, border: `1px solid ${theme.border}`,
              background: theme.bgCard, fontSize: 11, cursor: 'pointer', color: theme.textDim,
            }}>{p.l}</button>
          ))}
          <span style={{ fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>Del mes</span>
          <select value={rangeFrom} onChange={e => onRangeFrom(Number(e.target.value))} style={{
            padding: '6px 10px', borderRadius: 8, border: `1px solid ${theme.border}`,
            background: theme.bgInput, color: theme.text, fontSize: 12,
          }}>
            {monthOpts.map(m => <option key={m.v} value={m.v}>{m.v} — {m.l}</option>)}
          </select>
          <span style={{ fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>al mes</span>
          <select value={rangeTo} onChange={e => onRangeTo(Number(e.target.value))} style={{
            padding: '6px 10px', borderRadius: 8, border: `1px solid ${theme.border}`,
            background: theme.bgInput, color: theme.text, fontSize: 12,
          }}>
            {monthOpts.map(m => <option key={m.v} value={m.v}>{m.v} — {m.l}</option>)}
          </select>
        </div>
      ) : (
        <div style={{ display: 'grid', gap: 8 }}>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {onSelectAllMonths && (
              <button type="button" onClick={onSelectAllMonths} style={{
                padding: '4px 10px', borderRadius: 6, border: `1px solid ${theme.primary}`,
                background: `${theme.primary}18`, fontSize: 11, cursor: 'pointer', color: theme.primary, fontWeight: 600,
              }}>Todos los meses</button>
            )}
            {onClearMonths && (
              <button type="button" onClick={onClearMonths} style={{
                padding: '4px 10px', borderRadius: 6, border: `1px solid ${theme.border}`,
                background: theme.bgInput, fontSize: 11, cursor: 'pointer', color: theme.textDim,
              }}>Ninguno</button>
            )}
          </div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {monthOpts.map(m => {
            const on = selectedMonths.includes(m.v);
            return (
              <button key={m.v} type="button" onClick={() => onToggleMonth(m.v)} style={{
                padding: '6px 10px', borderRadius: 8, border: `1px solid ${on ? theme.primary : theme.border}`,
                background: on ? theme.primary : theme.bgInput,
                color: on ? '#fff' : theme.textDim,
                fontSize: 11, fontWeight: 700, cursor: 'pointer', fontFamily: 'Inter, sans-serif',
              }}>{m.l}</button>
            );
          })}
          </div>
        </div>
      )}
    </div>
  );
};

/* SVG estático (respaldo); la gráfica principal usa Chart.js en dashboard-interactive-chart.jsx */
const DashboardRevenueChart = ({ theme, months, height = 220 }) => {
  const data = months?.length ? months : [];
  const totals = data.map(m => Number(m.total) || 0);
  const pagosVals = data.map(m => Number(m.pagos) || 0);
  const tiendaVals = data.map(m => Number(m.tienda) || 0);
  const max = Math.max(...totals, 1) * 1.15;
  const padL = 44;
  const padR = 16;
  const w = 600;
  const chartW = w - padL - padR;
  const barW = data.length ? Math.min(36, Math.max(14, chartW / data.length - 8)) : 24;
  const gap = data.length ? (chartW - barW * data.length) / Math.max(data.length, 1) : 0;

  if (!data.length) {
    return (
      <div style={{ height, display: 'grid', placeItems: 'center', color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>
        Sin ingresos registrados en el periodo seleccionado.
      </div>
    );
  }

  const yAt = (v) => 200 - (v / max) * 180;
  const xBar = (i) => padL + gap / 2 + i * (barW + gap);

  return (
    <div style={{ height, position: 'relative' }}>
      <svg viewBox={`0 0 ${w} 220`} style={{ width: '100%', height: '100%' }}>
        {[0, 1, 2, 3, 4].map(i => (
          <line key={i} x1={padL} x2={w - padR} y1={20 + i * 40} y2={20 + i * 40} stroke={theme.border} strokeWidth="1" />
        ))}
        {data.map((m, i) => {
          const x = xBar(i);
          const tienda = tiendaVals[i];
          const pagos = pagosVals[i];
          const yTop = yAt(tienda + pagos);
          const ySplit = yAt(pagos);
          return (
            <g key={m.month}>
              {tienda > 0 && (
                <rect x={x} y={ySplit} width={barW} height={200 - ySplit} rx="4" fill={theme.accent} opacity="0.85" />
              )}
              {pagos > 0 && (
                <rect x={x} y={yTop} width={barW} height={Math.max(0, ySplit - yTop)} rx="4" fill={theme.primary} opacity="0.9" />
              )}
              <text x={x + barW / 2} y={216} textAnchor="middle" fill={theme.textMute} fontSize="10" fontFamily="Inter, sans-serif" fontWeight="600">{m.label}</text>
            </g>
          );
        })}
      </svg>
      <div style={{ display: 'flex', gap: 16, marginTop: 8, fontSize: 11, fontFamily: 'Inter, sans-serif', color: theme.textDim }}>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ width: 10, height: 10, borderRadius: 2, background: theme.primary }} /> Mensualidades
        </span>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ width: 10, height: 10, borderRadius: 2, background: theme.accent }} /> Tienda en línea
        </span>
      </div>
    </div>
  );
};

/** Unifica stats del API con el objeto que usan los paneles del dashboard. */
function mergeDashboardFinance (stats) {
  if (!stats) {
    return {
      activos: 0, pct: {}, alumnosPagaronMes: 0, alumnosConAdeudo: 0, totalAdeudoMes: 0,
      mesesAtrasados: 0, mesesUrgentes: 0, ingresoMensualidadesMes: 0, ingresoTiendaMes: 0,
      ingresoTotalPagadoMes: 0, ingresoAcumuladoHistorico: 0, egresosMes: 0, balanceMes: 0,
    };
  }
  const f = stats.finance || {};
  return {
    ...f,
    activos: f.activos ?? stats.activos ?? 0,
    alumnosPagaronMes: f.alumnosPagaronMes ?? 0,
    alumnosConAdeudo: f.alumnosConAdeudo ?? stats.alumnosConAdeudo ?? 0,
    totalAdeudoMes: f.totalAdeudoMes ?? stats.totalAdeudoMes ?? 0,
    mesesAtrasados: f.mesesAtrasados ?? stats.mesesAtrasados ?? 0,
    mesesUrgentes: f.mesesUrgentes ?? stats.mesesUrgentes ?? 0,
    ingresoMensualidadesMes: f.ingresoMensualidadesMes ?? stats.ingresosMensualidadesMes ?? 0,
    ingresoTiendaMes: f.ingresoTiendaMes ?? stats.ingresosTiendaMes ?? 0,
    ingresoTotalPagadoMes: f.ingresoTotalPagadoMes ?? stats.pagadoMes ?? 0,
    ingresoAcumuladoHistorico: f.ingresoAcumuladoHistorico ?? stats.ingresoAcumuladoHistorico ?? 0,
    ingresoAcumuladoMensualidades: f.ingresoAcumuladoMensualidades ?? stats.ingresoAcumuladoMensualidades ?? 0,
    ingresoAcumuladoTienda: f.ingresoAcumuladoTienda ?? stats.ingresoAcumuladoTienda ?? 0,
    egresoAcumuladoHistorico: f.egresoAcumuladoHistorico ?? stats.egresoAcumuladoHistorico ?? 0,
    egresoAcumuladoNomina: f.egresoAcumuladoNomina ?? stats.egresoAcumuladoNomina ?? 0,
    egresoAcumuladoGastos: f.egresoAcumuladoGastos ?? stats.egresoAcumuladoGastos ?? 0,
    egresoAcumuladoRetiros: f.egresoAcumuladoRetiros ?? stats.egresoAcumuladoRetiros ?? 0,
    ingresoMensualidadesActivosPagaron: f.ingresoMensualidadesActivosPagaron ?? 0,
    recaudadoTiendaAnio: stats.recaudadoTiendaAnio ?? 0,
    ordenesPend: stats.ordenesPend ?? 0,
    egresosMes: f.egresosMes ?? stats.egresosMes ?? 0,
    egresosMesGastos: f.egresosMesGastos ?? stats.egresosMesGastos ?? 0,
    egresosMesNomina: f.egresosMesNomina ?? stats.egresosMesNomina ?? 0,
    egresosMesRetiros: f.egresosMesRetiros ?? stats.egresosMesRetiros ?? 0,
    balanceMes: (() => {
      const ing = Number(f.ingresoTotalPagadoMes ?? stats.pagadoMes) || 0;
      const egr = Number(f.egresosMes ?? stats.egresosMes) || 0;
      const stored = f.balanceMes ?? stats.balanceMes;
      if (stored != null && !Number.isNaN(Number(stored))) return Number(stored);
      return Math.round((ing - egr) * 100) / 100;
    })(),
    egresosMesGastosCount: f.egresosMesGastosCount ?? 0,
    egresosMesNominaCount: f.egresosMesNominaCount ?? 0,
    egresosMesRetirosCount: f.egresosMesRetirosCount ?? 0,
    egresosMesMovimientosCount: f.egresosMesMovimientosCount ?? 0,
    loadedAt: stats.loadedAt ?? null,
    tramoHoy: f.tramoHoy ?? '—',
    billingHint: stats.billingHint ?? f.billingHint,
    monthlyFee: stats.billingSettings?.monthly_fee,
    pct: f.pct || {},
  };
}

const useNarrowDashboard = (breakpoint = 1080) => {
  const [narrow, setNarrow] = React.useState(
    () => (typeof window !== 'undefined' ? window.innerWidth < breakpoint : false)
  );
  React.useEffect(() => {
    const onResize = () => setNarrow(window.innerWidth < breakpoint);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, [breakpoint]);
  return narrow;
};

const AdminDashboard = ({ theme, cardVariant = 'standard', onNav }) => {
  const isMobile = useIsMobile();
  const now = new Date();
  const [stats, setStats] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [chartYear, setChartYear] = React.useState(now.getFullYear());
  const [filterMode, setFilterMode] = React.useState('rango');
  const [rangeFrom, setRangeFrom] = React.useState(Math.max(1, now.getMonth() + 1 - 5));
  const [rangeTo, setRangeTo] = React.useState(now.getMonth() + 1);
  const [selectedMonths, setSelectedMonths] = React.useState([]);
  const [loadError, setLoadError] = React.useState('');
  const [exportBusy, setExportBusy] = React.useState('');
  const [refreshing, setRefreshing] = React.useState(false);
  const [periodDetailOpen, setPeriodDetailOpen] = React.useState(false);
  const [invResetOpen, setInvResetOpen] = React.useState(false);
  const [invResetBusy, setInvResetBusy] = React.useState(false);
  const [invResetErr, setInvResetErr] = React.useState('');
  const [invPwOpen, setInvPwOpen] = React.useState(false);
  const [invPwBusy, setInvPwBusy] = React.useState(false);
  const [invPwErr, setInvPwErr] = React.useState('');
  const cancelLoadRef = React.useRef({ cancelled: false });
  const DashInvResetModal = (typeof window !== 'undefined' && window.InventoryResetHistoryModal) || (() => null);
  const DashInvPwModal = (typeof window !== 'undefined' && window.InventoryResetPasswordModal) || (() => null);
  const DashInvResetBtn = (typeof window !== 'undefined' && window.InventoryResetUnlockedButton) || (() => null);
  const { unlocked: invResetUnlocked, unlock: invResetUnlock, lock: invResetLock } = useInventoryResetUnlock();
  const [chartSeries, setChartSeries] = React.useState(
    () => ({ ...(typeof DEFAULT_DASHBOARD_CHART_SERIES === 'object' ? DEFAULT_DASHBOARD_CHART_SERIES : {}) }),
  );

  const loadStats = React.useCallback(async ({ silent = false } = {}) => {
    if (!silent) setLoading(true);
    else setRefreshing(true);
    setLoadError('');
    if (!isSupabaseReady()) {
      const st = typeof getSupabaseConfigStatus === 'function' ? getSupabaseConfigStatus() : null;
      if (!cancelLoadRef.current.cancelled) {
        setStats(null);
        setLoadError(st?.message || 'Supabase no configurado (revisa config.js).');
        setLoading(false);
        setRefreshing(false);
      }
      return;
    }
    try {
      const s = await fetchAdminDashboardStats(chartYear);
      if (!cancelLoadRef.current.cancelled) {
        setStats(s);
        window.__tecosNominaHighlight = (s?.nominaPagoManana?.length || 0) > 0;
        const meta = s?._meta || {};
        if (meta.paymentsTotal === 0 && !meta.loadWarnings?.length) {
          setLoadError('No se leyeron pagos. Entra como administrador con correo y contraseña (no solo el acceso rápido).');
        } else if (meta.paymentsPagado === 0 && meta.paymentsEnRevision > 0) {
          setLoadError(`Hay ${meta.paymentsEnRevision} comprobante(s) en revisión. Aprueba en Verificar comprobantes para que aparezcan como ingreso.`);
        }
      }
    } catch (e) {
      console.error('[Tecos] dashboard', e);
      if (!cancelLoadRef.current.cancelled) {
        setStats(null);
        setLoadError(e.message || 'Error al cargar el dashboard');
      }
    }
    if (!cancelLoadRef.current.cancelled) {
      setLoading(false);
      setRefreshing(false);
    }
  }, [chartYear]);

  React.useEffect(() => {
    cancelLoadRef.current.cancelled = false;
    loadStats({ silent: false });
    return () => { cancelLoadRef.current.cancelled = true; };
  }, [loadStats]);

  React.useEffect(() => {
    const onSync = () => loadStats({ silent: true });
    window.addEventListener('tecos:academy-data-sync', onSync);
    window.addEventListener('tecos:payments-changed', onSync);
    window.addEventListener('tecos:comprobantes-changed', onSync);
    window.addEventListener('tecos:orders-changed', onSync);
    window.addEventListener('tecos:students-changed', onSync);
    window.addEventListener('tecos:finance-changed', onSync);
    window.addEventListener('tecos:settings-changed', onSync);
    window.addEventListener('tecos:inventory-history-reset', onSync);
    const onVisible = () => {
      if (document.visibilityState === 'visible') loadStats({ silent: true });
    };
    document.addEventListener('visibilitychange', onVisible);
    return () => {
      window.removeEventListener('tecos:academy-data-sync', onSync);
      window.removeEventListener('tecos:payments-changed', onSync);
      window.removeEventListener('tecos:comprobantes-changed', onSync);
      window.removeEventListener('tecos:orders-changed', onSync);
      window.removeEventListener('tecos:students-changed', onSync);
      window.removeEventListener('tecos:finance-changed', onSync);
      window.removeEventListener('tecos:settings-changed', onSync);
      window.removeEventListener('tecos:inventory-history-reset', onSync);
      document.removeEventListener('visibilitychange', onVisible);
    };
  }, [loadStats]);

  const openInvReset = () => { setInvResetErr(''); setInvResetOpen(true); };
  const openInvPw = () => { setInvPwErr(''); setInvPwOpen(true); };
  const verifyInvPw = async (password) => {
    if (typeof verifyAdminSessionPassword !== 'function') {
      setInvPwErr('Verificación no disponible. Recarga la página.');
      return;
    }
    setInvPwBusy(true);
    setInvPwErr('');
    try {
      await verifyAdminSessionPassword(password);
      invResetUnlock();
      setInvPwOpen(false);
    } catch (e) {
      setInvPwErr(e.message || 'Contraseña incorrecta.');
    }
    setInvPwBusy(false);
  };

  const runInvResetFromDash = async () => {
    if (typeof resetInventoryMovementsHistory !== 'function') {
      setInvResetErr('Función no cargada. Recarga la página.');
      return;
    }
    setInvResetBusy(true);
    setInvResetErr('');
    try {
      await resetInventoryMovementsHistory();
      setInvResetOpen(false);
      if (typeof window !== 'undefined') {
        window.dispatchEvent(new CustomEvent('tecos:products-changed'));
        window.dispatchEvent(new CustomEvent('tecos:inventory-history-reset'));
      }
      loadStats({ silent: true });
    } catch (e) {
      setInvResetErr(e.message || 'No se pudo reiniciar el historial.');
    }
    setInvResetBusy(false);
  };

  React.useEffect(() => {
    if (!periodDetailOpen) return undefined;
    const id = window.setInterval(() => loadStats({ silent: true }), 30000);
    return () => window.clearInterval(id);
  }, [periodDetailOpen, loadStats]);

  const s = stats || {
    totalStudents: 0, activos: 0, pendientes: 0, vencidos: 0,
    alumnosConAdeudo: 0, totalAdeudoMes: 0, mesesAtrasados: 0, mesesUrgentes: 0,
    pagadoMes: 0, ordenesPend: 0, eventosProximos: 0, recentPayments: [],
    monthlySeries: [],     hero: {
      ingresosHoy: 0, ingresosMensualidadesHoy: 0, ingresosTiendaHoy: 0,
      nuevosMes: 0, tasaActivos: 0, comprobantesPend: 0,
    },
    alerts: [], recentOrders: [], upcomingEvents: [],
    ingresosMensualidadesMes: 0, ingresosTiendaMes: 0, recaudadoAnio: 0,
    recaudadoMensualidadesAnio: 0, recaudadoTiendaAnio: 0,
    ingresoAcumuladoHistorico: 0, egresosMes: 0, balanceMes: 0, finance: { activos: 0, pct: {} },
  };

  const fin = mergeDashboardFinance(stats);
  const pct = (n) => (fin.pct && fin.pct[n] != null ? `${fin.pct[n]}%` : '—');
  const billingSettings = stats?.billingSettings || null;

  const hero = s.hero || {
    ingresosHoy: 0, ingresosMensualidadesHoy: 0, ingresosTiendaHoy: 0,
    nuevosMes: 0, tasaActivos: 0, comprobantesPend: 0,
  };
  const fmtMoney = (n) => `$${(Number(n) || 0).toLocaleString('es-MX')}`;
  const allMonths = s.monthlySeries || [];
  const chartFilter = filterMode === 'meses'
    ? { mode: 'meses', selectedMonths }
    : { mode: 'rango', rangeFrom: Math.min(rangeFrom, rangeTo), rangeTo: Math.max(rangeFrom, rangeTo) };
  const chartMonths = filterDashboardMonths(allMonths, chartFilter);
  const chartTotal = sumDashboardMonths(chartMonths);
  const chartPagos = chartMonths.reduce((n, m) => n + (Number(m.pagos) || 0), 0);
  const chartTienda = chartMonths.reduce((n, m) => n + (Number(m.tienda) || 0), 0);
  const trend = calcDashboardTrend(allMonths, chartMonths);

  const toggleMonth = (m) => {
    setSelectedMonths(prev => (prev.includes(m) ? prev.filter(x => x !== m) : [...prev, m].sort((a, b) => a - b)));
  };

  const toggleChartSeries = (id) => {
    setChartSeries((prev) => ({ ...prev, [id]: !prev[id] }));
  };

  const selectAllChartSeries = () => {
    const next = {};
    (typeof DASHBOARD_CHART_SERIES !== 'undefined' ? DASHBOARD_CHART_SERIES : []).forEach((s) => { next[s.id] = true; });
    setChartSeries(next);
  };

  const clearChartSeries = () => {
    const next = {};
    (typeof DASHBOARD_CHART_SERIES !== 'undefined' ? DASHBOARD_CHART_SERIES : []).forEach((s) => { next[s.id] = false; });
    setChartSeries(next);
  };

  const alertColor = (c) => ({
    danger: theme.danger, warning: theme.warning, info: theme.info, urgent: theme.urgent,
  }[c] || theme.primary);

  const adeudoCount = s.alumnosConAdeudo ?? s.pendientes ?? 0;
  const atrasoMeses = (s.mesesAtrasados ?? 0) + (s.mesesUrgentes ?? 0);

  const incomeSplitMetrics = [
    {
      l: 'Mensualidades (mes)',
      v: fmtMoney(s.ingresosMensualidadesMes),
      d: `Cobrado hoy ${fmtMoney(hero.ingresosMensualidadesHoy)} · Año ${fmtMoney(s.recaudadoMensualidadesAnio)}`,
      i: 'money',
      c: theme.primary,
      nav: 'pagos',
      highlight: true,
    },
    {
      l: 'Tienda en línea',
      v: fmtMoney(s.ingresosTiendaMes),
      d: `Confirmadas · hoy ${fmtMoney(hero.ingresosTiendaHoy)} · año ${fmtMoney(s.recaudadoTiendaAnio)}`,
      i: 'cart',
      c: theme.accent,
      nav: 'comprobantes',
      comprobantesTab: 'tienda',
      highlight: true,
    },
  ];

  const metrics = [
    {
      l: 'Ingresos del mes (total)',
      v: `$${(s.pagadoMes || 0).toLocaleString('es-MX')}`,
      d: 'Mensualidades del mes en curso ya pagadas + tienda cobrada en el mes',
      i: 'chart',
      c: theme.success,
      nav: 'pagos',
    },
    {
      l: 'Adeudo total (mes)',
      v: `$${(s.totalAdeudoMes || 0).toLocaleString('es-MX')}`,
      d: `${adeudoCount} alumno(s) con adeudo`,
      i: 'warning',
      c: adeudoCount ? theme.danger : theme.success,
      nav: 'alumnos',
      alumnosFilter: adeudoCount ? 'adeudo' : undefined,
    },
    {
      l: 'Recaudado ' + chartYear,
      v: `$${(s.recaudadoAnio || 0).toLocaleString('es-MX')}`,
      d: 'mensualidades pagadas + tienda confirmada',
      i: 'chart',
      c: theme.primary,
      nav: 'pagos',
    },
    { l: 'Órdenes tienda', v: String(s.ordenesPend), d: 'por confirmar (no suman ingreso)', i: 'cart', c: theme.accent, nav: 'comprobantes', comprobantesTab: 'tienda' },
    { l: 'Alumnos activos', v: String(s.activos), d: `${s.totalStudents} registrados`, i: 'users', c: theme.primary, nav: 'alumnos' },
    {
      l: 'Mensualidades atrasadas',
      v: String(atrasoMeses),
      d: s.mesesUrgentes ? `${s.mesesUrgentes} urgentes` : 'sin atraso grave',
      i: 'clock',
      c: atrasoMeses ? theme.warning : theme.success,
      nav: 'pagos',
    },
    { l: 'Ingresos hoy', v: `$${hero.ingresosHoy.toLocaleString('es-MX')}`, d: 'mensualidades + tienda cobradas hoy', i: 'flame', c: theme.info, nav: 'pagos' },
    { l: 'Por revisar', v: String(hero.comprobantesPend), d: 'comprobantes', i: 'doc', c: theme.urgent, nav: 'comprobantes' },
  ];

  const recentPay = s.recentPayments?.length ? s.recentPayments : [];
  const alerts = s.alerts?.length ? s.alerts : [];
  const upcoming = s.upcomingEvents?.length ? s.upcomingEvents : [];
  const recentOrders = s.recentOrders?.length ? s.recentOrders : [];

  const meta = s._meta || {};
  const dashNav = (target) => dashboardNavigate(onNav, target);
  const currentMonth = typeof describeDashboardCurrentMonth === 'function'
    ? describeDashboardCurrentMonth(now)
    : { label: '', shortLabel: '', calendarRange: '' };
  const chartPeriodLabel = typeof describeChartFilter === 'function'
    ? describeChartFilter(chartFilter, chartYear)
    : `Año ${chartYear}`;

  const dashPad = isMobile ? '12px 14px' : 28;
  const dashGap = isMobile ? 14 : 20;
  const chartCardPad = isMobile ? 14 : 22;
  const chartHeight = isMobile ? 260 : 460;
  const panelBox = isMobile ? { width: '100%', maxWidth: '100%', minWidth: 0, boxSizing: 'border-box' } : undefined;

  return (
    <div
      className={`tecos-page-shell tecos-dash${isMobile ? ' tecos-dash--mobile' : ''}`}
      style={{
        padding: dashPad,
        display: 'grid',
        gap: dashGap,
        width: '100%',
        maxWidth: '100%',
        minWidth: 0,
        boxSizing: 'border-box',
      }}
    >
      <div className="tecos-dash-live-banner" style={{
        padding: '12px 16px', borderRadius: 10,
        background: loadError ? `${theme.warning}18` : `${theme.success}12`,
        border: `1px solid ${loadError ? theme.warning : theme.success}44`,
        display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center', justifyContent: 'space-between',
        width: '100%', maxWidth: '100%', minWidth: 0, boxSizing: 'border-box',
      }}>
        <div
          style={{ fontSize: 13, fontFamily: 'Inter, sans-serif', color: loadError ? theme.warning : theme.textDim, flex: 1, minWidth: 0, userSelect: 'none' }}
          onClick={(e) => typeof onInventoryResetRevealGesture === 'function' && onInventoryResetRevealGesture(e, {
            unlocked: invResetUnlocked,
            onNeedPassword: openInvPw,
            onOpenReset: openInvReset,
          })}
        >
          {loadError || (
            <>Datos en vivo: <strong>{meta.paymentsTotal ?? 0}</strong> pago(s) · <strong>{meta.paymentsPagado ?? 0}</strong> mens. confirmadas · <strong>{meta.paymentsEnRevision ?? 0}</strong> mens. en revisión · <strong>{meta.ordersLoaded ?? 0}</strong> orden(es) tienda · <strong>{meta.ordersConfirmadas ?? 0}</strong> confirmadas</>
          )}
        </div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
          {invResetUnlocked && (
            <DashInvResetBtn
              theme={theme}
              onClick={openInvReset}
              busy={invResetBusy}
              onLock={invResetLock}
            />
          )}
          <Btn theme={theme} kind="ghost" size="sm" icon="history" onClick={() => loadStats({ silent: true })} disabled={loading || refreshing}>
            {refreshing ? 'Actualizando…' : 'Actualizar'}
          </Btn>
        </div>
      </div>

      {typeof DashboardFinancialClearBanner === 'function' && stats && (
        <DashboardFinancialClearBanner
          theme={theme}
          stats={stats}
          onCleared={() => loadStats({ silent: true })}
        />
      )}

      <DashInvPwModal
        theme={theme}
        open={invPwOpen}
        onClose={() => !invPwBusy && setInvPwOpen(false)}
        onVerify={verifyInvPw}
        busy={invPwBusy}
        err={invPwErr}
      />

      <DashInvResetModal
        theme={theme}
        open={invResetOpen}
        onClose={() => !invResetBusy && setInvResetOpen(false)}
        onConfirm={runInvResetFromDash}
        busy={invResetBusy}
        err={invResetErr}
      />

      {typeof DashboardPortalSyncBanner === 'function' && (
        <DashboardPortalSyncBanner theme={theme} fin={fin} meta={meta} billingSettings={billingSettings} onNav={dashNav} />
      )}

      {typeof DashboardPeriodOverview === 'function' && (
        <DashboardPeriodOverview
          theme={theme}
          currentMonth={currentMonth}
          fin={fin}
          meta={meta}
          fmtMoney={fmtMoney}
          chartFilter={chartFilter}
          chartYear={chartYear}
          chartMonths={chartMonths}
          chartTotal={chartTotal}
          chartPagos={chartPagos}
          chartTienda={chartTienda}
          loading={loading && !stats}
          refreshing={refreshing}
          open={periodDetailOpen}
          onOpenChange={setPeriodDetailOpen}
          onNav={dashNav}
          invResetUnlocked={invResetUnlocked}
          onInvResetNeedPassword={openInvPw}
          onInvResetLock={invResetLock}
          onResetInventoryHistory={openInvReset}
        />
      )}

      {typeof DashboardKpiGrid === 'function' && (
        <DashboardKpiGrid
          theme={theme}
          fin={fin}
          fmtMoney={fmtMoney}
          pct={pct}
          currentMonth={currentMonth}
          loading={loading && !stats}
          onNav={dashNav}
        />
      )}

      <div className="tecos-dash-split">
        {typeof DashboardAlumnosCobranza === 'function'
          ? <DashboardAlumnosCobranza theme={theme} fin={fin} fmtMoney={fmtMoney} onNav={dashNav} loading={loading} currentMonth={currentMonth} />
          : null}
        {typeof DashboardIngresosEgresos === 'function'
          ? <DashboardIngresosEgresos theme={theme} fin={fin} fmtMoney={fmtMoney} pct={pct} onNav={dashNav} loading={loading} currentMonth={currentMonth} />
          : null}
      </div>

      <div className="tecos-dash-chart-card">
      <Card theme={theme} style={{ padding: chartCardPad, maxWidth: '100%', overflow: 'hidden', ...panelBox }}>
        <div className="tecos-dash-chart-head" style={{
          display: 'flex',
          flexDirection: isMobile ? 'column' : 'row',
          justifyContent: 'space-between',
          alignItems: isMobile ? 'stretch' : 'flex-start',
          marginBottom: 12,
          flexWrap: 'wrap',
          gap: 12,
        }}>
          <div className="tecos-dash-chart-head__intro" style={{ flex: 1, minWidth: 0, maxWidth: '100%' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', marginBottom: 6 }}>
              <h3 style={{
                fontFamily: 'Bebas Neue, sans-serif',
                fontSize: isMobile ? 22 : 28,
                letterSpacing: 1,
                color: theme.text,
                margin: 0,
                fontWeight: 400,
              }}>GRÁFICA INTERACTIVA</h3>
              {typeof DashboardPeriodBadge === 'function' && (
                <DashboardPeriodBadge theme={theme} label={chartPeriodLabel} variant="chart" />
              )}
            </div>
            <p style={{ color: theme.textDim, fontSize: 12, marginTop: 4, fontFamily: 'Inter, sans-serif', maxWidth: '100%', lineHeight: 1.5 }}>
              Sumando <strong style={{ color: theme.text }}>{chartMonths.length} mes(es)</strong> del año {chartYear}.
              Ingresos del periodo: <strong style={{ color: theme.success }}>{fmtMoney(chartTotal)}</strong>
              {' '}(mens. {fmtMoney(chartPagos)} + tienda {fmtMoney(chartTienda)}).
              Los KPIs de arriba usan <strong style={{ color: theme.primary }}>{currentMonth.label}</strong>, no este filtro.
            </p>
          </div>
          <div className="tecos-dash-chart-head__actions" style={{
            display: 'flex',
            flexDirection: isMobile ? 'column' : 'row',
            gap: 8,
            alignItems: isMobile ? 'stretch' : 'center',
            flexWrap: 'wrap',
            width: isMobile ? '100%' : 'auto',
          }}>
            {trend && <Badge theme={theme} color={trend.startsWith('-') ? 'danger' : 'success'}>{trend} vs periodo ant.</Badge>}
            {!isMobile && (
              <span style={{ fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>
                Total ingresos periodo: <strong style={{ color: theme.text }}>${chartTotal.toLocaleString('es-MX')}</strong>
              </span>
            )}
            <Btn theme={theme} kind="soft" icon="download" disabled={!!exportBusy} style={isMobile ? { width: '100%', justifyContent: 'center' } : undefined} onClick={async () => {
              setExportBusy('pdf');
              try { await exportDashboardFinancePdf(s, chartFilter, chartYear, theme, chartSeries); }
              catch (e) { alert(e.message); }
              setExportBusy('');
            }}>{exportBusy === 'pdf' ? 'PDF…' : 'PDF'}</Btn>
            <Btn theme={theme} kind="soft" icon="download" disabled={!!exportBusy} style={isMobile ? { width: '100%', justifyContent: 'center' } : undefined} onClick={async () => {
              setExportBusy('xlsx');
              try { await exportDashboardFinanceExcel(s, chartFilter, chartYear, theme, chartSeries); }
              catch (e) { alert(e.message); }
              setExportBusy('');
            }}>{exportBusy === 'xlsx' ? 'Excel…' : 'Excel'}</Btn>
          </div>
        </div>
        <DashboardMonthFilter
          theme={theme}
          year={chartYear}
          onYearChange={setChartYear}
          mode={filterMode}
          onModeChange={setFilterMode}
          rangeFrom={rangeFrom}
          rangeTo={rangeTo}
          onRangeFrom={setRangeFrom}
          onRangeTo={setRangeTo}
          selectedMonths={selectedMonths}
          onToggleMonth={toggleMonth}
          onSelectAllMonths={() => setSelectedMonths([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])}
          onClearMonths={() => setSelectedMonths([])}
          onPresetRange={(f, t) => { setFilterMode('rango'); setRangeFrom(f); setRangeTo(t); }}
        />
        {typeof DashboardSeriesChecklist === 'function' && (
          <div style={{ marginTop: 14 }}>
            <DashboardSeriesChecklist
              theme={theme}
              seriesState={chartSeries}
              months={chartMonths}
              onToggle={toggleChartSeries}
              onSelectAll={selectAllChartSeries}
              onClearAll={clearChartSeries}
            />
          </div>
        )}
        <div className="tecos-dash-chart-wrap tecos-dash-scroll-x">
          <p className="tecos-dash-scroll-hint" aria-hidden="true">Desliza horizontalmente si la gráfica no cabe</p>
          {loading ? <LoadingBlock theme={theme} label="Cargando gráfica…" /> : (
            typeof DashboardInteractiveFinanceChart === 'function'
              ? <DashboardInteractiveFinanceChart theme={theme} months={chartMonths} seriesState={chartSeries} height={chartHeight} />
              : <DashboardRevenueChart theme={theme} months={chartMonths} height={320} />
          )}
        </div>
        {typeof DashboardChartLiveSummary === 'function' && !loading && (
          <DashboardChartLiveSummary theme={theme} months={chartMonths} seriesState={chartSeries} periodLabel={chartPeriodLabel} />
        )}
        {typeof DashboardChartMonthTable === 'function' && (
          <div style={{ marginTop: 18 }}>
            <DashboardChartMonthTable
              theme={theme}
              chartMonths={chartMonths}
              chartYear={chartYear}
              chartFilter={chartFilter}
              fmtMoney={fmtMoney}
              loading={loading}
            />
          </div>
        )}
      </Card>
      </div>

      <div className="tecos-dash-bottom">
      <Card theme={theme} style={panelBox}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 24, letterSpacing: 1, color: theme.text, margin: 0, fontWeight: 400 }}>ALERTAS IMPORTANTES</h3>
          <div style={{ display: 'grid', gap: 10, marginTop: 16 }}>
            {!loading && alerts.length === 0 && (
              <p style={{ color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>Sin alertas pendientes.</p>
            )}
            {alerts.map(a => {
              const c = alertColor(a.color);
              return (
                <button key={a.id} type="button" onClick={() => dashboardNavigate(onNav, a)} style={{
                  display: 'flex', alignItems: 'center', gap: 12, padding: 10,
                  borderRadius: 10, background: theme.bgInput, border: `1px solid ${theme.border}`,
                  cursor: onNav ? 'pointer' : 'default', width: '100%', textAlign: 'left',
                }}>
                  <div style={{ width: 36, height: 36, borderRadius: 8, background: `${c}22`, color: c, display: 'grid', placeItems: 'center' }}>
                    <Icon name={a.icon} size={16} />
                  </div>
                  <div style={{ flex: 1, fontSize: 13, color: theme.text, fontFamily: 'Inter, sans-serif' }}>{a.text}</div>
                  <Icon name="chev" size={14} style={{ color: theme.textMute }} />
                </button>
              );
            })}
          </div>
        </Card>
      </div>

      <div className="tecos-dash-activity">
        <Card theme={theme} style={panelBox}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, letterSpacing: 1, color: theme.text, margin: 0, marginBottom: 14, fontWeight: 400 }}>ÚLTIMOS PAGOS</h3>
          {!loading && recentPay.length === 0 && meta.paymentsEnRevision > 0 && (
            <p style={{ color: theme.warning, fontSize: 12, fontFamily: 'Inter, sans-serif', marginBottom: 10 }}>
              Hay {meta.paymentsEnRevision} comprobante(s) pendientes de aprobación; aún no cuentan como cobro.
            </p>
          )}
          {(loading ? [] : recentPay).length === 0 && !loading ? (
            <p style={{ color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>Sin cobros confirmados (estado pagado).</p>
          ) : (loading ? [{ n: '…', a: '—', d: 'Cargando', s: 'pagado', c: '' }] : recentPay.map(p => ({
            n: p.name, a: p.a || `$${Number(p.monto || 0).toLocaleString('es-MX')}`, d: p.d || p.mes, s: p.estado, c: p.concept,
          }))).map((p, i) => (
            <div key={i} className="tecos-dash-activity-row" style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 0', borderBottom: i < (loading ? 0 : recentPay.length) - 1 ? `1px solid ${theme.border}` : 'none' }}>
              <Avatar name={p.n} size={32} theme={theme} />
              <div className="tecos-dash-activity-row__main" style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, color: theme.text, fontWeight: 600, fontFamily: 'Inter, sans-serif' }}>{p.n}</div>
                <div style={{ fontSize: 10, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>{p.c || 'Mensualidad'} · {p.d}</div>
              </div>
              <div className="tecos-dash-activity-row__side" style={{ textAlign: 'right' }}>
                <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 18, color: theme.text, letterSpacing: 0.5 }}>{p.a}</div>
                <StatusPill status={p.s} theme={theme} />
              </div>
            </div>
          ))}
        </Card>

        <Card theme={theme} style={panelBox}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, letterSpacing: 1, color: theme.text, margin: 0, marginBottom: 14, fontWeight: 400 }}>PRÓXIMOS EVENTOS</h3>
          {!loading && upcoming.length === 0 && (
            <p style={{ color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>No hay eventos próximos.</p>
          )}
          {upcoming.map((e, i) => (
            <div key={e.id || i} className="tecos-dash-activity-row" style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 0', borderBottom: i < upcoming.length - 1 ? `1px solid ${theme.border}` : 'none' }}>
              <div style={{ width: 36, height: 36, borderRadius: 8, background: `${theme.primary}22`, color: theme.primary, display: 'grid', placeItems: 'center', flexShrink: 0 }}>
                <Icon name="cal" size={16} />
              </div>
              <div className="tecos-dash-activity-row__main" style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, color: theme.text, fontWeight: 600, fontFamily: 'Inter, sans-serif' }}>{e.title}</div>
                <div style={{ fontSize: 10, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>{e.when}{e.location ? ` · ${e.location}` : ''}</div>
              </div>
            </div>
          ))}
        </Card>

        <Card theme={theme} style={panelBox}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, letterSpacing: 1, color: theme.text, margin: 0, marginBottom: 14, fontWeight: 400 }}>ÓRDENES RECIENTES</h3>
          {!loading && recentOrders.length === 0 && (
            <p style={{ color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>Sin órdenes recientes.</p>
          )}
          {recentOrders.map((o, i) => (
            <div key={o.id || i} className="tecos-dash-activity-row" style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 0', borderBottom: i < recentOrders.length - 1 ? `1px solid ${theme.border}` : 'none' }}>
              <div style={{ width: 36, height: 36, borderRadius: 8, background: theme.bgInput, color: theme.primary, display: 'grid', placeItems: 'center', flexShrink: 0 }}>
                <Icon name="cart" size={16} />
              </div>
              <div className="tecos-dash-activity-row__main" style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, color: theme.text, fontWeight: 600, fontFamily: 'Inter, sans-serif' }}>{o.name}</div>
                <div style={{ fontSize: 10, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>
                  ${o.amount.toLocaleString('es-MX')} · {o.student}{o.when ? ` · ${o.when}` : ''}
                </div>
              </div>
              <StatusPill status={o.status} theme={theme} />
            </div>
          ))}
        </Card>
      </div>
    </div>
  );
};


const MetricCard = ({ m, theme, variant = 'standard' }) => {
  if (variant === 'minimal') {
    return (
      <Card theme={theme} hover style={{ padding: 16 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start' }}>
          <div>
            <div style={{ fontSize: 11, color: theme.textMute, letterSpacing: 1.2, fontWeight: 700, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>{m.l}</div>
            <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 34, color: theme.text, marginTop: 6, letterSpacing: 0.5, fontWeight: 400, lineHeight: 1 }}>{m.v}</div>
            <div style={{ fontSize: 11, color: theme.textDim, marginTop: 6, fontFamily: 'Inter, sans-serif' }}>{m.d}</div>
          </div>
          {m.t && <Badge theme={theme} color={m.t.includes('+') ? 'success' : 'danger'}>{m.t}</Badge>}
        </div>
      </Card>
    );
  }
  if (variant === 'gradient') {
    return (
      <Card theme={theme} hover style={{
        padding: 16, position: 'relative', overflow: 'hidden',
        background: `linear-gradient(135deg, ${m.c}22, ${theme.bgCard})`,
        borderColor: `${m.c}33`,
      }}>
        <div style={{
          position: 'absolute', top: -20, right: -20, width: 100, height: 100,
          borderRadius: '50%', background: `radial-gradient(circle, ${m.c}33, transparent 70%)`,
        }}></div>
        <div style={{ position: 'relative' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
            <div style={{ width: 36, height: 36, borderRadius: 8, background: m.c, color: '#fff', display: 'grid', placeItems: 'center' }}>
              <Icon name={m.i} size={18} />
            </div>
            {m.t && <Badge theme={theme} color={m.t.includes('+') ? 'success' : 'danger'}>{m.t}</Badge>}
          </div>
          <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 34, color: theme.text, letterSpacing: 0.5, fontWeight: 400, lineHeight: 1 }}>{m.v}</div>
          <div style={{ fontSize: 11, color: theme.textMute, letterSpacing: 1, fontWeight: 700, textTransform: 'uppercase', marginTop: 6, fontFamily: 'Inter, sans-serif' }}>{m.l}</div>
          <div style={{ fontSize: 11, color: theme.textDim, marginTop: 2, fontFamily: 'Inter, sans-serif' }}>{m.d}</div>
        </div>
      </Card>
    );
  }
  // standard
  return (
    <Card theme={theme} hover style={{ padding: 16 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
        <div style={{ width: 40, height: 40, borderRadius: 10, background: `${m.c}22`, color: m.c, display: 'grid', placeItems: 'center' }}>
          <Icon name={m.i} size={18} />
        </div>
        {m.t && <Badge theme={theme} color={m.t.includes('+') ? 'success' : m.t.includes('-') ? 'danger' : 'primary'}>{m.t}</Badge>}
      </div>
      <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 36, color: theme.text, letterSpacing: 0.5, fontWeight: 400, lineHeight: 1 }}>{m.v}</div>
      <div style={{ fontSize: 11, color: theme.textMute, letterSpacing: 1, fontWeight: 700, textTransform: 'uppercase', marginTop: 6, fontFamily: 'Inter, sans-serif' }}>{m.l}</div>
      <div style={{ fontSize: 11, color: theme.textDim, marginTop: 2, fontFamily: 'Inter, sans-serif' }}>{m.d}</div>
    </Card>
  );
};

Object.assign(window, { Sidebar, AdminTopbar, AdminDashboard, MetricCard });
