/* global React, window, Vex */
// Practice Lab — Note identification on the staff.
//
// Renders a single natural note via VexFlow and asks the user which
// letter it is (C/D/E/F/G/A/B). Octave is intentionally not asked —
// for a beginner exercise the answer space stays small.

(function () {
  'use strict';
  window.PracticeLab = window.PracticeLab || {};
  window.PracticeLab.ui = window.PracticeLab.ui || {};

  function vexAvailable() {
    // Coerce to a real boolean. Using && would return the Vex.Flow class
    // itself when truthy; React.useState(thatClass) would then INVOKE the
    // class as a lazy initializer (without `new`) and throw a class-
    // constructor error inside the component.
    return !!(typeof window.Vex !== 'undefined' && window.Vex && window.Vex.Flow);
  }

  // Render a single note on the given clef inside containerEl. Wipes the
  // container first so re-rendering on Next doesn't stack staves.
  function renderNote(containerEl, clef, letter, octave) {
    if (!vexAvailable() || !containerEl) return;
    containerEl.innerHTML = '';
    try {
      const VF = window.Vex.Flow;
      const width = 260;
      const height = 130;
      const renderer = new VF.Renderer(containerEl, VF.Renderer.Backends.SVG);
      renderer.resize(width, height);
      const ctx = renderer.getContext();
      ctx.setFont('Plus Jakarta Sans', 11);
      const stave = new VF.Stave(10, 12, width - 20);
      stave.addClef(clef);
      stave.setContext(ctx).draw();

      const key = letter.toLowerCase() + '/' + octave;
      const note = new VF.StaveNote({
        clef: clef,
        keys: [key],
        duration: 'w',
        autoStem: true,
      });
      const voice = new VF.Voice({ num_beats: 4, beat_value: 4 });
      voice.addTickables([note]);
      new VF.Formatter().joinVoices([voice]).format([voice], width - 80);
      voice.draw(ctx, stave);
    } catch (e) {
      // Render-time failure — fall back to a generic message. NEVER print
      // the answer letter here: leaking it gives the user a free correct
      // attempt and skews stats. Skip the round (the user can pick Next).
      try {
        containerEl.textContent = 'Staff failed to render — pick Next for a new question.';
        containerEl.style.padding = '24px';
        containerEl.style.textAlign = 'center';
        containerEl.style.color = '#999';
        containerEl.style.fontSize = '13px';
      } catch (e2) {}
    }
  }

  const ExerciseNoteStaff = function (props) {
    const difficulty = props.difficulty || 'beginner';
    const onAttempt = props.onAttempt || function () {};
    const onNext = props.onNext || function () {};

    const [prompt, setPrompt] = React.useState(null);
    const [picked, setPicked] = React.useState(null);
    const [vexReady, setVexReady] = React.useState(vexAvailable());
    const staffRef = React.useRef(null);
    const submittedRef = React.useRef(false);

    // Wait for VexFlow CDN script to finish loading (it's loaded at the
    // top of index.html so usually ready on mount, but be defensive).
    React.useEffect(function () {
      if (vexReady) return;
      const id = setInterval(function () {
        if (vexAvailable()) { setVexReady(true); clearInterval(id); }
      }, 100);
      return function () { clearInterval(id); };
    }, [vexReady]);

    // Build a new prompt each mount / difficulty change.
    React.useEffect(function () {
      const p = window.PracticeLab.theory.randomNoteOnStaff(difficulty);
      setPrompt(p);
      setPicked(null);
      submittedRef.current = false;
    }, [difficulty]);

    // Render the note when prompt + Vex are both ready.
    React.useEffect(function () {
      if (!prompt || !vexReady || !staffRef.current) return;
      renderNote(staffRef.current, prompt.clef, prompt.letter, prompt.octave);
    }, [prompt, vexReady]);

    function handlePick(letter) {
      if (picked || !prompt || submittedRef.current) return;
      submittedRef.current = true;
      setPicked(letter);
      const wasCorrect = letter === prompt.letter;
      onAttempt({
        exerciseType: 'note_staff',
        difficulty: difficulty,
        prompt: {
          clef: prompt.clef,
          note_letter: prompt.letter,
          octave: prompt.octave,
          midi: prompt.midi,
        },
        userAnswer: letter,
        correctAnswer: prompt.letter,
        wasCorrect: wasCorrect,
      });
    }

    if (!prompt) return null;

    const LETTERS = window.PracticeLab.theory.NOTE_LETTERS;

    return (
      <div>
        <div style={{ fontSize: 14, color: 'oklch(45% 0.04 265)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Note on staff</div>
        <div style={{ fontSize: 18, color: 'oklch(20% 0.04 265)', fontWeight: 600, marginBottom: 18 }}>What note is this? <span style={{ color: 'oklch(55% 0.04 265)', fontSize: 14, fontWeight: 500 }}>({prompt.clef} clef)</span></div>

        <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 24 }}>
          <div ref={staffRef} style={{
            background: '#fff',
            border: '1px solid oklch(92% 0.012 265)',
            borderRadius: 8,
            padding: 6,
            minHeight: 130,
            minWidth: 260,
            overflow: 'hidden',
          }}>
            {!vexReady && <div style={{ padding: 30, color: 'oklch(55% 0.04 265)', fontSize: 12 }}>Loading staff…</div>}
          </div>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(58px, 1fr))', gap: 8, maxWidth: 520, margin: '0 auto 18px' }}>
          {LETTERS.map(function (L) {
            const isPicked = picked === L;
            const isCorrect = L === prompt.letter;
            let bg = '#fff';
            let border = 'oklch(88% 0.02 265)';
            let color = 'oklch(20% 0.04 265)';
            if (picked) {
              if (isCorrect) { bg = 'oklch(94% 0.07 145)'; border = 'oklch(60% 0.16 145)'; color = 'oklch(28% 0.14 145)'; }
              else if (isPicked) { bg = 'oklch(95% 0.07 25)'; border = 'oklch(60% 0.18 25)'; color = 'oklch(32% 0.16 25)'; }
              else { bg = 'oklch(98% 0.005 265)'; color = 'oklch(55% 0.04 265)'; }
            }
            return (
              <button
                key={L}
                onClick={function () { handlePick(L); }}
                disabled={!!picked}
                style={{
                  padding: '14px 10px',
                  borderRadius: 10,
                  border: '1px solid ' + border,
                  background: bg,
                  color: color,
                  fontSize: 16,
                  fontWeight: 700,
                  cursor: picked ? 'default' : 'pointer',
                  fontFamily: "'Plus Jakarta Sans', sans-serif",
                  minHeight: 50,
                  transition: 'all 0.14s',
                }}
              >{L}</button>
            );
          })}
        </div>

        {picked && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            {picked === prompt.letter ? (
              <div style={{ fontSize: 14, fontWeight: 700, color: 'oklch(35% 0.16 145)' }}>Correct — that’s {prompt.letter}{prompt.octave} on the {prompt.clef} clef.</div>
            ) : (
              <div style={{ fontSize: 14, fontWeight: 700, color: 'oklch(38% 0.18 25)' }}>Not quite — the answer was {prompt.letter}.</div>
            )}
            <button
              onClick={onNext}
              style={{
                marginLeft: 'auto',
                padding: '10px 22px',
                borderRadius: 10,
                border: 'none',
                background: 'oklch(35% 0.16 265)',
                color: '#fff',
                fontSize: 13,
                fontWeight: 700,
                cursor: 'pointer',
                fontFamily: "'Plus Jakarta Sans', sans-serif",
              }}
            >Next</button>
          </div>
        )}
      </div>
    );
  };

  window.PracticeLab.ui.ExerciseNoteStaff = ExerciseNoteStaff;
})();
