/**
 * Integrierter Slot-Editor für Progressionsgraphen (Phase B).
 */
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import api from '../utils/api'
import ExercisePickerModal from './ExercisePickerModal'
import ExerciseGapFillPrepModal from './exercises/ExerciseGapFillPrepModal'
import ProgressionSlotCard from './ProgressionSlotCard'
import ProgressionFindingsPanel from './ProgressionFindingsPanel'
import {
  aiPreviewToQuickCreateDraft,
  buildQuickCreateAiPreview,
} from '../utils/exerciseAiQuickCreate'
import {
  buildPathGapPlanningContextForAi,
  gapOfferContextDisplayLines,
  initialStageLearningGoalFromOffer,
} from '../utils/planningContextForExerciseAi'
import {
  addSlotToDraft,
  applyGapOfferToDraft,
  applyMatchStepsToSlots,
  collectGapOffersFromApiResponse,
  hydrateProgressionGraphDraft,
  insertSlotInDraft,
  librarySlotExercise,
  majorStepsToOverridePayload,
  moveSlotInDraft,
  patchSlotInDraft,
  removeSlotFromDraft,
  saveProgressionGraphDraft,
  SLOT_MAX,
  slotsAsPathStepRows,
  slotsToEvaluateSteps,
  syncProgressionRoadmapFromSlots,
} from '../utils/progressionGraphDraft'

function roadmapStructuredPayload(startSituation, targetState, roadmapNotes) {
  const body = {}
  const start = (startSituation || '').trim()
  const target = (targetState || '').trim()
  const notes = (roadmapNotes || '').trim()
  if (start) body.start_situation = start
  if (target) body.target_state = target
  if (notes) body.roadmap_notes = notes
  return body
}

function resolveDefaultFocusAreaId(targetSummary, focusAreas) {
  const targetName = targetSummary?.focus_areas?.[0]
  if (targetName && Array.isArray(focusAreas) && focusAreas.length) {
    const norm = String(targetName).trim().toLowerCase()
    const hit = focusAreas.find((fa) => String(fa.name || '').trim().toLowerCase() === norm)
    if (hit?.id) return Number(hit.id)
  }
  return focusAreas?.[0]?.id ? Number(focusAreas[0].id) : null
}

export default function ProgressionGraphEditor({ graphId, embedded = false, onSaved }) {
  const [graphMeta, setGraphMeta] = useState(null)
  const [draft, setDraft] = useState(null)
  const [busy, setBusy] = useState(false)
  const [loadErr, setLoadErr] = useState('')
  const [actionErr, setActionErr] = useState('')
  const [pickContext, setPickContext] = useState(null)
  const [pathQa, setPathQa] = useState(null)
  const [gapFillOffers, setGapFillOffers] = useState([])
  const [evaluating, setEvaluating] = useState(false)
  const [matching, setMatching] = useState(false)
  const [roadmapLoading, setRoadmapLoading] = useState(false)
  const [semanticBrief, setSemanticBrief] = useState(null)
  const [targetSummary, setTargetSummary] = useState(null)
  const [focusAreas, setFocusAreas] = useState([])

  const [activeOffer, setActiveOffer] = useState(null)
  const [activeOfferSlotIndex, setActiveOfferSlotIndex] = useState(null)
  const [gapPrepOpen, setGapPrepOpen] = useState(false)
  const [gapPrepTitle, setGapPrepTitle] = useState('')
  const [gapPrepStageGoal, setGapPrepStageGoal] = useState('')
  const [gapPrepSupplements, setGapPrepSupplements] = useState('')
  const [gapPrepFocusAreaId, setGapPrepFocusAreaId] = useState('')
  const [gapPrepError, setGapPrepError] = useState('')
  const [generatingOfferId, setGeneratingOfferId] = useState(null)
  const [gapAiBusy, setGapAiBusy] = useState(false)

  const loadGraph = useCallback(async () => {
    if (!graphId) return
    setBusy(true)
    setLoadErr('')
    try {
      const [graph, edges] = await Promise.all([
        api.getExerciseProgressionGraph(Number(graphId)),
        api.listExerciseProgressionEdges(Number(graphId)),
      ])
      setGraphMeta(graph)
      setDraft(
        hydrateProgressionGraphDraft({
          artifact: graph?.planning_roadmap,
          edges: Array.isArray(edges) ? edges : [],
          graphName: graph?.name,
        }),
      )
      const findings = graph?.planning_roadmap?.last_findings
      if (findings) setPathQa(findings)
    } catch (e) {
      setLoadErr(e.message || 'Graph konnte nicht geladen werden')
      setDraft(null)
    } finally {
      setBusy(false)
    }
  }, [graphId])

  useEffect(() => {
    loadGraph()
  }, [loadGraph])

  useEffect(() => {
    let cancelled = false
    api
      .listFocusAreas({ status: 'active' })
      .then((fa) => {
        if (!cancelled) setFocusAreas(Array.isArray(fa) ? fa : [])
      })
      .catch(() => {
        if (!cancelled) setFocusAreas([])
      })
    return () => {
      cancelled = true
    }
  }, [])

  const patchDraft = useCallback((patchFn) => {
    setDraft((prev) => {
      if (!prev) return prev
      const next = patchFn(prev)
      return { ...next, dirty: true }
    })
  }, [])

  const gapContextParams = useMemo(() => {
    if (!draft) return {}
    return {
      goalQuery: draft.goalQuery,
      semanticBrief,
      graphId,
      pathSteps: slotsAsPathStepRows(draft),
      editableMajorSteps: draft.majorSteps,
      progressionRoadmap: draft.progressionRoadmap,
      startSituation: draft.startSituation,
      targetState: draft.targetState,
      roadmapNotes: draft.roadmapNotes,
    }
  }, [draft, semanticBrief, graphId])

  const handlePickExercise = async (exercise) => {
    if (!pickContext || !exercise?.id) return
    const { slotIndex, role } = pickContext
    const entry = librarySlotExercise({
      exerciseId: exercise.id,
      exerciseTitle: exercise.title || `Übung #${exercise.id}`,
    })
    patchDraft((d) => {
      const slots = d.slots.map((s, i) => {
        if (i !== slotIndex) return s
        if (role === 'primary') return { ...s, primary: entry }
        const siblings = [...(s.siblings || [])]
        if (!siblings.some((x) => x.exerciseId === entry.exerciseId)) siblings.push(entry)
        return { ...s, siblings }
      })
      return syncProgressionRoadmapFromSlots({ ...d, slots })
    })
    setPickContext(null)
  }

  const handlePatchLearningGoal = (slotIndex, value) => {
    patchDraft((d) => patchSlotInDraft(d, slotIndex, { learning_goal: value }))
  }

  const handlePatchPhase = (slotIndex, value) => {
    patchDraft((d) => patchSlotInDraft(d, slotIndex, { phase: value }))
  }

  const handleMoveSlot = (slotIndex, dir) => {
    patchDraft((d) => moveSlotInDraft(d, slotIndex, dir))
  }

  const handleRemoveSlot = (slotIndex) => {
    if ((draft?.slots?.length || 0) <= 2) {
      alert('Mindestens zwei Slots müssen bleiben.')
      return
    }
    if (!window.confirm(`Slot ${slotIndex + 1} wirklich entfernen?`)) return
    patchDraft((d) => removeSlotFromDraft(d, slotIndex))
  }

  const handleInsertAfter = (slotIndex) => {
    if ((draft?.slots?.length || 0) >= SLOT_MAX) {
      alert(`Maximal ${SLOT_MAX} Slots.`)
      return
    }
    patchDraft((d) => insertSlotInDraft(d, slotIndex))
  }

  const handleAddSlot = () => {
    if ((draft?.slots?.length || 0) >= SLOT_MAX) {
      alert(`Maximal ${SLOT_MAX} Slots.`)
      return
    }
    patchDraft((d) => addSlotToDraft(d))
  }

  const handleClearPrimary = (slotIndex) => {
    patchDraft((d) => {
      const slots = d.slots.map((s, i) =>
        i === slotIndex
          ? {
              ...s,
              primary: {
                kind: 'empty',
                exerciseId: null,
                variantId: null,
                exerciseTitle: '',
                variantName: null,
                proposalKey: null,
                aiSuggestion: null,
              },
              siblings: [],
            }
          : s,
      )
      return syncProgressionRoadmapFromSlots({ ...d, slots })
    })
  }

  const handleRemoveSibling = (slotIndex, sibIdx) => {
    patchDraft((d) => {
      const slots = d.slots.map((s, i) => {
        if (i !== slotIndex) return s
        return { ...s, siblings: s.siblings.filter((_, j) => j !== sibIdx) }
      })
      return { ...d, slots }
    })
  }

  const validMajorSteps = useMemo(() => {
    if (!draft?.slots) return []
    return draft.slots.filter((s) => (s.learning_goal || '').trim().length >= 3)
  }, [draft?.slots])

  const applyApiResponse = (res) => {
    setSemanticBrief(res?.semantic_brief_summary || null)
    setTargetSummary(res?.target_profile_summary || null)
    setPathQa(res?.path_qa || null)
    setGapFillOffers(collectGapOffersFromApiResponse(res))
  }

  const runRoadmapGenerate = async () => {
    const q = (draft?.goalQuery || '').trim()
    if (q.length < 3) {
      alert('Ziel-Anfrage: mindestens 3 Zeichen.')
      return
    }
    setRoadmapLoading(true)
    setActionErr('')
    try {
      const res = await api.suggestProgressionPath({
        query: q,
        max_steps: draft.maxSteps || 5,
        include_llm_intent: true,
        include_path_qa: false,
        include_llm_path_qa: false,
        include_path_reorder: false,
        include_ai_gap_fill: false,
        include_roadmap_preview: true,
        include_llm_roadmap: true,
        roadmap_only: true,
        progression_graph_id: Number(graphId),
        ...roadmapStructuredPayload(draft.startSituation, draft.targetState, draft.roadmapNotes),
      })
      const roadmap = res?.progression_roadmap
      if (!roadmap) throw new Error('Keine Roadmap in der Antwort')
      const hydrated = hydrateProgressionGraphDraft({
        artifact: {
          goal_query: q,
          progression_roadmap: roadmap,
          start_situation: draft.startSituation,
          target_state: draft.targetState,
          roadmap_notes: draft.roadmapNotes,
          max_steps: (roadmap?.roadmap?.major_steps || []).length || draft.maxSteps,
        },
        edges: [],
        graphName: draft.graphName,
      })
      setDraft({ ...hydrated, goalQuery: q, dirty: true })
      setSemanticBrief(res?.semantic_brief_summary || null)
    } catch (e) {
      setActionErr(e.message || 'Roadmap-Generierung fehlgeschlagen')
    } finally {
      setRoadmapLoading(false)
    }
  }

  const runMatch = async () => {
    const q = (draft?.goalQuery || '').trim()
    if (q.length < 3) {
      alert('Ziel-Anfrage: mindestens 3 Zeichen.')
      return
    }
    if (validMajorSteps.length < 2) {
      alert('Mindestens zwei Slots mit Lernziel (je 3+ Zeichen) nötig.')
      return
    }
    setMatching(true)
    setActionErr('')
    try {
      const synced = syncProgressionRoadmapFromSlots(draft)
      const override = majorStepsToOverridePayload(synced.slots)
      const res = await api.suggestProgressionPath({
        query: q,
        max_steps: synced.slots.length,
        include_llm_intent: true,
        include_path_qa: true,
        include_llm_path_qa: true,
        include_path_reorder: false,
        include_ai_gap_fill: true,
        include_roadmap_preview: true,
        include_llm_roadmap: false,
        roadmap_first: true,
        roadmap_override: override,
        progression_graph_id: Number(graphId),
        ...roadmapStructuredPayload(draft.startSituation, draft.targetState, draft.roadmapNotes),
      })
      const next = applyMatchStepsToSlots(
        {
          ...synced,
          progressionRoadmap: res?.progression_roadmap || synced.progressionRoadmap,
          pathSkillExpectations: res?.path_skill_expectations || synced.pathSkillExpectations,
        },
        res?.steps,
      )
      setDraft(next)
      applyApiResponse(res)
    } catch (e) {
      setActionErr(e.message || 'Übungs-Match fehlgeschlagen')
    } finally {
      setMatching(false)
    }
  }

  const runEvaluate = async () => {
    const q = (draft?.goalQuery || '').trim()
    if (q.length < 3) {
      alert('Ziel-Anfrage: mindestens 3 Zeichen.')
      return
    }
    setEvaluating(true)
    setActionErr('')
    try {
      const synced = syncProgressionRoadmapFromSlots(draft)
      const override =
        validMajorSteps.length >= 2 ? majorStepsToOverridePayload(synced.slots) : undefined
      const res = await api.suggestProgressionPath({
        query: q,
        max_steps: synced.slots.length || draft.maxSteps || 5,
        include_path_qa: true,
        include_llm_path_qa: true,
        include_ai_gap_fill: true,
        include_path_reorder: false,
        include_llm_intent: false,
        evaluate_only: true,
        evaluate_steps: slotsToEvaluateSteps(synced),
        roadmap_override: override,
        progression_graph_id: Number(graphId),
        ...roadmapStructuredPayload(draft.startSituation, draft.targetState, draft.roadmapNotes),
      })
      applyApiResponse(res)
      setDraft((prev) => (prev ? { ...prev, lastFindings: res?.path_qa || null } : prev))
    } catch (e) {
      setActionErr(e.message || 'Bewertung fehlgeschlagen')
    } finally {
      setEvaluating(false)
    }
  }

  const handleSave = async () => {
    if (!draft || !graphId) return
    setBusy(true)
    setActionErr('')
    try {
      await saveProgressionGraphDraft(api, graphId, { ...draft, lastFindings: pathQa })
      await loadGraph()
      if (typeof onSaved === 'function') await onSaved()
      alert('Progressionsgraph gespeichert.')
    } catch (e) {
      setActionErr(e.message || 'Speichern fehlgeschlagen')
    } finally {
      setBusy(false)
    }
  }

  const handleApplyGapOffer = (offer, slotIndex) => {
    setDraft((prev) => {
      const next = applyGapOfferToDraft(prev, offer, { slotIndex })
      return { ...next, dirty: true }
    })
    setGapFillOffers((prev) => prev.filter((o) => o.offer_id !== offer?.offer_id))
  }

  const handleInsertGapSlot = (offer) => {
    if ((draft?.slots?.length || 0) >= SLOT_MAX) {
      alert(`Maximal ${SLOT_MAX} Slots — zuerst einen Slot entfernen.`)
      return
    }
    setDraft((prev) => {
      const next = applyGapOfferToDraft(prev, offer, { insertNewSlot: true })
      return { ...next, dirty: true }
    })
    setGapFillOffers((prev) => prev.filter((o) => o.offer_id !== offer?.offer_id))
  }

  const openGapFillPrep = (offer, slotIndex = null) => {
    const defaultFocus = resolveDefaultFocusAreaId(targetSummary, focusAreas)
    setActiveOffer(offer)
    setActiveOfferSlotIndex(slotIndex)
    setGapPrepTitle((offer?.title_hint || '').trim())
    setGapPrepStageGoal(initialStageLearningGoalFromOffer(offer, gapContextParams))
    setGapPrepSupplements('')
    setGapPrepFocusAreaId(defaultFocus ? String(defaultFocus) : '')
    setGapPrepError('')
    setGapPrepOpen(true)
  }

  const runGapFillAiSuggest = async (offer, prep, slotIndex) => {
    const title = (prep?.title || offer?.title_hint || '').trim()
    if (title.length < 3) {
      alert('Titel: mindestens 3 Zeichen.')
      return
    }
    const supplements = (prep?.supplements || '').trim()
    const stageGoal = (prep?.stageLearningGoal || '').trim()
    let goalText = (offer?.goal_for_ai || offer?.sketch || '').trim()
    if (supplements) {
      goalText = `${goalText}\n\nTrainer-Ergänzungen:\n${supplements}`.trim()
    }
    const focusId =
      prep?.focusAreaId != null && Number.isFinite(Number(prep.focusAreaId))
        ? Number(prep.focusAreaId)
        : resolveDefaultFocusAreaId(targetSummary, focusAreas)
    if (!focusId) {
      alert('Bitte einen Fokusbereich wählen.')
      return
    }
    const focusRow = (focusAreas || []).find((x) => Number(x.id) === focusId)
    const focusHint = (focusRow?.name || offer?.primary_topic || '').trim()

    setGapAiBusy(true)
    setGeneratingOfferId(offer?.offer_id || null)
    setGapPrepError('')
    try {
      const planningContext = buildPathGapPlanningContextForAi({
        offer,
        ...gapContextParams,
        stageLearningGoalOverride: stageGoal,
        gapTrainerSupplements: supplements,
      })
      const aiRes = await api.suggestExerciseAi({
        title,
        goal: goalText || undefined,
        execution: '',
        preparation: '',
        trainer_notes: supplements || '',
        focus_area_hint: focusHint || undefined,
        focus_areas_context: [{ focus_area_id: focusId, is_primary: true }],
        planning_context: planningContext || undefined,
        include_summary: true,
        include_skills: true,
        include_instructions: true,
      })
      const preview = buildQuickCreateAiPreview(aiRes, { sketchPlain: goalText })
      if (!preview.hasSummaryProposal && !preview.hasInstructionChoices && !preview.hasSkillChoices) {
        throw new Error('Die KI lieferte keinen verwertbaren Vorschlag.')
      }
      const aiDraft = aiPreviewToQuickCreateDraft(preview, {
        title,
        focusAreaId: focusId,
        sketchPlain: goalText,
      })
      const enrichedOffer = {
        ...offer,
        proposal_title: title,
        ai_suggestion: aiDraft,
        has_ai_payload: true,
      }
      setDraft((prev) => {
        const next = applyGapOfferToDraft(prev, enrichedOffer, { slotIndex })
        return { ...next, dirty: true }
      })
      setGapFillOffers((prev) => prev.filter((o) => o.offer_id !== offer?.offer_id))
      setGapPrepOpen(false)
      setActiveOffer(null)
    } catch (e) {
      setGapPrepError(e.message || 'KI-Anlage fehlgeschlagen')
    } finally {
      setGapAiBusy(false)
      setGeneratingOfferId(null)
    }
  }

  const submitGapFillPrep = async () => {
    const title = (gapPrepTitle || '').trim()
    if (title.length < 3) {
      alert('Titel: mindestens 3 Zeichen.')
      return
    }
    const focusId = parseInt(String(gapPrepFocusAreaId).trim(), 10)
    if (!Number.isFinite(focusId) || focusId < 1) {
      alert('Bitte einen Fokusbereich wählen.')
      return
    }
    if (!activeOffer) return
    await runGapFillAiSuggest(
      activeOffer,
      {
        title,
        stageLearningGoal: (gapPrepStageGoal || '').trim(),
        supplements: (gapPrepSupplements || '').trim(),
        focusAreaId: focusId,
      },
      activeOfferSlotIndex,
    )
  }

  if (loadErr) {
    return (
      <div className="card">
        <p className="form-error">{loadErr}</p>
        <Link to="/exercises" className="btn btn-secondary">
          Zurück
        </Link>
      </div>
    )
  }

  if (!draft) {
    return (
      <div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
        <div className="spinner" />
      </div>
    )
  }

  return (
    <div>
      {!embedded ? (
        <div style={{ marginBottom: '12px', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: '8px' }}>
          <div>
            <h2 style={{ margin: 0, fontSize: '1.15rem' }}>
              {graphMeta?.name || draft.graphName || `Graph #${graphId}`}
            </h2>
            <p style={{ margin: '4px 0 0', fontSize: '12px', color: 'var(--text3)' }}>
              Roadmap-Slots · KI-Match · Graph-Bewertung (max. {SLOT_MAX} Slots)
            </p>
          </div>
          <Link to="/exercises" className="btn btn-secondary" style={{ fontSize: '12px' }}>
            Zur Übersicht
          </Link>
        </div>
      ) : null}

      {actionErr ? (
        <p className="form-error" style={{ marginTop: 0 }}>
          {actionErr}
        </p>
      ) : null}

      <div
        className="progression-graph-editor-grid"
        style={{
          display: 'grid',
          gridTemplateColumns: 'minmax(0, 1fr) minmax(280px, 340px)',
          gap: '14px',
          alignItems: 'start',
        }}
      >
        <div>
          <div className="card" style={{ marginBottom: '12px' }}>
            <h3 style={{ marginTop: 0, fontSize: '1rem' }}>Ziel & Roadmap</h3>
            <div className="form-row">
              <label className="form-label">Ziel-Anfrage</label>
              <textarea
                className="form-input"
                rows={2}
                value={draft.goalQuery}
                disabled={busy}
                onChange={(e) => patchDraft((d) => ({ ...d, goalQuery: e.target.value }))}
                placeholder="z. B. Vom Anfänger zum sauberen Gerade-Tritt"
              />
            </div>
            <div className="form-row" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
              <div>
                <label className="form-label">Start-Situation</label>
                <input
                  className="form-input"
                  value={draft.startSituation}
                  disabled={busy}
                  onChange={(e) => patchDraft((d) => ({ ...d, startSituation: e.target.value }))}
                />
              </div>
              <div>
                <label className="form-label">Ziel-Zustand</label>
                <input
                  className="form-input"
                  value={draft.targetState}
                  disabled={busy}
                  onChange={(e) => patchDraft((d) => ({ ...d, targetState: e.target.value }))}
                />
              </div>
            </div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', marginTop: '8px' }}>
              <button
                type="button"
                className="btn btn-secondary"
                disabled={busy || roadmapLoading}
                onClick={runRoadmapGenerate}
              >
                {roadmapLoading ? 'Roadmap…' : 'Roadmap generieren'}
              </button>
              <button
                type="button"
                className="btn btn-secondary"
                disabled={busy || matching}
                onClick={runMatch}
              >
                {matching ? 'Match…' : 'Übungen matchen'}
              </button>
              <button type="button" className="btn btn-primary" disabled={busy} onClick={handleSave}>
                {busy ? 'Speichern…' : 'Graph speichern'}
              </button>
            </div>
            {draft.dirty ? (
              <p style={{ margin: '8px 0 0', fontSize: '11px', color: 'var(--accent-dark)' }}>
                Ungespeicherte Änderungen
              </p>
            ) : null}
          </div>

          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
            <h3 style={{ margin: 0, fontSize: '1rem' }}>Slots ({draft.slots.length})</h3>
            <button
              type="button"
              className="btn btn-secondary"
              style={{ fontSize: '12px' }}
              disabled={busy || draft.slots.length >= SLOT_MAX}
              onClick={handleAddSlot}
            >
              Slot am Ende
            </button>
          </div>

          {draft.slots.map((slot, idx) => (
            <ProgressionSlotCard
              key={`slot-${idx}-${slot.learning_goal?.slice(0, 12) || 'x'}`}
              slot={slot}
              slotIndex={idx}
              slotCount={draft.slots.length}
              disabled={busy}
              onPickPrimary={(i) => setPickContext({ slotIndex: i, role: 'primary' })}
              onPickSibling={(i) => setPickContext({ slotIndex: i, role: 'sibling' })}
              onClearPrimary={handleClearPrimary}
              onRemoveSibling={handleRemoveSibling}
              onPatchLearningGoal={handlePatchLearningGoal}
              onPatchPhase={handlePatchPhase}
              onMoveUp={(i) => handleMoveSlot(i, -1)}
              onMoveDown={(i) => handleMoveSlot(i, 1)}
              onRemoveSlot={handleRemoveSlot}
              onInsertAfter={handleInsertAfter}
            />
          ))}
        </div>

        <ProgressionFindingsPanel
          pathQa={pathQa}
          gapFillOffers={gapFillOffers}
          draft={draft}
          slotCount={draft.slots.length}
          loading={evaluating}
          error=""
          onEvaluate={runEvaluate}
          onApplyGapOffer={handleApplyGapOffer}
          onInsertGapSlot={handleInsertGapSlot}
          onGenerateGapAi={openGapFillPrep}
          generatingOfferId={generatingOfferId}
          aiBusy={gapAiBusy}
          evaluateDisabled={busy || !draft.goalQuery?.trim()}
        />
      </div>

      {pickContext ? (
        <ExercisePickerModal
          open
          onClose={() => setPickContext(null)}
          onSelectExercise={handlePickExercise}
        />
      ) : null}

      <ExerciseGapFillPrepModal
        open={gapPrepOpen}
        offer={activeOffer}
        onClose={() => {
          if (gapAiBusy) return
          setGapPrepOpen(false)
          setGapPrepError('')
        }}
        title={gapPrepTitle}
        onTitleChange={setGapPrepTitle}
        stageLearningGoal={gapPrepStageGoal}
        onStageLearningGoalChange={setGapPrepStageGoal}
        supplements={gapPrepSupplements}
        onSupplementsChange={setGapPrepSupplements}
        focusAreaId={gapPrepFocusAreaId}
        onFocusAreaChange={setGapPrepFocusAreaId}
        focusAreas={focusAreas}
        contextLines={gapOfferContextDisplayLines(activeOffer, gapContextParams)}
        error={gapPrepError}
        busy={gapAiBusy}
        onSubmit={submitGapFillPrep}
      />

      <style>{`
        @media (max-width: 900px) {
          .progression-graph-editor-grid {
            grid-template-columns: 1fr !important;
          }
        }
      `}</style>
    </div>
  )
}
