// Screens · T02 · StudentCards — Grid of 20 student tiles, teacher perspective.
// Materializes constitutional principle 8: opt-in estricto por estudiante para cualquier vista individual docente.
const { useState, useEffect } = React;

// ── SVG icons (no emoji — per constitutional constraint) ──────────────────
function LockIcon({ size = 12, stroke = 'currentColor', strokeWidth = 1.5, style }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke={stroke} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round"
      style={style} aria-hidden="true">
      <rect x="4" y="11" width="16" height="10" rx="2" />
      <path d="M8 11V7a4 4 0 1 1 8 0v4" />
      <circle cx="12" cy="16" r="1" fill={stroke} stroke="none" />
    </svg>
  );
}

function CheckIcon({ size = 12, stroke = 'currentColor', strokeWidth = 2, style }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke={stroke} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round"
      style={style} aria-hidden="true">
      <polyline points="20 6 9 17 4 12" />
    </svg>
  );
}

// ── Quadrant → visual tokens ───────────────────────────────────────────────
const QUADRANT_VISUAL = {
  'coherente-plus':  { bg: 'hsl(var(--atlas-accent-50))',   border: 'hsl(var(--atlas-accent-200))',   label: 'Coherente+', short: 'Coh+'  },
  'subestima':       { bg: 'hsl(var(--atlas-cool-50))',     border: 'hsl(var(--atlas-cool-200))',     label: 'Subestima',  short: 'Sub'   },
  'coherente-minus': { bg: 'hsl(var(--atlas-paper-100))',   border: 'hsl(var(--atlas-paper-300))',    label: 'Coherente−', short: 'Coh−'  },
  'riesgo':          { bg: 'hsl(var(--atlas-critical-50))', border: 'hsl(var(--atlas-critical-200))', label: 'Riesgo',     short: 'Riesgo'},
};

// Avatar tone per user — deterministic fallback from quadrant
function toneFromPeer(peer) {
  // Mariana explicit warm, Sofía accent, Diego cool — aligned with users fixture
  if (peer.id === 'mariana') return { bg: 'hsl(var(--atlas-warm-400))',   fg: T.paper0 };
  if (peer.id === 'sofia')   return { bg: 'hsl(var(--atlas-accent-500))', fg: 'white'   };
  if (peer.id === 'diego')   return { bg: 'hsl(var(--atlas-cool-500))',   fg: 'white'   };
  const q = peer.quadrant;
  if (q === 'coherente-plus')  return { bg: 'hsl(var(--atlas-accent-500))',   fg: 'white'  };
  if (q === 'subestima')       return { bg: 'hsl(var(--atlas-cool-500))',     fg: 'white'  };
  if (q === 'coherente-minus') return { bg: 'hsl(var(--atlas-paper-400))',    fg: T.ink8   };
  return { bg: 'hsl(var(--atlas-critical-500))', fg: 'white' };  // riesgo
}

function shortName(full) {
  const parts = full.split(' ');
  if (parts.length < 2) return full;
  return `${parts[0]} ${parts[1][0]}.`;
}

// ── Student card ──────────────────────────────────────────────────────────
function StudentCard({ peer, index, onClick }) {
  const [hov, setHov] = useState(false);
  const visual = QUADRANT_VISUAL[peer.quadrant] || QUADRANT_VISUAL['coherente-minus'];
  const tone = toneFromPeer(peer);

  return (
    <div
      onClick={() => onClick(peer)}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        position: 'relative',
        background: visual.bg,
        border: `1px solid ${visual.border}`,
        borderRadius: 10,
        height: 140,
        padding: '14px 12px 12px',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'space-between',
        cursor: 'pointer',
        transition: 'transform 150ms ease-out, box-shadow 150ms ease-out',
        transform: hov ? 'translateY(-2px)' : 'translateY(0)',
        boxShadow: hov ? 'var(--atlas-shadow-md)' : 'var(--atlas-shadow-sm)',
        animation: `fadeSlideUp 320ms ease-out ${index * 40}ms both`,
      }}
      title={`${peer.name} · ${visual.label} · ${peer.lastActive}`}
    >
      {/* Opt-in indicator — top right */}
      <div style={{
        position: 'absolute', top: 8, right: 8,
        width: 18, height: 18, borderRadius: '50%',
        background: peer.optedIn ? T.acc5 : T.cool5,
        color: 'white',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        boxShadow: '0 1px 2px rgba(0,0,0,0.15)',
      }}>
        {peer.optedIn
          ? <CheckIcon size={10} stroke="white" strokeWidth={2.5} />
          : <LockIcon size={10} stroke="white" strokeWidth={2} />}
      </div>

      {/* Avatar */}
      <div style={{
        width: 48, height: 48, borderRadius: '50%',
        background: tone.bg, color: tone.fg,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: T.sans, fontWeight: 700, fontSize: '0.9375rem',
        letterSpacing: '0.02em',
        marginTop: 4,
      }}>
        {peer.initials}
      </div>

      {/* Name */}
      <div style={{
        fontFamily: T.sans, fontSize: '0.875rem', fontWeight: 600,
        color: T.ink8, textAlign: 'center',
        whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        width: '100%',
      }}>
        {shortName(peer.name)}
      </div>

      {/* Quadrant label */}
      <div style={{
        fontFamily: T.sans, fontSize: '0.6875rem', fontWeight: 600,
        color: T.ink5, textTransform: 'uppercase', letterSpacing: '0.06em',
        textAlign: 'center',
      }}>
        {visual.label}
      </div>
    </div>
  );
}

// ── Filter pill ───────────────────────────────────────────────────────────
function FilterPill({ active, children, onClick }) {
  const [hov, setHov] = useState(false);
  return (
    <button onClick={onClick}
      onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
      style={{
        fontFamily: T.sans, fontSize: '0.8125rem', fontWeight: 600,
        padding: '5px 12px', borderRadius: 999, cursor: 'pointer',
        background: active ? T.ink8 : (hov ? T.paper2 : T.paper1),
        color: active ? T.paper0 : T.ink7,
        border: `1px solid ${active ? T.ink8 : T.paper3}`,
        transition: 'background 120ms, color 120ms, border-color 120ms',
      }}>
      {children}
    </button>
  );
}

// ── Mini sparkline ────────────────────────────────────────────────────────
function Sparkline({ values = [2, 5, 3, 8, 6, 10, 12], width = 80, height = 20, color }) {
  const max = Math.max(...values, 1);
  const step = width / (values.length - 1 || 1);
  const pts = values.map((v, i) => `${i * step},${height - (v / max) * (height - 2) - 1}`).join(' ');
  return (
    <svg width={width} height={height} style={{ display: 'block' }}>
      <polyline points={pts} fill="none" stroke={color || T.ink4} strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// ── Derive a per-peer graph state snapshot (deterministic from completedNodes) ──
function peerNodesSnapshot(peer) {
  const base = AtlasData.nodes;
  return base.map((n, i) => {
    let state;
    if (i < peer.completedNodes) state = 'completed';
    else if (i === peer.completedNodes) state = 'in_progress';
    else if (i === peer.completedNodes + 1 || i === peer.completedNodes + 2) state = 'gated';
    else state = 'locked';
    return { ...n, state };
  });
}

// ── Detail drawer ─────────────────────────────────────────────────────────
function StudentDrawer({ peer, onClose, onGoToGates }) {
  const [toast, setToast] = useState(null);

  useEffect(() => {
    function onEsc(e) { if (e.key === 'Escape') onClose(); }
    window.addEventListener('keydown', onEsc);
    return () => window.removeEventListener('keydown', onEsc);
  }, [onClose]);

  if (!peer) return null;
  const visual = QUADRANT_VISUAL[peer.quadrant];
  const tone = toneFromPeer(peer);
  const firstName = peer.name.split(' ')[0];

  function sendOptInRequest() {
    setToast(`Solicitud enviada a ${firstName}. Revisará cuando abra Atlas.`);
    setTimeout(() => setToast(null), 2500);
  }

  const gapAbs = Math.abs(peer.gapPercent);
  const gapPositive = peer.gapPercent > 0;
  const gapNeutral = gapAbs < 3;
  const gapBg = gapNeutral
    ? 'hsl(var(--atlas-paper-100))'
    : gapPositive
      ? 'hsl(var(--atlas-warm-50))'
      : 'hsl(var(--atlas-cool-50))';
  const gapAccent = gapNeutral ? T.paper4 : gapPositive ? T.warm5 : T.cool5;

  // Recent sessions — Mariana uses real data, others get variants
  const sessions = peer.id === 'mariana'
    ? AtlasData.sessionsByCourse.pmd.slice(0, 3).map(s => ({
        label: `${s.title.split(' · ')[0]} · ${s.nodeLabel || '—'}`,
        sub: s.lastActivity,
      }))
    : [
        { label: `Tarea 3 · ${peer.completedNodes >= 2 ? 'reg. lineal' : 'EDA'}`, sub: peer.lastActive },
        { label: `Tarea 2 · EDA`, sub: '18 abr' },
        { label: `Tarea 1 · limpieza`, sub: '11 abr' },
      ];

  return (
    <div style={{
      position: 'absolute', right: 0, top: 0, bottom: 0, width: 420,
      background: T.paper0, borderLeft: `1px solid ${T.paper3}`,
      boxShadow: '-4px 0 24px rgba(0,0,0,0.10)',
      overflow: 'auto', zIndex: 10,
      animation: 'slideInRight 240ms ease-out',
    }}>
      {/* Close button */}
      <button onClick={onClose} aria-label="Cerrar"
        style={{
          position: 'absolute', top: 14, right: 14,
          background: 'none', border: 'none', cursor: 'pointer',
          color: T.ink4, fontSize: 22, lineHeight: 1, padding: '4px 8px',
        }}>×</button>

      <div style={{ padding: '28px 28px 24px' }}>
        {/* Header */}
        <div style={{ display: 'flex', gap: 14, alignItems: 'center', marginBottom: 14 }}>
          <div style={{
            width: 64, height: 64, borderRadius: '50%',
            background: tone.bg, color: tone.fg,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontFamily: T.sans, fontWeight: 700, fontSize: '1.125rem',
            flexShrink: 0,
          }}>{peer.initials}</div>
          <div style={{ minWidth: 0 }}>
            <div style={{
              fontFamily: T.serif, fontSize: '1.375rem', fontWeight: 500,
              color: T.ink9, lineHeight: 1.2,
              fontVariationSettings: '"opsz" 24',
            }}>{peer.name}</div>
            <div style={{ marginTop: 6, display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap' }}>
              <Chip style={{ background: visual.bg, borderColor: visual.border, color: T.ink7 }}>
                {visual.label}
              </Chip>
              <span style={{ fontFamily: T.sans, fontSize: '0.8125rem', color: T.ink5 }}>
                activa {peer.lastActive}
              </span>
            </div>
          </div>
        </div>

        {peer.optedIn ? (
          <OptedInBody peer={peer} sessions={sessions}
            gapAbs={gapAbs} gapPositive={gapPositive} gapNeutral={gapNeutral}
            gapBg={gapBg} gapAccent={gapAccent}
            onGoToGates={onGoToGates}
          />
        ) : (
          <LockedBody peer={peer} firstName={firstName}
            onSendRequest={sendOptInRequest} toast={toast}
          />
        )}
      </div>
    </div>
  );
}

// ── Opted-in body ─────────────────────────────────────────────────────────
function OptedInBody({ peer, sessions, gapAbs, gapPositive, gapNeutral, gapBg, gapAccent, onGoToGates }) {
  const nodes = peerNodesSnapshot(peer);

  return (
    <>
      {/* Opt-in audit chip */}
      <div style={{ marginBottom: 20 }}>
        <span style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          fontFamily: T.mono, fontSize: '0.6875rem', fontWeight: 500,
          background: 'hsl(var(--atlas-accent-50))',
          border: `1px solid hsl(var(--atlas-accent-200))`,
          color: T.acc7,
          borderRadius: 4, padding: '3px 8px',
        }}>
          <CheckIcon size={10} stroke={T.acc6} strokeWidth={2.5} />
          opt-in firmado · 2026-03-15
        </span>
      </div>

      {/* Gap band */}
      <div style={{
        background: gapBg,
        borderLeft: `3px solid ${gapAccent}`,
        borderRadius: 4,
        padding: '12px 14px',
        marginBottom: 20,
      }}>
        <p style={{
          fontFamily: T.serif, fontSize: '0.9375rem',
          color: T.ink8, lineHeight: 1.55, margin: 0,
          fontVariationSettings: '"opsz" 14',
        }}>
          {gapNeutral
            ? <>Su percepción y su desempeño en <Ref>regresión lineal</Ref> están <strong>alineados</strong> (±{gapAbs}%).</>
            : gapPositive
              ? <>Cree saber un <strong>{gapAbs}%</strong> más de lo que demuestra en <Ref>regresión lineal</Ref>.</>
              : <>Demuestra un <strong>{gapAbs}%</strong> más de lo que cree saber en <Ref>regresión lineal</Ref>.</>}
        </p>
      </div>

      {/* Debt */}
      <div style={{ marginBottom: 22 }}>
        <div style={{ display: 'flex', alignItems: 'flex-end', gap: 10 }}>
          <div style={{
            fontFamily: T.serif, fontSize: '1.25rem', fontWeight: 500,
            color: peer.debtMinutes > 0 ? T.crit7 : T.ink7,
            fontVariationSettings: '"opsz" 24',
          }}>
            {peer.debtMinutes} min
          </div>
          <div style={{ marginLeft: 'auto' }}>
            <Sparkline
              values={peer.debtMinutes > 0 ? [3, 5, 8, 6, 10, 12, peer.debtMinutes] : [0, 0, 1, 0, 2, 1, 0]}
              color={peer.debtMinutes > 0 ? T.warm5 : T.acc5}
            />
          </div>
        </div>
        <div style={{ fontFamily: T.sans, fontSize: '0.8125rem', color: T.ink5, marginTop: 2 }}>
          {peer.debtMinutes > 0 ? 'deuda abierta esta semana' : 'sin deuda abierta'}
        </div>
      </div>

      {/* Mini graph */}
      <div style={{ marginBottom: 22 }}>
        <div style={{
          fontFamily: T.sans, fontSize: '0.75rem', fontWeight: 600, color: T.ink4,
          textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 8,
        }}>Su grafo</div>
        <div style={{
          background: T.paper1, border: `1px solid ${T.paper3}`,
          borderRadius: 8, padding: 8, overflow: 'hidden',
          display: 'flex', justifyContent: 'center',
        }}>
          <SVGGraph nodes={nodes} edges={AtlasData.edges} width={360} height={240} miniMode={true} />
        </div>
      </div>

      {/* Recent sessions */}
      <div style={{ marginBottom: 24 }}>
        <div style={{
          fontFamily: T.sans, fontSize: '0.75rem', fontWeight: 600, color: T.ink4,
          textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 10,
        }}>Sesiones recientes</div>
        {sessions.map((s, i) => (
          <div key={i} style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            padding: '8px 0', borderBottom: i === sessions.length - 1 ? 'none' : `1px solid ${T.paper3}`,
          }}>
            <span style={{ fontFamily: T.sans, fontSize: '0.875rem', color: T.ink8 }}>{s.label}</span>
            <span style={{ fontFamily: T.mono, fontSize: '0.75rem', color: T.ink4 }}>{s.sub}</span>
          </div>
        ))}
      </div>

      {/* CTA */}
      <Btn onClick={onGoToGates} variant="primary" size="md"
        style={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
        <span>Definir activity-gates para esta materia</span>
        <span>→</span>
      </Btn>
    </>
  );
}

// ── Locked body ───────────────────────────────────────────────────────────
function LockedBody({ peer, firstName, onSendRequest, toast }) {
  return (
    <>
      <div style={{
        padding: '32px 20px 24px',
        display: 'flex', flexDirection: 'column', alignItems: 'center',
        textAlign: 'center',
        animation: 'fadeIn 280ms ease-out',
      }}>
        <div style={{
          width: 72, height: 72, borderRadius: '50%',
          background: 'hsl(var(--atlas-cool-50))',
          border: `1px solid hsl(var(--atlas-cool-200))`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          marginBottom: 18,
        }}>
          <LockIcon size={32} stroke={T.cool5} strokeWidth={1.75} />
        </div>

        <h3 style={{
          fontFamily: T.serif, fontSize: '1.125rem', fontWeight: 500,
          color: T.ink8, lineHeight: 1.35, margin: '0 0 10px',
          fontVariationSettings: '"opsz" 18',
        }}>
          {firstName} no ha firmado opt-in individual.
        </h3>

        <p style={{
          fontFamily: T.sans, fontSize: '0.9375rem', color: T.ink6 || T.ink5,
          lineHeight: 1.55, margin: '0 0 22px', maxWidth: 340,
        }}>
          Para ver sus deudas, sesiones y grafo, {firstName} debe firmar el consentimiento de vista individual.
          El docente no puede activarlo por ella.
        </p>

        <Btn onClick={onSendRequest} variant="secondary" size="sm">
          Enviar solicitud de opt-in
        </Btn>

        <p style={{
          fontFamily: T.mono, fontSize: '0.6875rem', color: T.ink4,
          margin: '20px 0 0', maxWidth: 320, lineHeight: 1.5,
        }}>
          Las solicitudes caducan a las 72h y requieren justificación pedagógica.
        </p>

        {toast && (
          <div style={{
            marginTop: 18,
            fontFamily: T.sans, fontSize: '0.8125rem',
            background: 'hsl(var(--atlas-accent-50))',
            border: `1px solid hsl(var(--atlas-accent-200))`,
            color: T.acc7,
            borderRadius: 6, padding: '8px 12px',
            animation: 'fadeIn 180ms ease-out',
          }}>{toast}</div>
        )}
      </div>
    </>
  );
}

// ── Main screen ───────────────────────────────────────────────────────────
function T02StudentCards() {
  const [appState, set] = useAtlasState();
  const [filter, setFilter] = useState('todos'); // todos | riesgo | subestima | coherente-minus | coherente-plus
  const [fading, setFading] = useState(false);
  const [animKey, setAnimKey] = useState(0);

  const peers = AtlasData.peers || [];
  const counts = peers.reduce((acc, p) => {
    acc.todos += 1;
    acc[p.quadrant] = (acc[p.quadrant] || 0) + 1;
    return acc;
  }, { todos: 0 });

  const filtered = filter === 'todos' ? peers : peers.filter(p => p.quadrant === filter);

  const selected = peers.find(p => p.id === appState.selectedStudentId) || null;

  // Filter change: fade out then in
  function changeFilter(next) {
    if (next === filter) return;
    setFading(true);
    setTimeout(() => {
      setFilter(next);
      setAnimKey(k => k + 1);
      setFading(false);
    }, 120);
  }

  function openPeer(peer) {
    set({ selectedStudentId: peer.id });
  }
  function closeDrawer() {
    set({ selectedStudentId: null });
  }

  const filterDefs = [
    { id: 'todos',           label: 'Todos',      count: counts.todos },
    { id: 'riesgo',          label: 'Riesgo',     count: counts['riesgo'] || 0 },
    { id: 'subestima',       label: 'Sub',        count: counts['subestima'] || 0 },
    { id: 'coherente-minus', label: 'Coh−',       count: counts['coherente-minus'] || 0 },
    { id: 'coherente-plus',  label: 'Coh+',       count: counts['coherente-plus'] || 0 },
  ];

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.paper0 }}>
      <TopBar
        left={
          <>
            <button onClick={() => set({ screen: 't01' })}
              aria-label="Volver"
              style={{ background: 'none', border: 'none', cursor: 'pointer', color: T.ink5, fontSize: 18, padding: '0 4px' }}>←</button>
            <AtlasLogo />
            <span style={{ fontFamily: T.sans, fontSize: '0.875rem', color: T.ink5 }}>
              / {AtlasData.teacher?.program?.split('·').pop()?.trim() || 'MAT-PMD'} · estudiantes
            </span>
          </>
        }
        right={
          <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
            {filterDefs.map(f => (
              <FilterPill key={f.id}
                active={filter === f.id}
                onClick={() => changeFilter(f.id)}>
                {f.label} ({f.count})
              </FilterPill>
            ))}
          </div>
        }
      />

      <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
        <div style={{
          height: '100%', overflow: 'auto',
          padding: '32px 40px 48px',
          marginRight: selected ? 420 : 0,
          transition: 'margin-right 240ms ease-out',
        }}>
          <div style={{ maxWidth: 1100, margin: '0 auto' }}>
            {/* Heading */}
            <div style={{ marginBottom: 28, animation: 'fadeSlideUp 320ms ease-out' }}>
              <h1 style={{
                fontFamily: T.serif, fontSize: '1.25rem', fontWeight: 500,
                color: T.ink9, margin: '0 0 6px',
                fontVariationSettings: '"opsz" 24',
              }}>
                Estudiantes de la cohorte · {peers.length}
              </h1>
              <p style={{
                fontFamily: T.sans, fontSize: '0.9375rem', color: T.ink5,
                margin: 0, lineHeight: 1.5, maxWidth: 620,
              }}>
                Los detalles individuales solo son visibles con opt-in firmado del estudiante.
              </p>
            </div>

            {/* Grid */}
            <div key={animKey} style={{
              display: 'grid',
              gridTemplateColumns: 'repeat(5, 1fr)',
              gap: 12,
              opacity: fading ? 0 : 1,
              transition: 'opacity 120ms ease-out',
            }}>
              {filtered.map((peer, i) => (
                <StudentCard key={peer.id} peer={peer} index={i} onClick={openPeer} />
              ))}
            </div>

            {filtered.length === 0 && (
              <div style={{
                fontFamily: T.sans, fontSize: '0.9375rem', color: T.ink5,
                textAlign: 'center', padding: '48px 0',
              }}>
                Sin estudiantes en este cuadrante.
              </div>
            )}
          </div>
        </div>

        {/* Drawer */}
        {selected && (
          <StudentDrawer
            peer={selected}
            onClose={closeDrawer}
            onGoToGates={() => set({ screen: 't03' })}
          />
        )}
      </div>
    </div>
  );
}

Object.assign(window, { T02StudentCards });
