const { useEffect, useMemo, useRef, useState } = React;

const OPERATOR_ASSESSMENT_ACTIVE_ATTEMPT_KEY = 'eg_operator_assessment_active_attempt_v1';
const OPERATOR_ASSESSMENT_PUBLIC_ATTEMPT_PREFIX = 'eg_operator_assessment_public_attempt_v1:';

function getAssessmentAttemptStorageKey(guestAccess = null) {
  if (guestAccess?.token) {
    return `${OPERATOR_ASSESSMENT_PUBLIC_ATTEMPT_PREFIX}${guestAccess.token}`;
  }
  return OPERATOR_ASSESSMENT_ACTIVE_ATTEMPT_KEY;
}

function formatAssessmentDateTime(value) {
  if (!value) return 'Sin registro';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  return new Intl.DateTimeFormat('es-CL', {
    day: '2-digit',
    month: 'short',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  }).format(date);
}

function formatSeconds(totalSeconds) {
  if (!Number.isFinite(totalSeconds) || totalSeconds <= 0) return '0 min';
  const minutes = Math.max(1, Math.round(totalSeconds / 60));
  return `${minutes} min`;
}

function getAssessmentDisplayScore(attempt) {
  const score = attempt?.totalScore ?? attempt?.provisionalScore;
  return score === null || score === undefined ? null : Number(score);
}

function getAssessmentScaleRows() {
  return [
    ['85-100', 'Perfil altamente compatible', 'Presenta muy buenas condiciones para avanzar a Academy y entrevista operacional.', BRAND.green.main],
    ['75-84', 'Perfil compatible', 'Cumple con las condiciones esperadas para continuar el proceso.', '#63C77C'],
    ['65-74', 'Perfil con observaciones', 'Puede continuar, pero requiere revisión del supervisor antes de decidir.', BRAND.amber.main],
    ['0-64', 'Perfil no recomendado', 'No alcanza actualmente el estándar mínimo esperado para el cargo.', BRAND.red.main],
  ];
}

function normalizeReviewerState(value) {
  if (!value) return 'En evaluación';
  return value;
}

function scoreBar(score, max, color) {
  const pct = max > 0 ? Math.max(0, Math.min(100, (Number(score || 0) / max) * 100)) : 0;
  return (
    <div style={{ display:'grid', gap:6 }}>
      <ProgressBar value={pct} color={color} height={8}/>
      <div style={{ fontSize:11, color:BRAND.text.light }}>{Number(score || 0)} / {max}</div>
    </div>
  );
}

function AssessmentOverview({
  attempts,
  onStart,
  onOpenAttempt,
  onResumeAttempt,
  onGenerateAccessLink,
  activeAttemptId,
  loading,
  generatingAccessLink = false,
  accessLink = '',
  accessLinkError = '',
}) {
  const isCompact = useViewportMatch(860);
  const submitted = attempts.filter((attempt) => attempt.status !== 'IN_PROGRESS');
  const pendingReview = attempts.filter((attempt) => attempt.status === 'SUBMITTED');
  const avgScore = submitted.length
    ? (submitted.reduce((sum, attempt) => sum + Number(attempt.totalScore ?? attempt.provisionalScore ?? 0), 0) / submitted.length).toFixed(1)
    : '--';

  return (
    <div>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:isCompact ? 'stretch' : 'flex-start', flexDirection:isCompact ? 'column' : 'row', gap:12, marginBottom:24 }}>
        <div>
          <h2 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:24, color:BRAND.text.primary, margin:'0 0 4px' }}>
            Evaluación de Postulantes
          </h2>
          <p style={{ fontSize:14, color:BRAND.text.muted, margin:0, maxWidth:760 }}>
            Operator Assessment V1 para medir competencia digital, observación, atención sostenida, criterio, comunicación y disciplina operacional.
          </p>
        </div>
        <div style={{ display:'flex', gap:10, flexWrap:'wrap' }}>
          {activeAttemptId && (
            <button onClick={onResumeAttempt} style={{ padding:'10px 16px', borderRadius:8, border:`1px solid ${BRAND.border}`, background:'white', color:BRAND.text.secondary, fontSize:13, fontWeight:600, cursor:'pointer' }}>
              Continuar evaluaci??n
            </button>
          )}
          <button onClick={onGenerateAccessLink} disabled={generatingAccessLink} style={{ padding:'10px 16px', borderRadius:8, border:`1px solid ${BRAND.border}`, background:'white', color:BRAND.text.secondary, fontSize:13, fontWeight:600, cursor:generatingAccessLink ? 'wait' : 'pointer' }}>
            {generatingAccessLink ? 'Generando link...' : 'Generar link'}
          </button>
          <button onClick={onStart} style={{ padding:'10px 18px', borderRadius:8, border:'none', background:BRAND.green.main, color:'white', fontSize:13, fontWeight:600, cursor:'pointer' }}>
            Nuevo proceso
          </button>
        </div>
      </div>
      {(accessLink || accessLinkError) && (
        <Card style={{ padding:20, marginBottom:20, background:'#FCFDFC' }}>
          <div style={{ display:'grid', gap:10 }}>
            <div>
              <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:16, color:BRAND.text.primary, marginBottom:4 }}>
                Enlace unico de evaluacion
              </div>
              <div style={{ fontSize:13, color:BRAND.text.muted, lineHeight:1.6 }}>
                Cada vez que generes uno nuevo, el anterior deja de estar vigente. El enlace solo puede utilizarse una vez.
              </div>
            </div>
            {accessLink ? (
              <div style={{ display:'grid', gap:10 }}>
                <div style={{ padding:'12px 14px', borderRadius:10, border:`1px solid ${BRAND.border}`, background:'white', fontSize:13, color:BRAND.text.secondary, wordBreak:'break-all' }}>
                  {accessLink}
                </div>
                <button onClick={() => navigator.clipboard?.writeText(accessLink)} style={{ width:'fit-content', padding:'10px 14px', borderRadius:8, border:'none', background:BRAND.green.main, color:'white', fontSize:13, fontWeight:700, cursor:'pointer' }}>
                  Copiar link
                </button>
              </div>
            ) : (
              <div style={{ padding:'10px 12px', borderRadius:10, background:BRAND.red.light, color:BRAND.red.dark, fontSize:13 }}>
                {accessLinkError}
              </div>
            )}
          </div>
        </Card>
      )}
      <div style={{ display:'grid', gridTemplateColumns:isCompact ? '1fr' : 'repeat(auto-fit, minmax(190px, 1fr))', gap:16, marginBottom:20 }}>
        <StatCard label="Postulantes" value={attempts.length} sub="registros acumulados" color={BRAND.green.main} icon="users"/>
        <StatCard label="Pendientes revisión" value={pendingReview.length} sub="requieren evaluación manual" color={BRAND.amber.main} icon="star"/>
        <StatCard label="Promedio general" value={avgScore} sub="score de intentos enviados" color={BRAND.blue.main} icon="trending-up"/>
        <StatCard label="En evaluación" value={attempts.filter((attempt) => attempt.status === 'IN_PROGRESS').length} sub="procesos activos" color={BRAND.red.main} icon="award"/>
      </div>

      <Card style={{ padding:0, overflow:'hidden' }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'18px 20px', borderBottom:`1px solid ${BRAND.border}` }}>
          <div>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:15, color:BRAND.text.primary }}>Panel Supervisor / RR.HH.</div>
            <div style={{ fontSize:12, color:BRAND.text.muted, marginTop:4 }}>Revisión de resultados por dimensión y estado de evaluación.</div>
          </div>
          {loading && <div style={{ fontSize:12, color:BRAND.text.light }}>Actualizando…</div>}
        </div>

        {attempts.length === 0 ? (
          <div style={{ padding:24, fontSize:13, color:BRAND.text.muted }}>
            Aún no hay postulantes registrados. Usa <strong>Nuevo proceso</strong> para iniciar la primera evaluación.
          </div>
        ) : (
          <div style={{ overflowX:'auto' }}>
            <table style={{ width:'100%', borderCollapse:'collapse', minWidth:980 }}>
              <thead>
                <tr style={{ background:'#FAFCFA' }}>
                  {['Nombre','Fecha','Operator Score','Digital','Observación','Atención','Criterio','Comunicación','Disciplina','Clasificación','Estado'].map((label) => (
                    <th key={label} style={{ padding:'12px 14px', textAlign:'left', fontSize:11, textTransform:'uppercase', letterSpacing:'0.06em', color:BRAND.text.light, borderBottom:`1px solid ${BRAND.border}` }}>{label}</th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {attempts.map((attempt) => (
                  <tr key={attempt.id} onClick={() => onOpenAttempt(attempt.id)} style={{ cursor:'pointer' }}>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}` }}>
                      <div style={{ fontSize:13, fontWeight:700, color:BRAND.text.primary }}>{attempt.applicant.name}</div>
                      <div style={{ fontSize:11, color:BRAND.text.light }}>{attempt.applicant.email}</div>
                    </td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{formatAssessmentDateTime(attempt.submittedAt || attempt.startedAt)}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, fontWeight:700, color:BRAND.text.primary }}>
                      {attempt.totalScore ?? (attempt.status === 'IN_PROGRESS' ? '--' : 'Pendiente')}
                    </td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{attempt.moduleScores?.digital ?? '--'}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{attempt.moduleScores?.observation ?? '--'}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{attempt.moduleScores?.attention ?? '--'}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{attempt.moduleScores?.criterion ?? '--'}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{attempt.moduleScores?.communication ?? 'Pendiente'}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{attempt.moduleScores?.discipline ?? '--'}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}`, fontSize:12, color:BRAND.text.secondary }}>{attempt.classification || 'En evaluación'}</td>
                    <td style={{ padding:'14px', borderBottom:`1px solid ${BRAND.border}` }}>
                      <span style={{ display:'inline-flex', alignItems:'center', gap:6, padding:'5px 9px', borderRadius:999, fontSize:11, fontWeight:700, background: attempt.status === 'REVIEWED' ? BRAND.green.light : attempt.status === 'SUBMITTED' ? BRAND.amber.light : BRAND.blue.light, color: attempt.status === 'REVIEWED' ? BRAND.green.dark : attempt.status === 'SUBMITTED' ? BRAND.amber.dark : BRAND.blue.dark }}>
                        {attempt.status === 'REVIEWED' ? 'Compatible / revisado' : attempt.status === 'SUBMITTED' ? 'Pendiente revisión' : 'En evaluación'}
                      </span>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </Card>
    </div>
  );
}

function StartAssessmentForm({ onCancel, onSubmit, loading }) {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  return (
    <Card style={{ padding:24, maxWidth:640 }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12, marginBottom:20 }}>
        <div>
          <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary, margin:'0 0 6px' }}>
            Nuevo proceso de evaluación
          </h3>
          <p style={{ fontSize:14, color:BRAND.text.muted, margin:0 }}>
            Crea la ficha del postulante e inicia la prueba de aptitud operacional.
          </p>
        </div>
        <button onClick={onCancel} style={{ background:'none', border:'none', color:BRAND.text.light, cursor:'pointer', fontSize:13 }}>Cerrar</button>
      </div>

      <div style={{ display:'grid', gap:16 }}>
        <div>
          <label style={{ display:'block', fontSize:12, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Nombre completo</label>
          <input value={name} onChange={(event) => setName(event.target.value)} placeholder="Ej: Camila Torres" style={{ width:'100%', padding:'12px 14px', borderRadius:10, border:`1.5px solid ${BRAND.border}`, outline:'none', fontSize:14 }} />
        </div>
        <div>
          <label style={{ display:'block', fontSize:12, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Correo</label>
          <input value={email} onChange={(event) => setEmail(event.target.value)} placeholder="Ej: camila@correo.cl" style={{ width:'100%', padding:'12px 14px', borderRadius:10, border:`1.5px solid ${BRAND.border}`, outline:'none', fontSize:14 }} />
        </div>
      </div>

      <div style={{ display:'flex', justifyContent:'flex-end', gap:10, marginTop:20 }}>
        <button onClick={onCancel} style={{ padding:'10px 16px', borderRadius:8, border:`1px solid ${BRAND.border}`, background:'white', color:BRAND.text.secondary, cursor:'pointer' }}>
          Cancelar
        </button>
        <button onClick={() => onSubmit({ name, email })} disabled={loading || !name.trim() || !email.trim()} style={{ padding:'10px 18px', borderRadius:8, border:'none', background:loading || !name.trim() || !email.trim() ? '#BFD7CD' : BRAND.green.main, color:'white', cursor:loading ? 'wait' : 'pointer', fontWeight:700 }}>
          {loading ? 'Creando…' : 'Iniciar evaluación'}
        </button>
      </div>
    </Card>
  );
}

function StartAssessmentFormV2({ onCancel, onSubmit, loading, errorMessage = '' }) {
  const [name, setName] = useState('');
  const [rut, setRut] = useState('');
  const [email, setEmail] = useState('');
  const [phone, setPhone] = useState('');
  const isDisabled = loading || !name.trim() || !rut.trim() || !email.trim() || !phone.trim();

  return (
    <Card style={{ padding:24, maxWidth:640 }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12, marginBottom:20 }}>
        <div>
          <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary, margin:'0 0 6px' }}>
            Nuevo proceso de evaluación
          </h3>
          <p style={{ fontSize:14, color:BRAND.text.muted, margin:0 }}>
            Crea la ficha del postulante e inicia la prueba de aptitud operacional.
          </p>
        </div>
        <button onClick={onCancel} style={{ background:'none', border:'none', color:BRAND.text.light, cursor:'pointer', fontSize:13 }}>
          Cerrar
        </button>
      </div>

      <div style={{ display:'grid', gap:16 }}>
        <div>
          <label style={{ display:'block', fontSize:12, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Nombre completo</label>
          <input value={name} onChange={(event) => setName(event.target.value)} placeholder="Ej: Camila Torres" style={{ width:'100%', padding:'12px 14px', borderRadius:10, border:`1.5px solid ${BRAND.border}`, outline:'none', fontSize:14 }} />
        </div>
        <div>
          <label style={{ display:'block', fontSize:12, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>RUT</label>
          <input value={rut} onChange={(event) => setRut(event.target.value)} placeholder="Ej: 12.345.678-9" style={{ width:'100%', padding:'12px 14px', borderRadius:10, border:`1.5px solid ${BRAND.border}`, outline:'none', fontSize:14 }} />
        </div>
        <div>
          <label style={{ display:'block', fontSize:12, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Correo</label>
          <input value={email} onChange={(event) => setEmail(event.target.value)} placeholder="Ej: camila@correo.cl" style={{ width:'100%', padding:'12px 14px', borderRadius:10, border:`1.5px solid ${BRAND.border}`, outline:'none', fontSize:14 }} />
        </div>
        <div>
          <label style={{ display:'block', fontSize:12, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Teléfono</label>
          <input value={phone} onChange={(event) => setPhone(event.target.value)} placeholder="Ej: +56 9 1234 5678" style={{ width:'100%', padding:'12px 14px', borderRadius:10, border:`1.5px solid ${BRAND.border}`, outline:'none', fontSize:14 }} />
        </div>
      </div>

      {errorMessage && (
        <div style={{ marginTop:16, padding:'10px 12px', borderRadius:10, background:BRAND.red.light, color:BRAND.red.dark, fontSize:13 }}>
          {errorMessage}
        </div>
      )}

      <div style={{ display:'flex', justifyContent:'flex-end', gap:10, marginTop:20 }}>
        <button onClick={onCancel} style={{ padding:'10px 16px', borderRadius:8, border:`1px solid ${BRAND.border}`, background:'white', color:BRAND.text.secondary, cursor:'pointer' }}>
          Cancelar
        </button>
        <button onClick={() => onSubmit({ name, rut, email, phone })} disabled={isDisabled} style={{ padding:'10px 18px', borderRadius:8, border:'none', background:isDisabled ? '#BFD7CD' : BRAND.green.main, color:'white', cursor:loading ? 'wait' : 'pointer', fontWeight:700 }}>
          {loading ? 'Creando…' : 'Iniciar evaluación'}
        </button>
      </div>
    </Card>
  );
}

function ModuleIntroCard({ title, description, bulletPoints, onContinue, ctaLabel='Continuar' }) {
  return (
    <Card style={{ padding:24 }}>
      <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary, margin:'0 0 8px' }}>{title}</h3>
      <p style={{ fontSize:14, color:BRAND.text.muted, lineHeight:1.7, margin:'0 0 18px' }}>{description}</p>
      <div style={{ display:'grid', gap:10, marginBottom:18 }}>
        {bulletPoints.map((item) => (
          <div key={item} style={{ display:'flex', gap:10, alignItems:'flex-start' }}>
            <div style={{ width:20, height:20, borderRadius:'50%', background:BRAND.green.light, color:BRAND.green.dark, fontSize:11, fontWeight:700, display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>✓</div>
            <div style={{ fontSize:13, color:BRAND.text.secondary, lineHeight:1.6 }}>{item}</div>
          </div>
        ))}
      </div>
      <button onClick={onContinue} style={{ padding:'11px 18px', borderRadius:8, border:'none', background:BRAND.green.main, color:'white', fontSize:13, fontWeight:700, cursor:'pointer' }}>{ctaLabel}</button>
    </Card>
  );
}

function MultipleChoiceModule({ title, description, questions, answers, onChange, onComplete, pointsLabel }) {
  const [index, setIndex] = useState(0);
  const question = questions[index];

  function handleAnswer(value) {
    const nextAnswers = { ...(answers || {}), [question.id]: value };
    onChange(nextAnswers);

    if (index === questions.length - 1) {
      onComplete(nextAnswers);
      return;
    }

    setIndex(index + 1);
  }

  return (
    <Card style={{ padding:24 }}>
      <div style={{ display:'flex', justifyContent:'space-between', gap:12, marginBottom:16, flexWrap:'wrap' }}>
        <div>
          <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary, margin:'0 0 6px' }}>{title}</h3>
          <p style={{ fontSize:14, color:BRAND.text.muted, margin:0 }}>{description}</p>
        </div>
        <div style={{ fontSize:12, color:BRAND.text.light }}>{pointsLabel}</div>
      </div>

      <div style={{ fontSize:12, fontWeight:700, color:BRAND.green.dark, marginBottom:10 }}>Pregunta {index + 1} de {questions.length}</div>
      <div style={{ fontSize:18, fontWeight:700, color:BRAND.text.primary, lineHeight:1.45, marginBottom:18 }}>{question.prompt}</div>

      <div style={{ display:'grid', gap:10 }}>
        {question.options.map((option) => {
          const active = answers?.[question.id] === option.value;
          return (
            <button key={option.value} onClick={() => handleAnswer(option.value)} style={{ width:'100%', textAlign:'left', borderRadius:12, border:`1.5px solid ${active ? BRAND.green.main : BRAND.border}`, background:active ? BRAND.green.light : 'white', padding:'14px 16px', cursor:'pointer', fontSize:14, color:active ? BRAND.green.dark : BRAND.text.secondary, lineHeight:1.5 }}>
              <strong style={{ marginRight:6 }}>{option.value}.</strong>{option.label}
            </button>
          );
        })}
      </div>
    </Card>
  );
}

function AvailabilityModule({ draft, onChange, onComplete }) {
  const [step, setStep] = useState(0);
  const questions = [
    {
      key: 'shiftAvailability',
      prompt: 'El cargo requiere trabajar mediante sistema de turnos, incluyendo turnos nocturnos, fines de semana y festivos. ¿Tiene disponibilidad para trabajar bajo estas condiciones?',
      options: [
        { value: true, label: 'Sí' },
        { value: false, label: 'No' },
      ],
    },
    {
      key: 'nightShiftExperience',
      prompt: '¿Ha trabajado anteriormente en turnos nocturnos?',
      options: [
        { value: 'Sí, regularmente', label: 'Sí, regularmente' },
        { value: 'Algunas veces', label: 'Algunas veces' },
        { value: 'Nunca', label: 'Nunca' },
      ],
    },
  ];
  const current = questions[step];

  function handleSelection(value) {
    const next = { ...(draft || {}), [current.key]: value };
    onChange(next);
    if (step === questions.length - 1) {
      onComplete(next);
      return;
    }
    setStep(step + 1);
  }

  return (
    <Card style={{ padding:24 }}>
      <div style={{ fontSize:12, fontWeight:700, color:BRAND.text.light, textTransform:'uppercase', letterSpacing:'0.08em', marginBottom:10 }}>Módulo 0 · Habilitante</div>
      <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary, margin:'0 0 8px' }}>Disponibilidad operacional</h3>
      <p style={{ fontSize:14, color:BRAND.text.muted, margin:'0 0 18px' }}>Este bloque no asigna puntaje, pero sí registra condiciones básicas para el cargo.</p>
      <div style={{ fontSize:12, fontWeight:700, color:BRAND.green.dark, marginBottom:10 }}>Pregunta {step + 1} de 2</div>
      <div style={{ fontSize:18, fontWeight:700, color:BRAND.text.primary, lineHeight:1.5, marginBottom:18 }}>{current.prompt}</div>
      <div style={{ display:'grid', gap:10 }}>
        {current.options.map((option) => (
          <button key={String(option.value)} onClick={() => handleSelection(option.value)} style={{ textAlign:'left', borderRadius:12, border:`1.5px solid ${BRAND.border}`, background:'white', padding:'14px 16px', cursor:'pointer', fontSize:14, color:BRAND.text.secondary }}>
            {option.label}
          </button>
        ))}
      </div>
    </Card>
  );
}

function ObservationModule({ scenes, answers, onChange, onComplete }) {
  const [sceneIndex, setSceneIndex] = useState(0);
  const [phase, setPhase] = useState('intro');
  const [countdown, setCountdown] = useState(0);
  const [questionIndex, setQuestionIndex] = useState(0);
  const scene = scenes[sceneIndex];

  useEffect(() => {
    if (phase !== 'observe') return undefined;
    setCountdown(scene.durationSeconds);
    const startedAt = Date.now();
    const timer = window.setInterval(() => {
      const elapsed = Math.floor((Date.now() - startedAt) / 1000);
      const remaining = Math.max(0, scene.durationSeconds - elapsed);
      setCountdown(remaining);
      if (remaining <= 0) {
        window.clearInterval(timer);
        setPhase('questions');
      }
    }, 250);

    return () => window.clearInterval(timer);
  }, [phase, scene.durationSeconds]);

  function answerQuestion(value) {
    const sceneAnswers = { ...(answers?.[scene.id] || {}), [scene.questions[questionIndex].id]: value };
    const nextAnswers = { ...(answers || {}), [scene.id]: sceneAnswers };
    onChange(nextAnswers);

    if (questionIndex < scene.questions.length - 1) {
      setQuestionIndex(questionIndex + 1);
      return;
    }

    if (sceneIndex < scenes.length - 1) {
      setSceneIndex(sceneIndex + 1);
      setPhase('intro');
      setQuestionIndex(0);
      return;
    }

    onComplete(nextAnswers);
  }

  if (phase === 'intro') {
    return (
      <ModuleIntroCard
        title={`Observación visual · ${scene.title}`}
        description="Se mostrará una imagen CCTV durante 10 segundos. Luego responderás cuatro preguntas sin posibilidad de volver a verla."
        bulletPoints={[
          'Observa solo información operacionalmente relevante.',
          'No podrás reiniciar esta escena una vez iniciada.',
          'La imagen se oculta automáticamente al terminar el tiempo.',
        ]}
        ctaLabel="Comenzar observación"
        onContinue={() => setPhase('observe')}
      />
    );
  }

  if (phase === 'observe') {
    return (
      <Card style={{ padding:24 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, marginBottom:12 }}>
          <div>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary }}>{scene.title}</div>
            <div style={{ fontSize:13, color:BRAND.text.muted }}>Observa la escena y prepárate para responder.</div>
          </div>
          <div style={{ padding:'8px 12px', borderRadius:999, background:BRAND.green.light, color:BRAND.green.dark, fontSize:12, fontWeight:700 }}>
            {countdown}s
          </div>
        </div>
        <div style={{ overflow:'hidden', borderRadius:20, border:`1px solid ${BRAND.border}` }}>
          <img src={scene.imageUrl} alt={scene.title} style={{ display:'block', width:'100%', height:'auto', aspectRatio:'16 / 9', objectFit:'cover' }} />
        </div>
      </Card>
    );
  }

  const question = scene.questions[questionIndex];
  return (
    <Card style={{ padding:24 }}>
      <div style={{ fontSize:12, fontWeight:700, color:BRAND.text.light, textTransform:'uppercase', letterSpacing:'0.08em', marginBottom:10 }}>{scene.title}</div>
      <div style={{ fontSize:12, fontWeight:700, color:BRAND.green.dark, marginBottom:10 }}>Pregunta {questionIndex + 1} de {scene.questions.length}</div>
      <div style={{ fontSize:18, fontWeight:700, color:BRAND.text.primary, lineHeight:1.45, marginBottom:18 }}>{question.prompt}</div>
      <div style={{ display:'grid', gap:10 }}>
        {question.options.map((option) => (
          <button key={option.value} onClick={() => answerQuestion(option.value)} style={{ width:'100%', textAlign:'left', borderRadius:12, border:`1.5px solid ${BRAND.border}`, background:'white', padding:'14px 16px', cursor:'pointer', fontSize:14, color:BRAND.text.secondary }}>
            <strong style={{ marginRight:6 }}>{option.value}.</strong>{option.label}
          </button>
        ))}
      </div>
    </Card>
  );
}

function SustainedAttentionModule({ stimuli, responses, onChange, onComplete }) {
  const [started, setStarted] = useState(false);
  const [index, setIndex] = useState(0);
  const [locked, setLocked] = useState(false);
  const [countdown, setCountdown] = useState(0);
  const startedAtRef = useRef(0);
  const responsesRef = useRef(responses || []);
  responsesRef.current = responses || [];

  const currentStimulus = stimuli[index];

  useEffect(() => {
    if (!started || !currentStimulus) return undefined;

    setLocked(false);
    setCountdown(Math.ceil(currentStimulus.durationMs / 1000));
    startedAtRef.current = window.performance.now();

    const countdownTimer = window.setInterval(() => {
      const elapsed = window.performance.now() - startedAtRef.current;
      setCountdown(Math.max(0, Math.ceil((currentStimulus.durationMs - elapsed) / 1000)));
    }, 100);

    const timeout = window.setTimeout(() => {
      window.clearInterval(countdownTimer);
      advance();
    }, currentStimulus.durationMs);

    return () => {
      window.clearInterval(countdownTimer);
      window.clearTimeout(timeout);
    };
  }, [started, index]);

  useEffect(() => {
    if (!started) return undefined;

    const onKeyDown = (event) => {
      if (event.code !== 'Space') return;
      event.preventDefault();
      registerEvent();
    };

    window.addEventListener('keydown', onKeyDown, { passive:false });
    return () => window.removeEventListener('keydown', onKeyDown);
  }, [started, index, locked]);

  function registerEvent() {
    if (!started || locked || !currentStimulus) return;
    setLocked(true);
    const reactionMs = Math.round(window.performance.now() - startedAtRef.current);
    const nextResponses = [
      ...responsesRef.current.filter((entry) => entry.stimulusId !== currentStimulus.id),
      {
        stimulusId: currentStimulus.id,
        pressed: true,
        reactionMs,
      },
    ];
    onChange(nextResponses);
  }

  function advance() {
    if (!currentStimulus) return;

    const hasResponse = responsesRef.current.some((entry) => entry.stimulusId === currentStimulus.id);
    const nextResponses = hasResponse
      ? responsesRef.current
      : [
          ...responsesRef.current,
          { stimulusId: currentStimulus.id, pressed: false, reactionMs: null },
        ];

    onChange(nextResponses);

    if (index >= stimuli.length - 1) {
      onComplete(nextResponses);
      return;
    }

    setIndex(index + 1);
  }

  if (!started) {
    return (
      <ModuleIntroCard
        title="Atención sostenida"
        description="Durante los próximos minutos aparecerán distintas imágenes. Presiona EVENTO solamente cuando observes una persona dentro del área restringida indicada."
        bulletPoints={[
          'Si no existe una persona dentro del área restringida, no presiones nada.',
          'También puedes presionar la barra espaciadora para marcar un evento.',
          'No se mostrará si acertaste o fallaste durante la prueba.',
        ]}
        ctaLabel="Iniciar prueba"
        onContinue={() => setStarted(true)}
      />
    );
  }

  return (
    <Card style={{ padding:24, userSelect:'none' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, marginBottom:12, flexWrap:'wrap' }}>
        <div>
          <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary }}>Atención sostenida</div>
          <div style={{ fontSize:13, color:BRAND.text.muted }}>Secuencia {index + 1} de {stimuli.length}</div>
        </div>
        <div style={{ display:'flex', gap:10, alignItems:'center' }}>
          <div style={{ padding:'8px 12px', borderRadius:999, background:BRAND.blue.light, color:BRAND.blue.dark, fontSize:12, fontWeight:700 }}>{countdown}s</div>
          <button onClick={registerEvent} disabled={locked} style={{ padding:'12px 22px', borderRadius:10, border:'none', background:locked ? '#B6D4E8' : BRAND.red.main, color:'white', fontSize:15, fontWeight:800, letterSpacing:'0.04em', cursor:locked ? 'default' : 'pointer' }}>
            EVENTO
          </button>
        </div>
      </div>
      <div style={{ overflow:'hidden', borderRadius:20, border:`1px solid ${BRAND.border}` }}>
        <img src={currentStimulus.imageUrl} alt={`Secuencia ${index + 1}`} style={{ display:'block', width:'100%', height:'auto', aspectRatio:'16 / 9', objectFit:'cover' }} />
      </div>
      <div style={{ fontSize:12, color:BRAND.text.light, marginTop:10 }}>
        La barra espaciadora también marca un evento y no produce desplazamiento en este módulo.
      </div>
    </Card>
  );
}

function CommunicationModule({ scenario, imageUrl, text, onChange, onComplete }) {
  const value = text || '';
  return (
    <Card style={{ padding:24 }}>
      <div style={{ fontSize:12, fontWeight:700, color:BRAND.text.light, textTransform:'uppercase', letterSpacing:'0.08em', marginBottom:10 }}>Módulo 5</div>
      <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:22, color:BRAND.text.primary, margin:'0 0 8px' }}>Comunicación y registro</h3>
      {imageUrl ? (
        <div style={{ overflow:'hidden', borderRadius:20, border:`1px solid ${BRAND.border}`, margin:'0 0 16px' }}>
          <img src={imageUrl} alt="Escena de comunicación operacional" style={{ display:'block', width:'100%', height:'auto', aspectRatio:'16 / 9', objectFit:'cover' }} />
        </div>
      ) : (
        <p style={{ fontSize:14, color:BRAND.text.muted, lineHeight:1.7, margin:'0 0 16px' }}>{scenario}</p>
      )}
      <label style={{ display:'block', fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:8 }}>
        Describa lo ocurrido como si debiera dejarlo registrado en una bitácora.
      </label>
      <textarea value={value} maxLength={300} onChange={(event) => onChange(event.target.value)} rows={6} style={{ width:'100%', padding:'14px 16px', borderRadius:12, border:`1.5px solid ${BRAND.border}`, outline:'none', resize:'vertical', fontSize:14, lineHeight:1.6 }} />
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, marginTop:10 }}>
        <div style={{ fontSize:12, color:BRAND.text.light }}>Máximo 300 caracteres. Esta respuesta será revisada manualmente por supervisor o RR.HH.</div>
        <div style={{ fontSize:12, fontWeight:700, color:value.length > 280 ? BRAND.red.main : BRAND.text.light }}>{value.length} / 300</div>
      </div>
      <div style={{ marginTop:18 }}>
        <button onClick={() => onComplete(value)} disabled={!value.trim()} style={{ padding:'11px 18px', borderRadius:8, border:'none', background:!value.trim() ? '#BFD7CD' : BRAND.green.main, color:'white', fontSize:13, fontWeight:700, cursor:!value.trim() ? 'default' : 'pointer' }}>
          Continuar
        </button>
      </div>
    </Card>
  );
}

function AssessmentResult({ attempt, onBackToOverview, backLabel = 'Volver al panel' }) {
  const scores = attempt.moduleScores || {};
  const displayScore = getAssessmentDisplayScore(attempt);
  const scaleRows = getAssessmentScaleRows();
  return (
    <div style={{ display:'grid', gap:20 }}>
      <Card style={{ padding:24 }}>
        <div style={{ fontSize:12, fontWeight:700, color:BRAND.text.light, textTransform:'uppercase', letterSpacing:'0.08em', marginBottom:8 }}>
          eGuardian Operator Assessment
        </div>
        <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:28, color:BRAND.text.primary, margin:'0 0 8px' }}>
          {attempt.applicant.name}
        </h3>
        <div style={{ fontSize:14, color:BRAND.text.muted, marginBottom:18 }}>
          Resultado {attempt.totalScore === null ? 'preliminar' : 'final'} de la evaluación.
        </div>
        <div style={{ display:'flex', gap:14, alignItems:'baseline', flexWrap:'wrap', marginBottom:14 }}>
          <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:42, color:BRAND.text.primary }}>
            {displayScore === null ? '--' : displayScore}
          </div>
          <div style={{ fontSize:14, color:BRAND.text.light }}> / 100</div>
        </div>
        <div style={{ fontSize:13, color:BRAND.text.muted, marginBottom:10 }}>
          Nota obtenida: <strong style={{ color:BRAND.text.primary }}>{displayScore === null ? '--' : displayScore}</strong>
        </div>
        <div style={{ fontSize:18, fontWeight:700, color:attempt.status === 'REVIEWED' ? BRAND.green.dark : BRAND.amber.dark, marginBottom:10 }}>
          {attempt.classification || 'Pendiente de revisión'}
        </div>
        {attempt.totalScore === null && (
          <div style={{ fontSize:13, color:BRAND.text.muted }}>
            Comunicación escrita pendiente de revisión.
          </div>
        )}
      </Card>

      <Card style={{ padding:24 }}>
        <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:18 }}>
          Resultados por dimensión
        </div>
        <div style={{ display:'grid', gap:16 }}>
          <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Competencia digital</div>{scoreBar(scores.digital, 15, BRAND.green.main)}</div>
          <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Observación</div>{scoreBar(scores.observation, 20, BRAND.blue.main)}</div>
          <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Atención sostenida</div>{scoreBar(scores.attention, 20, BRAND.red.main)}</div>
          <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Criterio operacional</div>{scoreBar(scores.criterion, 20, BRAND.amber.main)}</div>
          <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Comunicación</div>{scoreBar(scores.communication || 0, 15, BRAND.purple.main)}</div>
          <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Disciplina</div>{scoreBar(scores.discipline, 10, BRAND.green.dark)}</div>
        </div>
      </Card>

      <Card style={{ padding:24 }}>
        <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:8 }}>
          Escala de calificaciÃ³n
        </div>
        <div style={{ fontSize:13, color:BRAND.text.muted, marginBottom:18 }}>
          La escala definida para el eGuardian Operator Assessment V1 es sobre 100 puntos.
        </div>
        <div style={{ display:'grid', gap:10 }}>
          {scaleRows.map(([range, classification, interpretation, color]) => (
            <div key={range} style={{ display:'grid', gridTemplateColumns:'110px minmax(220px, 1fr) minmax(260px, 1.4fr)', gap:16, alignItems:'start', padding:'14px 0', borderTop:`1px solid ${BRAND.border}` }}>
              <div style={{ fontSize:14, fontWeight:700, color:BRAND.text.primary }}>{range}</div>
              <div style={{ display:'flex', alignItems:'center', gap:8, fontSize:14, color:BRAND.text.primary, fontWeight:600 }}>
                <span style={{ width:13, height:13, borderRadius:'50%', background:color, display:'inline-block', flexShrink:0 }} />
                <span>{classification}</span>
              </div>
              <div style={{ fontSize:14, color:BRAND.text.secondary, lineHeight:1.6 }}>{interpretation}</div>
            </div>
          ))}
        </div>
      </Card>

      <div>
        <button onClick={onBackToOverview} style={{ padding:'10px 16px', borderRadius:8, border:`1px solid ${BRAND.border}`, background:'white', color:BRAND.text.secondary, cursor:'pointer' }}>
          {backLabel}
        </button>
      </div>
    </div>
  );
}

function AssessmentDetail({ attempt, currentUser, onBack, onReviewed }) {
  const isCompact = useViewportMatch(920);
  const metrics = attempt.sustainedMetrics || {};
  const manual = attempt.communicationManual || {};
  const [review, setReview] = useState({
    identification: manual.identification ?? 0,
    clarity: manual.clarity ?? 0,
    objectivity: manual.objectivity ?? 0,
    order: manual.order ?? 0,
  });
  const [saving, setSaving] = useState(false);

  async function submitReview() {
    setSaving(true);
    try {
      const response = await apiCall(`/api/operator-assessment/${attempt.id}/review`, {
        method: 'POST',
        body: JSON.stringify({
          ...review,
          reviewedBy: currentUser?.name || 'Supervisor',
        }),
      });
      onReviewed(response.attempt);
    } finally {
      setSaving(false);
    }
  }

  return (
    <div style={{ display:'grid', gap:20 }}>
      <button onClick={onBack} style={{ width:'fit-content', display:'flex', alignItems:'center', gap:6, background:'none', border:'none', cursor:'pointer', color:BRAND.text.muted, fontSize:13, fontWeight:500, padding:0 }}>
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M10 3L5 8L10 13" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>
        Volver al panel
      </button>

      <Card style={{ padding:24 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:isCompact ? 'stretch' : 'flex-start', flexDirection:isCompact ? 'column' : 'row', gap:14 }}>
          <div>
            <div style={{ fontSize:12, fontWeight:700, color:BRAND.text.light, textTransform:'uppercase', letterSpacing:'0.08em', marginBottom:8 }}>Ficha individual del postulante</div>
            <h3 style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:28, color:BRAND.text.primary, margin:'0 0 6px' }}>{attempt.applicant.name}</h3>
            <div style={{ fontSize:13, color:BRAND.text.muted, lineHeight:1.7 }}>
              {attempt.applicant.email}<br/>
              Fecha evaluación: {formatAssessmentDateTime(attempt.startedAt)}<br/>
              Duración total: {formatSeconds(attempt.durationSeconds)}
            </div>
          </div>
          <div style={{ minWidth:isCompact ? 'auto' : 220, padding:'18px 20px', borderRadius:16, background:'#FAFCFA', border:`1px solid ${BRAND.border}` }}>
            <div style={{ fontSize:12, color:BRAND.text.light, marginBottom:6 }}>Operator Score</div>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:800, fontSize:38, color:BRAND.text.primary, lineHeight:1 }}>{attempt.totalScore ?? '--'}</div>
            <div style={{ fontSize:13, fontWeight:700, color:attempt.status === 'REVIEWED' ? BRAND.green.dark : BRAND.amber.dark, marginTop:8 }}>{attempt.classification || 'Pendiente revisión'}</div>
          </div>
        </div>
      </Card>

      <div style={{ display:'grid', gridTemplateColumns:isCompact ? '1fr' : 'minmax(0, 1fr) 340px', gap:20 }}>
        <div style={{ display:'grid', gap:20 }}>
          <Card style={{ padding:24 }}>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:18 }}>Resultados por dimensión</div>
            <div style={{ display:'grid', gap:16 }}>
              <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Competencia digital</div>{scoreBar(attempt.moduleScores?.digital, 15, BRAND.green.main)}</div>
              <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Observación</div>{scoreBar(attempt.moduleScores?.observation, 20, BRAND.blue.main)}</div>
              <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Atención sostenida</div>{scoreBar(attempt.moduleScores?.attention, 20, BRAND.red.main)}</div>
              <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Criterio operacional</div>{scoreBar(attempt.moduleScores?.criterion, 20, BRAND.amber.main)}</div>
              <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Comunicación</div>{scoreBar(attempt.moduleScores?.communication || 0, 15, BRAND.purple.main)}</div>
              <div><div style={{ fontSize:13, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>Disciplina</div>{scoreBar(attempt.moduleScores?.discipline, 10, BRAND.green.dark)}</div>
            </div>
          </Card>

          <Card style={{ padding:24 }}>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:18 }}>Atención sostenida</div>
            <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(180px, 1fr))', gap:14 }}>
              {[
                ['Eventos totales', metrics.eventsTotal],
                ['Eventos detectados', metrics.eventsDetected],
                ['Eventos omitidos', metrics.eventsOmitted],
                ['% detección', metrics.detectionRate],
                ['Falsas alarmas', metrics.falseAlarms],
                ['Tiempo promedio', metrics.averageReactionMs ? `${metrics.averageReactionMs} ms` : '--'],
              ].map(([label, value]) => (
                <div key={label} style={{ padding:'14px 16px', borderRadius:12, border:`1px solid ${BRAND.border}`, background:'#FCFDFC' }}>
                  <div style={{ fontSize:11, color:BRAND.text.light, textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:6 }}>{label}</div>
                  <div style={{ fontSize:20, fontWeight:800, color:BRAND.text.primary }}>{value ?? '--'}</div>
                </div>
              ))}
            </div>
          </Card>

          <Card style={{ padding:24 }}>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:12 }}>Comunicación escrita</div>
            <div style={{ padding:'14px 16px', borderRadius:12, border:`1px solid ${BRAND.border}`, background:'#FCFDFC', fontSize:14, color:BRAND.text.secondary, lineHeight:1.7, whiteSpace:'pre-wrap' }}>
              {attempt.communicationText || 'Sin respuesta registrada.'}
            </div>
          </Card>
        </div>

        <div style={{ display:'grid', gap:20 }}>
          <Card style={{ padding:24 }}>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:14 }}>Fortalezas</div>
            <div style={{ display:'grid', gap:10 }}>
              {(attempt.strengths?.length ? attempt.strengths : ['Aún no se registran fortalezas automáticas.']).map((item) => (
                <div key={item} style={{ fontSize:13, color:BRAND.text.secondary, lineHeight:1.6, padding:'10px 12px', borderRadius:10, background:BRAND.green.light }}>
                  {item}
                </div>
              ))}
            </div>
          </Card>

          <Card style={{ padding:24 }}>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:14 }}>Alertas</div>
            <div style={{ display:'grid', gap:10 }}>
              {(attempt.alerts?.length ? attempt.alerts : ['Sin alertas automáticas.']).map((item) => (
                <div key={item} style={{ fontSize:13, color:BRAND.text.secondary, lineHeight:1.6, padding:'10px 12px', borderRadius:10, background:BRAND.amber.light }}>
                  {item}
                </div>
              ))}
            </div>
          </Card>

          <Card style={{ padding:24 }}>
            <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:18, color:BRAND.text.primary, marginBottom:14 }}>Puntuar comunicación</div>
            <div style={{ display:'grid', gap:12 }}>
              {[
                ['Identificación', 'identification', 5],
                ['Claridad', 'clarity', 4],
                ['Objetividad', 'objectivity', 3],
                ['Orden / síntesis', 'order', 3],
              ].map(([label, key, max]) => (
                <div key={key}>
                  <label style={{ display:'block', fontSize:12, fontWeight:700, color:BRAND.text.secondary, marginBottom:6 }}>{label} / {max}</label>
                  <input type="number" min="0" max={max} value={review[key]} onChange={(event) => setReview((prev) => ({ ...prev, [key]: Number(event.target.value) }))} style={{ width:'100%', padding:'11px 12px', borderRadius:10, border:`1.5px solid ${BRAND.border}`, outline:'none' }} />
                </div>
              ))}
            </div>
            <button onClick={submitReview} disabled={saving} style={{ width:'100%', marginTop:16, padding:'12px 18px', borderRadius:8, border:'none', background:saving ? '#BFD7CD' : BRAND.green.main, color:'white', fontSize:13, fontWeight:700, cursor:saving ? 'wait' : 'pointer' }}>
              {saving ? 'Guardando evaluación…' : 'Guardar evaluación'}
            </button>
          </Card>
        </div>
      </div>
    </div>
  );
}

function OperatorAssessmentModule({ currentUser = null, guestAccess = null }) {
  const [mode, setMode] = useState('overview');
  const [attempts, setAttempts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [submitting, setSubmitting] = useState(false);
  const [startError, setStartError] = useState('');
  const [submitError, setSubmitError] = useState('');
  const [activeAttemptId, setActiveAttemptId] = useState(() => {
    try {
      return localStorage.getItem(getAssessmentAttemptStorageKey(guestAccess)) || null;
    } catch (_error) {
      return null;
    }
  });
  const [activeAttempt, setActiveAttempt] = useState(null);
  const [definition, setDefinition] = useState(null);
  const [draft, setDraft] = useState({});
  const [moduleIndex, setModuleIndex] = useState(0);
  const [selectedAttempt, setSelectedAttempt] = useState(null);
  const [generatedAccessLink, setGeneratedAccessLink] = useState('');
  const [accessLinkError, setAccessLinkError] = useState('');
  const [generatingAccessLink, setGeneratingAccessLink] = useState(false);
  const attemptStorageKey = useMemo(() => getAssessmentAttemptStorageKey(guestAccess), [guestAccess?.token]);

  const moduleFlow = useMemo(() => ([
    { key:'availability', label:'Disponibilidad operacional' },
    { key:'digital', label:'Competencia digital' },
    { key:'observation', label:'Observación visual' },
    { key:'attention', label:'Atención sostenida' },
    { key:'criterion', label:'Criterio operacional' },
    { key:'communication', label:'Comunicación y registro' },
    { key:'discipline', label:'Disciplina operacional' },
  ]), []);

  useEffect(() => {
    if (guestAccess) {
      if (activeAttemptId) {
        resumeActiveAttempt();
      } else if (mode === 'overview') {
        setLoading(false);
        setMode('start');
      }
      return;
    }
    loadOverview();
  }, [guestAccess, activeAttemptId, mode]);

  async function loadOverview() {
    if (guestAccess) {
      setAttempts([]);
      setLoading(false);
      return;
    }
    setLoading(true);
    try {
      const response = await apiCall('/api/operator-assessment');
      setAttempts(Array.isArray(response.attempts) ? response.attempts : []);
    } finally {
      setLoading(false);
    }
  }

  async function persistDraft(nextDraft, nextModuleKey, nextModuleIndex) {
    setDraft(nextDraft);
    if (!activeAttemptId) return;

    const progressPercent = Math.round((nextModuleIndex / moduleFlow.length) * 100);
    try {
      await apiCall(`/api/operator-assessment/${activeAttemptId}`, {
        method:'PATCH',
        body: JSON.stringify({
          draft: nextDraft,
          currentModuleKey: nextModuleKey,
          progressPercent,
        }),
      });
      localStorage.setItem(attemptStorageKey, String(activeAttemptId));
    } catch (error) {
      console.error('No fue posible guardar el progreso del assessment.', error);
    }
  }

  function updateDraftField(field, value) {
    const nextDraft = { ...draft, [field]: value };
    persistDraft(nextDraft, moduleFlow[moduleIndex].key, moduleIndex);
  }

  async function beginProcess({ name, rut, email, phone }) {
    setSubmitting(true);
    setStartError('');
    try {
      const response = await apiCall('/api/operator-assessment', {
        method: 'POST',
        body: JSON.stringify({ name, rut, email, phone }),
      });
      if (!response?.attempt || !response?.definition) {
        throw new Error(response?.error || 'No fue posible iniciar la evaluación.');
      }
      setDefinition(response.definition);
      setActiveAttempt(response.attempt);
      setActiveAttemptId(String(response.attempt.id));
      setDraft(response.attempt.draft || {});
      setModuleIndex(Math.max(0, moduleFlow.findIndex((item) => item.key === (response.attempt.currentModuleKey || 'availability'))));
      localStorage.setItem(attemptStorageKey, String(response.attempt.id));
      setMode('running');
      if (!guestAccess) {
        await loadOverview();
      }
    } catch (error) {
      console.error('No fue posible iniciar la evaluación.', error);
      setStartError(error instanceof Error ? error.message : 'No fue posible iniciar la evaluación.');
    } finally {
      setSubmitting(false);
    }
  }

  async function resumeActiveAttempt() {
    if (!activeAttemptId) return;
    const response = await apiCall(`/api/operator-assessment/${activeAttemptId}`);
    setDefinition(response.definition);
    setActiveAttempt(response.attempt);
    setDraft(response.attempt.draft || {});
    setModuleIndex(Math.max(0, moduleFlow.findIndex((item) => item.key === (response.attempt.currentModuleKey || 'availability'))));
    setMode(response.attempt.status === 'IN_PROGRESS' ? 'running' : 'result');
  }

  async function openAttempt(id) {
    const response = await apiCall(`/api/operator-assessment/${id}`);
    setSelectedAttempt(response.attempt);
    setMode('detail');
  }

  async function generateAccessLink() {
    setGeneratingAccessLink(true);
    setAccessLinkError('');
    setGeneratedAccessLink('');
    try {
      const response = await apiCall('/api/operator-assessment/access-links', {
        method: 'POST',
      });
      if (!response?.url) {
        throw new Error(response?.error || 'No fue posible generar el link de acceso.');
      }
      setGeneratedAccessLink(response.url);
    } catch (error) {
      console.error('No fue posible generar el link de acceso.', error);
      setAccessLinkError(error instanceof Error ? error.message : 'No fue posible generar el link de acceso.');
    } finally {
      setGeneratingAccessLink(false);
    }
  }

  function advanceModule(nextDraftValue) {
    const nextModuleIndex = moduleIndex + 1;
    if (nextModuleIndex >= moduleFlow.length) {
      submitAssessment(nextDraftValue);
      return;
    }

    setModuleIndex(nextModuleIndex);
    persistDraft(nextDraftValue, moduleFlow[nextModuleIndex].key, nextModuleIndex);
  }

  async function submitAssessment(nextDraftValue) {
    if (!activeAttemptId) return;
    const submittedAt = new Date().toISOString();
    const payloadDraft = nextDraftValue || draft;
    setSubmitting(true);
    setSubmitError('');
    try {
      const response = await apiCall(`/api/operator-assessment/${activeAttemptId}/submit`, {
        method:'POST',
        body: JSON.stringify({
          draft: payloadDraft,
          submittedAt,
        }),
      });
      if (!response?.attempt) {
        throw new Error(response?.error || 'No fue posible cerrar la evaluación.');
      }
      setActiveAttempt(response.attempt);
      setSelectedAttempt(response.attempt);
      setMode('result');
      setDraft(payloadDraft);
      localStorage.removeItem(attemptStorageKey);
      if (guestAccess?.storageKey) {
        sessionStorage.removeItem(guestAccess.storageKey);
      }
      setActiveAttemptId(null);
      if (!guestAccess) {
        await loadOverview();
      }
    } catch (error) {
      console.error('No fue posible cerrar la evaluación.', error);
      setSubmitError(error instanceof Error ? error.message : 'No fue posible cerrar la evaluación.');
    } finally {
      setSubmitting(false);
    }
  }

  function applyReviewedAttempt(updatedAttempt) {
    setSelectedAttempt(updatedAttempt);
    setAttempts((current) => current.map((attempt) => attempt.id === updatedAttempt.id ? updatedAttempt : attempt));
  }

  const totalProgress = Math.round((moduleIndex / moduleFlow.length) * 100);
  const activeModule = moduleFlow[moduleIndex];

  if (mode === 'start') {
    return <StartAssessmentFormV2 onCancel={() => setMode(guestAccess ? 'start' : 'overview')} onSubmit={beginProcess} loading={submitting} errorMessage={startError} />;
  }

  if (mode === 'detail' && selectedAttempt) {
    return <AssessmentDetail attempt={selectedAttempt} currentUser={currentUser} onBack={() => setMode('overview')} onReviewed={applyReviewedAttempt} />;
  }

  if (mode === 'result' && (selectedAttempt || activeAttempt)) {
    return <AssessmentResult attempt={selectedAttempt || activeAttempt} onBackToOverview={() => guestAccess ? window.top.location.assign('/assessment-access-complete') : setMode('overview')} backLabel={guestAccess ? 'Cerrar' : 'Volver al panel'} />;
  }

  if (mode === 'running' && definition && activeAttempt) {
    return (
      <div style={{ display:'grid', gap:20 }}>
        <Card style={{ padding:20 }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, flexWrap:'wrap' }}>
            <div>
              <div style={{ fontSize:12, fontWeight:700, color:BRAND.text.light, textTransform:'uppercase', letterSpacing:'0.08em' }}>
                {definition.title}
              </div>
              <div style={{ fontFamily:'Space Grotesk,sans-serif', fontWeight:700, fontSize:20, color:BRAND.text.primary, marginTop:4 }}>
                {activeAttempt.applicant.name}
              </div>
            </div>
            <div style={{ minWidth:220 }}>
              <div style={{ display:'flex', justifyContent:'space-between', fontSize:12, color:BRAND.text.light, marginBottom:6 }}>
                <span>{activeModule.label}</span>
                <span>{totalProgress}%</span>
              </div>
              <ProgressBar value={totalProgress} color={BRAND.green.main} height={8}/>
            </div>
          </div>
        </Card>

        {activeModule.key === 'availability' && (
          <AvailabilityModule
            draft={{ shiftAvailability: draft.shiftAvailability, nightShiftExperience: draft.nightShiftExperience }}
            onChange={(value) => {
              const nextDraft = { ...draft, shiftAvailability: value.shiftAvailability, nightShiftExperience: value.nightShiftExperience };
              persistDraft(nextDraft, moduleFlow[moduleIndex].key, moduleIndex);
            }}
            onComplete={(value) => {
              const nextDraft = { ...draft, shiftAvailability: value.shiftAvailability, nightShiftExperience: value.nightShiftExperience };
              setDraft(nextDraft);
              advanceModule(nextDraft);
            }}
          />
        )}

        {activeModule.key === 'digital' && (
          <MultipleChoiceModule
            title="Competencia digital"
            description="Preguntas de manejo básico de computador y multitarea operacional."
            questions={definition.digitalQuestions}
            answers={draft.digitalAnswers}
            onChange={(value) => updateDraftField('digitalAnswers', value)}
            onComplete={(value) => {
              const nextDraft = { ...draft, digitalAnswers: value };
              setDraft(nextDraft);
              advanceModule(nextDraft);
            }}
            pointsLabel="5 preguntas · 15 puntos"
          />
        )}

        {activeModule.key === 'observation' && (
          <ObservationModule
            scenes={definition.observationScenes}
            answers={draft.observationAnswers}
            onChange={(value) => updateDraftField('observationAnswers', value)}
            onComplete={(value) => {
              const nextDraft = { ...draft, observationAnswers: value };
              setDraft(nextDraft);
              advanceModule(nextDraft);
            }}
          />
        )}

        {activeModule.key === 'attention' && (
          <SustainedAttentionModule
            stimuli={definition.attentionStimuli}
            responses={draft.attentionResponses}
            onChange={(value) => updateDraftField('attentionResponses', value)}
            onComplete={(value) => {
              const nextDraft = { ...draft, attentionResponses: value };
              setDraft(nextDraft);
              advanceModule(nextDraft);
            }}
          />
        )}

        {activeModule.key === 'criterion' && (
          <MultipleChoiceModule
            title="Criterio operacional"
            description="Casos breves para priorización y análisis de contexto."
            questions={definition.criterionQuestions}
            answers={draft.criterionAnswers}
            onChange={(value) => updateDraftField('criterionAnswers', value)}
            onComplete={(value) => {
              const nextDraft = { ...draft, criterionAnswers: value };
              setDraft(nextDraft);
              advanceModule(nextDraft);
            }}
            pointsLabel="5 casos · 20 puntos"
          />
        )}

        {activeModule.key === 'communication' && (
          <CommunicationModule
            scenario={definition.communicationScenario}
            imageUrl={definition.communicationImageUrl}
            text={draft.communicationText}
            onChange={(value) => updateDraftField('communicationText', value)}
            onComplete={(value) => {
              const nextDraft = { ...draft, communicationText: value };
              setDraft(nextDraft);
              advanceModule(nextDraft);
            }}
          />
        )}

        {activeModule.key === 'discipline' && (
          <MultipleChoiceModule
            title="Disciplina operacional"
            description="Conductas esperadas en continuidad, foco y cumplimiento de normas."
            questions={definition.disciplineQuestions}
            answers={draft.disciplineAnswers}
            onChange={(value) => updateDraftField('disciplineAnswers', value)}
            onComplete={(value) => {
              const nextDraft = { ...draft, disciplineAnswers: value };
              setDraft(nextDraft);
              advanceModule(nextDraft);
            }}
            pointsLabel="5 preguntas · 10 puntos"
          />
        )}

        {submitting && (
          <div style={{ fontSize:13, color:BRAND.text.muted }}>Enviando evaluación…</div>
        )}
        {submitError && (
          <div style={{ fontSize:13, color:BRAND.red.dark, background:BRAND.red.light, padding:'10px 12px', borderRadius:10 }}>
            {submitError}
          </div>
        )}
      </div>
    );
  }

  return (
    <AssessmentOverview
      attempts={attempts}
      onStart={() => setMode('start')}
      onOpenAttempt={openAttempt}
      onResumeAttempt={resumeActiveAttempt}
      onGenerateAccessLink={generateAccessLink}
      activeAttemptId={activeAttemptId}
      loading={loading}
      generatingAccessLink={generatingAccessLink}
      accessLink={generatedAccessLink}
      accessLinkError={accessLinkError}
    />
  );
}

window.OperatorAssessmentModule = OperatorAssessmentModule;



