/* Runner - bloc production/dictee : produire -> corriger -> retry -> reapparition differee. Extrait de runner.jsx (decoupage 2026-06-12, sans changement de logique). */

function RunnerProductionBlock({ exercise, glossFor, grade, onDone }) {
  const p = exercise.prompt || {};
  const dictation = exercise.type === "dictation_line" || (exercise.source && exercise.source.kind === "dictation_scene");
  const [answer, setAnswer] = useState("");
  const [dictationLevelId, setDictationLevelId] = useState("blocks");
  const [unitAnswersByLevel, setUnitAnswersByLevel] = useState({});
  const [unitChecked, setUnitChecked] = useState(null);
  const [checked, setChecked] = useState(null);
  // Boucle de production (AUDIT_PEDAGOGIQUE §6/levier #4) : produire -> corriger ->
  // RETRY immédiat -> réapparition différée si l'item a d'abord été raté.
  const [attempts, setAttempts] = useState(1);
  const [revealModel, setRevealModel] = useState(false);
  const [firstTags, setFirstTags] = useState([]);
  const [retryHint, setRetryHint] = useState("");
  useEffect(() => { setAnswer(""); setDictationLevelId("blocks"); setUnitAnswersByLevel({}); setUnitChecked(null); setChecked(null); setAttempts(1); setRevealModel(false); setFirstTags([]); setRetryHint(""); }, [exercise.id]);
  const must = p.must || [];
  const ex = exercise.expected || {};
  const model = dictation ? dictationExpectedForExercise(exercise) : (ex.model || ex.kr || "");
  const dictationLevels = dictation ? dictationLevelsForExercise(exercise) : [];
  const activeDictationLevel = dictationLevels.find(level => level.id === dictationLevelId) || dictationLevels.find(level => level.id === "blocks") || dictationLevels[0] || null;
  const activeDictationUnits = activeDictationLevel ? activeDictationLevel.units : [];
  const activeUnitAnswers = unitAnswersByLevel[activeDictationLevel && activeDictationLevel.id] || [];
  const dictationTags = Array.isArray(exercise.errorTags) && exercise.errorTags.length
    ? exercise.errorTags
    : ["son", "batchim", "espace", "particule", "terminaison"];
  const checkUnits = () => {
    const res = compareDictationUnits(activeDictationUnits, activeUnitAnswers);
    setUnitChecked(res);
  };
  const setUnitAnswer = (index, value) => {
    const levelId = (activeDictationLevel && activeDictationLevel.id) || "blocks";
    setUnitAnswersByLevel(prev => {
      const next = (prev[levelId] || []).slice();
      next[index] = value;
      return Object.assign({}, prev, { [levelId]: next });
    });
    setUnitChecked(null);
  };
  const check = () => {
    const expected = dictationExpectedForExercise(exercise);
    const res = dictation && expected ? compareDictationAnswer(expected, answer) : grade(answer);
    setChecked(res);
    if (res && !res.correct && attempts === 1) setFirstTags(Array.isArray(res.tags) ? res.tags : []);
  };
  const feedbackTags = checked && Array.isArray(checked.tags) ? checked.tags : [];
  const contrast = checked && !checked.correct ? contrastFor(model, answer, dictation) : null;
  const feedbackHint = checked && !checked.correct && !contrast ? explanationForTags(feedbackTags) : "";

  const wrong = !!checked && !checked.correct;
  const correctedAfterRetry = !!checked && checked.correct && attempts >= 2;
  const canRetry = wrong && attempts === 1 && !revealModel; // 1er essai raté : on rejoue
  const finalWrong = wrong && (attempts >= 2 || revealModel);
  const showModel = !!checked && (checked.correct || finalWrong); // le modèle n'apparaît qu'à la fin

  const retry = () => {
    setRetryHint(contrast ? contrast.reason : explanationForTags(firstTags));
    setAnswer(""); setChecked(null); setAttempts(2);
  };
  const finishCorrect = () => onDone({ answer, correct: true, correctedAfterRetry, errorTags: correctedAfterRetry ? firstTags : [], attempts, feedbackShown: true });
  const finishWrong = () => onDone({ answer, correct: false, errorTags: feedbackTags.length ? feedbackTags : firstTags, attempts, feedbackShown: true });

  const statusLabel = !checked ? ""
    : checked.correct
      ? (correctedAfterRetry ? "Corrigé après reprise" : (dictation ? dictationFeedbackLabel(checked) : "Correct"))
      : (dictation ? dictationFeedbackLabel(checked) : "À revoir");
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <div className="card" style={{ padding: 24, display: "grid", gap: 12 }}>
        <Eyebrow>{dictation ? "Dictée — écrivez ce que vous entendez" : (p.mode === "dialogue_reply" ? "Réponse de dialogue" : "Production guidée")}{attempts > 1 ? " · reprise" : ""}</Eyebrow>
        {!dictation && p.contextKr && (
          <div style={{ display: "grid", gap: 5, padding: "10px 12px", borderRadius: "var(--radius-sm)", background: "var(--card-2)", border: "1px solid var(--rule)" }}>
            <div className="eyebrow">Réplique précédente</div>
            <PhraseWords text={p.contextKr} glossFor={glossFor} audioUrl={p.audioUrl} audioRef={p.audioRef || p.contextKr} size={22} justify="flex-start" />
            <GlossAid text={p.contextKr} glossFor={glossFor} />
            {p.contextFr && <span className="faint" style={{ fontSize: 12.5 }}>{p.contextFr}</span>}
          </div>
        )}
        {dictation
          ? (
            <div style={{ display: "grid", gap: 12 }}>
              <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
                <span className="chip">1 · écouter</span>
                <SpeakBtn text="" audioRef={(p.audio && (p.audio.ref || p.audio.text)) || ""} audioUrl={(p.audio && p.audio.url) || ""} size={42} title="Réécouter la ligne" />
                {p.speaker && <span className="faint" style={{ fontSize: 12.5 }}>locuteur {p.speaker}</span>}
              </div>
              {dictationLevels.length > 0 && (
                <div style={{ display: "grid", gap: 10, padding: "12px 14px", borderRadius: "var(--radius-sm)", background: "var(--card-2)", border: "1px solid var(--rule)" }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                    <span className="chip">2 · niveaux</span>
                    <span className="faint" style={{ fontSize: 12.5 }}>Choisissez un niveau, puis reconstruisez avant la phrase entière.</span>
                  </div>
                  <div style={{ display: "flex", gap: 7, flexWrap: "wrap" }}>
                    {dictationLevels.map((level) => (
                      <button key={level.id} onClick={() => { setDictationLevelId(level.id); setUnitChecked(null); }} className="chip" style={{ cursor: "pointer", background: activeDictationLevel && activeDictationLevel.id === level.id ? "var(--accent-tint)" : "var(--card)", color: activeDictationLevel && activeDictationLevel.id === level.id ? "var(--accent-deep)" : "var(--ink-soft)", borderColor: activeDictationLevel && activeDictationLevel.id === level.id ? "transparent" : "var(--rule)" }}>
                        {level.order} · {polishRunnerText(level.label)} <span className="faint" style={{ fontSize: 11 }}>{level.units.length}</span>
                      </button>
                    ))}
                  </div>
                  {activeDictationLevel && activeDictationLevel.audioTags && activeDictationLevel.audioTags.length > 0 && (
                    <div className="faint" style={{ fontSize: 12 }}>À écouter ici : {activeDictationLevel.audioTags.join(" · ")}</div>
                  )}
                  {activeDictationLevel && activeDictationLevel.kind === "line" ? (
                    <div className="faint" style={{ fontSize: 12.5 }}>La phrase entière se valide dans le champ principal ci-dessous.</div>
                  ) : (
                    <>
                      <div style={{ display: "grid", gap: 8 }}>
                        {activeDictationUnits.map((unit, index) => {
                          const result = unitChecked && unitChecked.results && unitChecked.results[index];
                          const bg = result ? (result.correct ? "var(--good-tint)" : "var(--warn-tint)") : "var(--card)";
                          return (
                            <div key={unit.id || index} style={{ display: "grid", gridTemplateColumns: "auto minmax(0, 1fr) auto", gap: 8, alignItems: "center" }}>
                              <SpeakBtn text={unit.text} audioRef={(unit.audio && (unit.audio.ref || unit.audio.id)) || unit.text} audioUrl={unit.audio && unit.audio.url} size={32} title={`Écouter ${polishRunnerText(activeDictationLevel.label || activeDictationLevel.kind || "niveau").toLowerCase()} ${index + 1}`} />
                              <input value={activeUnitAnswers[index] || ""} onChange={ev => setUnitAnswer(index, ev.target.value)} className="kr" lang="ko" autoCapitalize="off" autoCorrect="off" spellCheck={false} placeholder={`${activeDictationLevel.kind} ${index + 1}`} style={{ minWidth: 0, padding: "9px 11px", borderRadius: "var(--radius-sm)", border: "1px solid var(--rule)", background: bg, color: "var(--ink)", fontSize: 18 }} />
                              {result && <span className="faint" style={{ fontSize: 12 }}>{result.correct ? "OK" : dictationFeedbackLabel(result)}</span>}
                            </div>
                          );
                        })}
                      </div>
                      <div style={{ display: "flex", justifyContent: "space-between", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
                        {unitChecked && !unitChecked.correct && unitChecked.tags.length > 0 && <span className="faint" style={{ fontSize: 12 }}>Tags niveau : {unitChecked.tags.join(" · ")}</span>}
                        <span style={{ flex: 1 }} />
                        <Btn kind="secondary" size="sm" icon="check" disabled={!activeUnitAnswers.some(v => String(v || "").trim())} onClick={checkUnits}>Vérifier ce niveau</Btn>
                      </div>
                    </>
                  )}
                </div>
              )}
              <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                <span className="chip">3 · phrase entière</span>
                <span className="faint" style={{ fontSize: 12.5 }}>Le résultat final est noté sur cette phrase.</span>
              </div>
            </div>
          )
          : <div style={{ fontFamily: "var(--serif)", fontSize: 24, color: "var(--accent-deep)" }}>{p.fr}</div>}
        {p.pattern && <div className="faint" style={{ fontSize: 13 }}>Patron : <span className="kr">{p.pattern}</span></div>}
        {must.length > 0 && <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>{must.map((m, i) => <span key={i} className="chip kr" style={{ fontSize: 12 }}>{m}</span>)}</div>}
        {attempts > 1 && !checked && retryHint && (
          <div className="fade-up" style={{ fontSize: 13, padding: "8px 12px", borderRadius: "var(--radius-sm)", background: "var(--accent-tint)", color: "var(--accent-deep)" }}>Indice : {retryHint}</div>
        )}
        <textarea value={answer} onChange={ev => { setAnswer(ev.target.value); setChecked(null); setRevealModel(false); }} rows={2} placeholder={attempts > 1 ? "Reprenez en corrigeant l'écart…" : "Votre réponse en coréen…"} className="kr" lang="ko" autoCapitalize="off" autoCorrect="off" spellCheck={false}
          style={{ width: "100%", padding: "11px 13px", borderRadius: "var(--radius-sm)", border: "1px solid var(--rule)", background: "var(--card-2)", color: "var(--ink)", fontSize: 20, resize: "vertical" }} />
        <KoreanKeyboard value={answer} onChange={(next) => { setAnswer(next); setChecked(null); setRevealModel(false); }} compact />
        {checked && (
          <div className="fade-up" style={{ display: "grid", gap: 8, padding: "12px 14px", borderRadius: "var(--radius-sm)", background: checked.correct ? "var(--good-tint)" : "var(--warn-tint)" }}>
            <div style={{ fontWeight: 700, color: checked.correct ? "var(--good)" : "var(--warn)" }}>{statusLabel}</div>
            {showModel && <div>Modèle : <span className="kr" style={{ fontSize: 18 }}>{checked.expected}</span></div>}
            {dictation && !checked.correct && <div className="faint" style={{ fontSize: 13 }}>À écouter : {dictationTags.join(" · ")}</div>}
            {wrong && contrast && <ContrastFeedback contrast={contrast} />}
            {wrong && !contrast && feedbackHint && <div className="faint" style={{ fontSize: 13 }}>À retenir : {feedbackHint}</div>}
            {wrong && feedbackTags.length > 0 && <div className="faint" style={{ fontSize: 12 }}>Tags : {feedbackTags.join(" · ")}</div>}
            {canRetry && <div className="faint" style={{ fontSize: 12.5 }}>Reprends tout de suite en corrigeant juste cet écart — le modèle reste caché.</div>}
            {finalWrong && <div className="faint" style={{ fontSize: 12.5 }}>Ce bloc n'est pas validé : la séance le remettra en reprise.</div>}
          </div>
        )}
      </div>
      <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, flexWrap: "wrap" }}>
        {!checked && <Btn kind="secondary" icon="eye" disabled={!answer.trim()} onClick={check}>Vérifier</Btn>}
        {checked && checked.correct && <Btn kind="primary" icon="check" onClick={finishCorrect}>Continuer</Btn>}
        {canRetry && <>
          <Btn kind="secondary" icon="eye" onClick={() => setRevealModel(true)}>Voir le modèle</Btn>
          <Btn kind="primary" icon="refresh" disabled={!answer.trim()} onClick={retry}>Réessayer</Btn>
        </>}
        {finalWrong && <Btn kind="primary" icon="refresh" onClick={finishWrong}>Continuer · à reprendre</Btn>}
      </div>
    </div>
  );
}

// Réparation = vrai mini-exercice objectif (AUDIT §réparation), plus de « J'ai
// compris » auto-déclaré. Deux modes pilotés par prompt.drill :
//  - choose_form : cloze à compléter en choisissant la bonne forme (이에요/예요, 이/가…).
//  - produce     : produire une micro-phrase qui réutilise l'ancre du patron.
