import { useState, useCallback, useMemo } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
  Search,
  Plus,
  Layers,
  Trash2,
  Loader2,
  Pencil,
  BookOpen,
  Headphones,
  PenTool,
  Mic,
  Briefcase,
  ChevronRight,
  ChevronLeft,
  Check,
  GripVertical,
} from "lucide-react";
import AiTipBanner from "@/components/ai/AiTipBanner";
import { examsService } from "@/services/exams.service";
import { useToast } from "@/hooks/use-toast";
import type {
  ExamStructure,
  ExamStructureConfig,
  ListeningPartConfig,
  ReadingPassageConfig,
  SpeakingPartConfig,
  WritingTaskConfig,
} from "@/types";

/* ───────────────────────────── constants ───────────────────────────── */

const INDUSTRY_OPTIONS = ["General", "Technology", "Healthcare", "Hospitality", "Education"];

type ModuleKey = "reading" | "listening" | "writing" | "speaking" | "level" | "industry";

interface ModuleMeta {
  key: ModuleKey;
  label: string;
  icon: React.ReactNode;
  color: string;
}

const ALL_MODULES: ModuleMeta[] = [
  { key: "listening", label: "Listening", icon: <Headphones className="h-4 w-4" />, color: "text-teal-600" },
  { key: "reading", label: "Reading", icon: <BookOpen className="h-4 w-4" />, color: "text-blue-600" },
  { key: "writing", label: "Writing", icon: <PenTool className="h-4 w-4" />, color: "text-orange-600" },
  { key: "speaking", label: "Speaking", icon: <Mic className="h-4 w-4" />, color: "text-purple-600" },
  { key: "level", label: "Level", icon: <Layers className="h-4 w-4" />, color: "text-indigo-600" },
  { key: "industry", label: "Industry", icon: <Briefcase className="h-4 w-4" />, color: "text-amber-700" },
];

/* ── Part / type pools ── */

const ALL_LISTENING_PART_TYPES = [
  { key: "social_conversation", label: "Social Conversation" },
  { key: "social_monologue", label: "Social Monologue" },
  { key: "academic_discussion", label: "Academic Discussion" },
  { key: "academic_monologue", label: "Academic Monologue" },
];

const LISTENING_PARTS_ACADEMIC = [
  { key: "academic_discussion", label: "P1: Academic Discussion" },
  { key: "academic_monologue", label: "P2: Academic Monologue" },
  { key: "social_conversation", label: "P3: Social Conversation" },
  { key: "social_monologue", label: "P4: Social Monologue" },
];
const LISTENING_PARTS_GENERAL = [
  { key: "social_conversation", label: "P1: Social Conversation" },
  { key: "social_monologue", label: "P2: Social Monologue" },
  { key: "academic_discussion", label: "P3: Academic Discussion" },
  { key: "academic_monologue", label: "P4: Academic Monologue" },
];

const LISTENING_QUESTION_TYPES = [
  { key: "mcq", label: "Multiple Choice" },
  { key: "matching", label: "Matching" },
  { key: "plan_map_diagram", label: "Plan/Map/Diagram Labelling" },
  { key: "form_note_table", label: "Form/Note/Table/Summary Completion" },
  { key: "gap_filling", label: "Gap-filling" },
  { key: "sentence_completion", label: "Sentence Completion" },
  { key: "true_false_ng", label: "True / False / Not Given" },
  { key: "short_answer", label: "Short-Answer Questions" },
];

const READING_QUESTION_TYPES = [
  { key: "mcq", label: "Multiple Choice" },
  { key: "identifying_info", label: "Identifying Information (T/F/NG)" },
  { key: "identifying_views", label: "Identifying Writer's Views (Y/N/NG)" },
  { key: "matching_info", label: "Matching Information" },
  { key: "matching_headings", label: "Matching Headings" },
  { key: "matching_features", label: "Matching Features" },
  { key: "matching_endings", label: "Matching Sentence Endings" },
  { key: "sentence_completion", label: "Sentence Completion" },
  { key: "summary_completion", label: "Summary/Note/Table/Flow-Chart Completion" },
  { key: "diagram_label", label: "Diagram Label Completion" },
  { key: "short_answer", label: "Short-Answer Questions" },
];

const READING_STYLES_ACADEMIC = ["Narrative", "Descriptive", "Discursive"];
const READING_STYLES_GENERAL = ["Everyday topics", "Work topics", "General interest"];

const ALL_WRITING_TASK_TYPES = [
  { key: "descriptive", label: "Descriptive", group: "Academic" },
  { key: "analytical", label: "Analytical", group: "Academic" },
  { key: "persuasive", label: "Persuasive", group: "Academic" },
  { key: "critical", label: "Critical", group: "Academic" },
  { key: "formal_letter", label: "Formal Letter", group: "General" },
  { key: "semi_formal_letter", label: "Semi-Formal Letter", group: "General" },
  { key: "informal_letter", label: "Informal Letter", group: "General" },
  { key: "scenario_based", label: "Scenario-Based Questions", group: "Both" },
  { key: "opinion_essay", label: "Opinion Essay", group: "Essay" },
  { key: "discussion_essay", label: "Discussion Essay", group: "Essay" },
  { key: "advantage_disadvantage", label: "Advantage / Disadvantage", group: "Essay" },
  { key: "solution_problem", label: "Solution / Problem", group: "Essay" },
  { key: "direct_two_question", label: "Direct / Two-Question", group: "Essay" },
];

const ALL_SPEAKING_PART_TYPES = [
  { key: "interview", label: "Introduction & Interview", defaultMin: 4, defaultMax: 5 },
  { key: "long_turn", label: "Long Turn (Cue Card)", defaultMin: 3, defaultMax: 4 },
  { key: "discussion", label: "Discussion", defaultMin: 4, defaultMax: 5 },
];

const LEVEL_EXERCISE_TYPE_POOL = [
  { key: "mc_blank", label: "Multiple Choice: Blank Space", defaultQty: 1 },
  { key: "mc_underline", label: "Multiple Choice: Underlined", defaultQty: 2 },
  { key: "fill_blanks", label: "Fill Blanks", defaultQty: 5 },
  { key: "fill_blanks_mc", label: "Fill Blanks: Multiple Choice", defaultQty: 10 },
  { key: "verb_tense", label: "Verb Tense", defaultQty: 11 },
];

const INDUSTRY_EXERCISE_TYPE_POOL = [
  { key: "mc_blank_space", label: "Multiple Choice: Blank Space", defaultQty: 5 },
  { key: "mc_normal", label: "Multiple Choice: Normal", defaultQty: 5 },
  { key: "fill_blanks", label: "Fill Blanks", defaultQty: 5 },
  { key: "true_false", label: "True/False", defaultQty: 5 },
  { key: "technical_writing", label: "Technical Writing", defaultQty: 2 },
];

const INDUSTRY_DIFFICULTY_OPTIONS = ["Beginner", "Intermediate", "Advanced"];

/* ───────────────────────── helpers ───────────────────────── */

function sumQTypes(qt: Record<string, number>): number {
  return Object.values(qt).reduce((s, v) => s + v, 0);
}

function lookupLabel(key: string, pool: { key: string; label: string }[]): string {
  return pool.find((p) => p.key === key)?.label ?? key;
}

/* ───────────────────────── wizard form state ───────────────────────── */

interface WizardWritingTask {
  type: string;
  min_words: number;
  label: string;
}

interface WizardLevelEntry {
  type: string;
  quantity: number;
}

interface WizardIndustryEntry {
  type: string;
  quantity: number;
}

interface WizardState {
  name: string;
  industry: string;
  exam_type: "academic" | "general";
  modules: Set<ModuleKey>;
  listening: { parts: ListeningPartConfig[] };
  reading: { passages: ReadingPassageConfig[] };
  writing: { tasks: WizardWritingTask[] };
  speaking: { parts: SpeakingPartConfig[] };
  level: WizardLevelEntry[];
  industryModule: { entries: WizardIndustryEntry[]; difficulty: string };
}

function defaultListeningParts(examType: "academic" | "general"): ListeningPartConfig[] {
  const parts = examType === "academic" ? LISTENING_PARTS_ACADEMIC : LISTENING_PARTS_GENERAL;
  return parts.map((p) => ({
    key: p.key,
    label: p.label,
    questions: 10,
    question_types: Object.fromEntries(LISTENING_QUESTION_TYPES.map((q) => [q.key, 0])),
  }));
}

function defaultReadingPassages(examType: "academic" | "general"): ReadingPassageConfig[] {
  const styles = examType === "academic" ? READING_STYLES_ACADEMIC : READING_STYLES_GENERAL;
  return styles.map((style) => ({
    style,
    questions: 13,
    question_types: Object.fromEntries(READING_QUESTION_TYPES.map((q) => [q.key, 0])),
  }));
}

function defaultWritingTasks(examType: "academic" | "general"): WizardWritingTask[] {
  return [
    { label: "Task 1", type: examType === "academic" ? "descriptive" : "formal_letter", min_words: 150 },
    { label: "Task 2", type: "opinion_essay", min_words: 250 },
  ];
}

function emptyWizard(): WizardState {
  return {
    name: "",
    industry: "",
    exam_type: "academic",
    modules: new Set(),
    listening: { parts: defaultListeningParts("academic") },
    reading: { passages: defaultReadingPassages("academic") },
    writing: { tasks: defaultWritingTasks("academic") },
    speaking: { parts: [...ALL_SPEAKING_PART_TYPES.map((p) => ({ key: p.key, label: p.label, duration_min: p.defaultMin, duration_max: p.defaultMax }))] },
    level: LEVEL_EXERCISE_TYPE_POOL.map((t) => ({ type: t.key, quantity: t.defaultQty })),
    industryModule: {
      entries: INDUSTRY_EXERCISE_TYPE_POOL.map((t) => ({ type: t.key, quantity: t.defaultQty })),
      difficulty: "Intermediate",
    },
  };
}

function wizardFromStructure(s: ExamStructure): WizardState {
  const cfg = (s.config && typeof s.config === "object" ? s.config : {}) as ExamStructureConfig;
  const examType = cfg.exam_type || "academic";
  const rawModules = (Array.isArray(s.modules) ? s.modules : []) as string[];
  const mods = new Set<ModuleKey>(
    rawModules.filter((m): m is ModuleKey =>
      ["reading", "listening", "writing", "speaking", "level", "industry"].includes(m),
    ),
  );

  const base = emptyWizard();
  base.name = s.name;
  base.industry = s.industry || "";
  base.exam_type = examType;
  base.modules = mods;

  if (cfg.listening?.parts?.length) {
    base.listening.parts = cfg.listening.parts;
  } else {
    base.listening.parts = defaultListeningParts(examType);
  }

  if (cfg.reading?.passages?.length) {
    base.reading.passages = cfg.reading.passages;
  } else {
    base.reading.passages = defaultReadingPassages(examType);
  }

  if (cfg.writing) {
    if (cfg.writing.tasks?.length) {
      base.writing.tasks = cfg.writing.tasks.map((t, i) => ({
        type: t.type,
        min_words: t.min_words,
        label: t.label || `Task ${i + 1}`,
      }));
    } else if (cfg.writing.task1 || cfg.writing.task2) {
      const tasks: WizardWritingTask[] = [];
      if (cfg.writing.task1) tasks.push({ label: "Task 1", type: cfg.writing.task1.type, min_words: cfg.writing.task1.min_words });
      if (cfg.writing.task2) tasks.push({ label: "Task 2", type: cfg.writing.task2.type, min_words: cfg.writing.task2.min_words });
      base.writing.tasks = tasks;
    }
  }

  if (cfg.speaking?.parts?.length) {
    base.speaking.parts = cfg.speaking.parts;
  }

  if (cfg.level) {
    if (cfg.level.entries?.length) {
      base.level = cfg.level.entries.map((e) => ({ type: e.type, quantity: e.quantity }));
    } else if (cfg.level.exercise_types) {
      base.level = Object.entries(cfg.level.exercise_types).map(([type, quantity]) => ({ type, quantity }));
    }
  }

  if (cfg.industry) {
    if (cfg.industry.entries?.length) {
      base.industryModule.entries = cfg.industry.entries.map((e) => ({ type: e.type, quantity: e.quantity }));
    } else if (cfg.industry.exercise_types) {
      base.industryModule.entries = Object.entries(cfg.industry.exercise_types).map(([type, quantity]) => ({ type, quantity }));
    }
    base.industryModule.difficulty = cfg.industry.difficulty || "Intermediate";
  }

  return base;
}

function wizardToPayload(w: WizardState): { name: string; industry: string; modules: string[]; config: ExamStructureConfig } {
  const modules = [...w.modules];
  const config: ExamStructureConfig = { exam_type: w.exam_type };

  if (w.modules.has("listening")) {
    const totalQ = w.listening.parts.reduce((s, p) => s + sumQTypes(p.question_types), 0);
    config.listening = { parts: w.listening.parts, total_questions: totalQ };
  }
  if (w.modules.has("reading")) {
    const totalQ = w.reading.passages.reduce((s, p) => s + sumQTypes(p.question_types), 0);
    config.reading = { passages: w.reading.passages, total_questions: totalQ };
  }
  if (w.modules.has("writing")) {
    config.writing = {
      tasks: w.writing.tasks.map((t) => ({ type: t.type, min_words: t.min_words, label: t.label })),
      rubric_id: null,
    };
  }
  if (w.modules.has("speaking")) {
    config.speaking = { parts: w.speaking.parts, rubric_id: null };
  }
  if (w.modules.has("level")) {
    config.level = {
      exercise_types: Object.fromEntries(w.level.map((e) => [e.type, e.quantity])),
      entries: w.level,
    };
  }
  if (w.modules.has("industry")) {
    config.industry = {
      exercise_types: Object.fromEntries(w.industryModule.entries.map((e) => [e.type, e.quantity])),
      entries: w.industryModule.entries,
      difficulty: w.industryModule.difficulty,
    };
  }

  return { name: w.name.trim(), industry: w.industry, modules, config };
}

/* ───────────────────── Stepper ───────────────────── */

const STEPS = ["Basics", "Modules", "Configure", "Review"] as const;

function StepIndicator({ current }: { current: number }) {
  return (
    <div className="flex items-center gap-1 mb-6">
      {STEPS.map((label, i) => (
        <div key={label} className="flex items-center gap-1">
          <div
            className={`flex items-center justify-center h-7 w-7 rounded-full text-xs font-semibold transition-colors ${
              i < current ? "bg-primary text-primary-foreground" : i === current ? "bg-primary text-primary-foreground ring-2 ring-primary/30" : "bg-muted text-muted-foreground"
            }`}
          >
            {i < current ? <Check className="h-3.5 w-3.5" /> : i + 1}
          </div>
          <span className={`text-xs font-medium hidden sm:inline ${i === current ? "text-foreground" : "text-muted-foreground"}`}>
            {label}
          </span>
          {i < STEPS.length - 1 && <ChevronRight className="h-3.5 w-3.5 text-muted-foreground mx-1" />}
        </div>
      ))}
    </div>
  );
}

/* ─────────────── Reusable: question type grid with toggles ─────────────── */

function QuestionTypeGrid({
  types,
  values,
  onChange,
}: {
  types: { key: string; label: string }[];
  values: Record<string, number>;
  onChange: (key: string, val: number) => void;
}) {
  const enabled = (k: string) => (values[k] ?? 0) > 0;
  const total = sumQTypes(values);

  return (
    <div className="space-y-2">
      <div className="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-1.5">
        {types.map((qt) => {
          const on = enabled(qt.key);
          return (
            <div key={qt.key} className={`flex items-center gap-2 rounded-md px-2 py-1 transition-colors ${on ? "bg-primary/5" : ""}`}>
              <Checkbox
                checked={on}
                onCheckedChange={(checked) => onChange(qt.key, checked ? Math.max(values[qt.key] || 1, 1) : 0)}
                className="h-3.5 w-3.5"
              />
              <Label className="text-xs truncate flex-1 cursor-pointer" onClick={() => onChange(qt.key, on ? 0 : Math.max(values[qt.key] || 1, 1))}>
                {qt.label}
              </Label>
              {on && (
                <Input
                  type="number" min={1} max={40}
                  className="w-14 h-6 text-xs text-center"
                  value={values[qt.key]}
                  onChange={(e) => onChange(qt.key, Math.max(1, Number(e.target.value) || 1))}
                />
              )}
            </div>
          );
        })}
      </div>
      <div className="text-xs text-muted-foreground text-right">
        {total} question{total !== 1 ? "s" : ""} selected
      </div>
    </div>
  );
}

/* ───────────────── Wizard dialog ───────────────── */

function ExamStructureWizard({
  open,
  onOpenChange,
  initial,
  onSave,
  saving,
  title,
}: {
  open: boolean;
  onOpenChange: (v: boolean) => void;
  initial: WizardState;
  onSave: (payload: ReturnType<typeof wizardToPayload>) => void;
  saving: boolean;
  title: string;
}) {
  const [step, setStep] = useState(0);
  const [w, setW] = useState<WizardState>(initial);

  const resetAndClose = useCallback(() => {
    setStep(0);
    setW(emptyWizard());
    onOpenChange(false);
  }, [onOpenChange]);

  const updateW = useCallback(<K extends keyof WizardState>(key: K, val: WizardState[K]) => {
    setW((prev) => ({ ...prev, [key]: val }));
  }, []);

  const handleExamTypeChange = useCallback((et: "academic" | "general") => {
    setW((prev) => ({
      ...prev,
      exam_type: et,
      listening: { parts: defaultListeningParts(et) },
      reading: { passages: defaultReadingPassages(et) },
      writing: { tasks: defaultWritingTasks(et) },
    }));
  }, []);

  const toggleModule = useCallback((key: ModuleKey) => {
    setW((prev) => {
      const next = new Set(prev.modules);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return { ...prev, modules: next };
    });
  }, []);

  const canNext = useMemo(() => {
    if (step === 0) return w.name.trim().length > 0;
    if (step === 1) return w.modules.size > 0;
    return true;
  }, [step, w.name, w.modules]);

  const selectedModules = useMemo(
    () => ALL_MODULES.filter((m) => w.modules.has(m.key)),
    [w.modules],
  );

  /* ── Listening mutations ── */

  const addListeningPart = () => {
    setW((prev) => {
      const usedKeys = new Set(prev.listening.parts.map((p) => p.key));
      const next = ALL_LISTENING_PART_TYPES.find((t) => !usedKeys.has(t.key)) ?? ALL_LISTENING_PART_TYPES[0];
      const newPart: ListeningPartConfig = {
        key: next.key,
        label: next.label,
        questions: 10,
        question_types: Object.fromEntries(LISTENING_QUESTION_TYPES.map((q) => [q.key, 0])),
      };
      return { ...prev, listening: { parts: [...prev.listening.parts, newPart] } };
    });
  };

  const removeListeningPart = (idx: number) => {
    setW((prev) => ({ ...prev, listening: { parts: prev.listening.parts.filter((_, i) => i !== idx) } }));
  };

  const updateListeningPartType = (idx: number, key: string) => {
    const meta = ALL_LISTENING_PART_TYPES.find((t) => t.key === key);
    setW((prev) => {
      const parts = [...prev.listening.parts];
      parts[idx] = { ...parts[idx], key, label: meta?.label ?? key };
      return { ...prev, listening: { parts } };
    });
  };

  const updateListeningQType = (partIdx: number, qKey: string, val: number) => {
    setW((prev) => {
      const parts = [...prev.listening.parts];
      parts[partIdx] = { ...parts[partIdx], question_types: { ...parts[partIdx].question_types, [qKey]: val } };
      return { ...prev, listening: { parts } };
    });
  };

  /* ── Reading mutations ── */

  const addReadingPassage = () => {
    const styles = w.exam_type === "academic" ? READING_STYLES_ACADEMIC : READING_STYLES_GENERAL;
    const usedStyles = new Set(w.reading.passages.map((p) => p.style));
    const nextStyle = styles.find((s) => !usedStyles.has(s)) ?? styles[0];
    setW((prev) => ({
      ...prev,
      reading: {
        passages: [
          ...prev.reading.passages,
          { style: nextStyle, questions: 13, question_types: Object.fromEntries(READING_QUESTION_TYPES.map((q) => [q.key, 0])) },
        ],
      },
    }));
  };

  const removeReadingPassage = (idx: number) => {
    setW((prev) => ({ ...prev, reading: { passages: prev.reading.passages.filter((_, i) => i !== idx) } }));
  };

  const updateReadingStyle = (idx: number, style: string) => {
    setW((prev) => {
      const passages = [...prev.reading.passages];
      passages[idx] = { ...passages[idx], style };
      return { ...prev, reading: { passages } };
    });
  };

  const updateReadingQType = (passageIdx: number, qKey: string, val: number) => {
    setW((prev) => {
      const passages = [...prev.reading.passages];
      passages[passageIdx] = { ...passages[passageIdx], question_types: { ...passages[passageIdx].question_types, [qKey]: val } };
      return { ...prev, reading: { passages } };
    });
  };

  /* ── Writing mutations ── */

  const addWritingTask = () => {
    setW((prev) => ({
      ...prev,
      writing: {
        tasks: [
          ...prev.writing.tasks,
          { label: `Task ${prev.writing.tasks.length + 1}`, type: "scenario_based", min_words: 150 },
        ],
      },
    }));
  };

  const removeWritingTask = (idx: number) => {
    setW((prev) => ({ ...prev, writing: { tasks: prev.writing.tasks.filter((_, i) => i !== idx) } }));
  };

  const updateWritingTask = (idx: number, patch: Partial<WizardWritingTask>) => {
    setW((prev) => {
      const tasks = [...prev.writing.tasks];
      tasks[idx] = { ...tasks[idx], ...patch };
      return { ...prev, writing: { tasks } };
    });
  };

  /* ── Speaking mutations ── */

  const addSpeakingPart = () => {
    const usedKeys = new Set(w.speaking.parts.map((p) => p.key));
    const next = ALL_SPEAKING_PART_TYPES.find((t) => !usedKeys.has(t.key)) ?? ALL_SPEAKING_PART_TYPES[0];
    setW((prev) => ({
      ...prev,
      speaking: {
        parts: [
          ...prev.speaking.parts,
          { key: next.key, label: next.label, duration_min: next.defaultMin, duration_max: next.defaultMax },
        ],
      },
    }));
  };

  const removeSpeakingPart = (idx: number) => {
    setW((prev) => ({ ...prev, speaking: { parts: prev.speaking.parts.filter((_, i) => i !== idx) } }));
  };

  const updateSpeakingPart = (idx: number, patch: Partial<SpeakingPartConfig>) => {
    setW((prev) => {
      const parts = [...prev.speaking.parts];
      parts[idx] = { ...parts[idx], ...patch };
      return { ...prev, speaking: { parts } };
    });
  };

  const updateSpeakingPartType = (idx: number, key: string) => {
    const meta = ALL_SPEAKING_PART_TYPES.find((t) => t.key === key);
    if (!meta) return;
    setW((prev) => {
      const parts = [...prev.speaking.parts];
      parts[idx] = { ...parts[idx], key, label: meta.label };
      return { ...prev, speaking: { parts } };
    });
  };

  /* ── Level mutations ── */

  const addLevelEntry = () => {
    const usedTypes = new Set(w.level.map((e) => e.type));
    const next = LEVEL_EXERCISE_TYPE_POOL.find((t) => !usedTypes.has(t.key));
    if (!next) return;
    updateW("level", [...w.level, { type: next.key, quantity: next.defaultQty }]);
  };

  const removeLevelEntry = (idx: number) => {
    updateW("level", w.level.filter((_, i) => i !== idx));
  };

  const updateLevelEntry = (idx: number, patch: Partial<WizardLevelEntry>) => {
    const entries = [...w.level];
    entries[idx] = { ...entries[idx], ...patch };
    updateW("level", entries);
  };

  /* ── Industry mutations ── */

  const addIndustryEntry = () => {
    const usedTypes = new Set(w.industryModule.entries.map((e) => e.type));
    const next = INDUSTRY_EXERCISE_TYPE_POOL.find((t) => !usedTypes.has(t.key));
    if (!next) return;
    updateW("industryModule", { ...w.industryModule, entries: [...w.industryModule.entries, { type: next.key, quantity: next.defaultQty }] });
  };

  const removeIndustryEntry = (idx: number) => {
    updateW("industryModule", { ...w.industryModule, entries: w.industryModule.entries.filter((_, i) => i !== idx) });
  };

  const updateIndustryEntry = (idx: number, patch: Partial<WizardIndustryEntry>) => {
    const entries = [...w.industryModule.entries];
    entries[idx] = { ...entries[idx], ...patch };
    updateW("industryModule", { ...w.industryModule, entries });
  };

  /* ── render: listening ── */

  const renderListeningConfig = () => {
    const totalQ = w.listening.parts.reduce((s, p) => s + sumQTypes(p.question_types), 0);
    return (
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <p className="text-sm text-muted-foreground">
            {w.listening.parts.length} part{w.listening.parts.length !== 1 ? "s" : ""} &middot; {totalQ} total questions &middot; Exam type: <strong className="text-foreground capitalize">{w.exam_type}</strong>
          </p>
          <Button type="button" variant="outline" size="sm" onClick={addListeningPart}>
            <Plus className="h-3.5 w-3.5 mr-1" /> Add Part
          </Button>
        </div>
        {w.listening.parts.map((part, pi) => (
          <Card key={`${part.key}-${pi}`} className="border">
            <CardHeader className="py-3 px-4">
              <div className="flex items-center gap-2">
                <GripVertical className="h-4 w-4 text-muted-foreground/50" />
                <span className="text-xs text-muted-foreground font-medium shrink-0">P{pi + 1}</span>
                <Select value={part.key} onValueChange={(v) => updateListeningPartType(pi, v)}>
                  <SelectTrigger className="h-7 text-xs flex-1">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {ALL_LISTENING_PART_TYPES.map((t) => (
                      <SelectItem key={t.key} value={t.key}>{t.label}</SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Badge variant="secondary" className="text-[10px] shrink-0">{sumQTypes(part.question_types)}q</Badge>
                {w.listening.parts.length > 1 && (
                  <Button type="button" variant="ghost" size="icon" className="h-7 w-7 text-destructive shrink-0" onClick={() => removeListeningPart(pi)}>
                    <Trash2 className="h-3.5 w-3.5" />
                  </Button>
                )}
              </div>
            </CardHeader>
            <CardContent className="px-4 pb-3">
              <QuestionTypeGrid
                types={LISTENING_QUESTION_TYPES}
                values={part.question_types}
                onChange={(qk, val) => updateListeningQType(pi, qk, val)}
              />
            </CardContent>
          </Card>
        ))}
      </div>
    );
  };

  /* ── render: reading ── */

  const renderReadingConfig = () => {
    const styles = w.exam_type === "academic" ? READING_STYLES_ACADEMIC : READING_STYLES_GENERAL;
    const totalQ = w.reading.passages.reduce((s, p) => s + sumQTypes(p.question_types), 0);
    const passageLabel = w.exam_type === "academic" ? "Passage" : "Section";

    return (
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <p className="text-sm text-muted-foreground">
            {w.reading.passages.length} {passageLabel.toLowerCase()}{w.reading.passages.length !== 1 ? "s" : ""} &middot; {totalQ} total questions &middot; Exam type: <strong className="text-foreground capitalize">{w.exam_type}</strong>
          </p>
          <Button type="button" variant="outline" size="sm" onClick={addReadingPassage}>
            <Plus className="h-3.5 w-3.5 mr-1" /> Add {passageLabel}
          </Button>
        </div>
        {w.reading.passages.map((passage, pi) => (
          <Card key={pi} className="border">
            <CardHeader className="py-3 px-4">
              <div className="flex items-center gap-2">
                <GripVertical className="h-4 w-4 text-muted-foreground/50" />
                <span className="text-xs text-muted-foreground font-medium shrink-0">{passageLabel} {pi + 1}</span>
                <Select value={passage.style} onValueChange={(v) => updateReadingStyle(pi, v)}>
                  <SelectTrigger className="h-7 text-xs flex-1">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {styles.map((s) => (
                      <SelectItem key={s} value={s}>{s}</SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Badge variant="secondary" className="text-[10px] shrink-0">{sumQTypes(passage.question_types)}q</Badge>
                {w.reading.passages.length > 1 && (
                  <Button type="button" variant="ghost" size="icon" className="h-7 w-7 text-destructive shrink-0" onClick={() => removeReadingPassage(pi)}>
                    <Trash2 className="h-3.5 w-3.5" />
                  </Button>
                )}
              </div>
            </CardHeader>
            <CardContent className="px-4 pb-3">
              <QuestionTypeGrid
                types={READING_QUESTION_TYPES}
                values={passage.question_types}
                onChange={(qk, val) => updateReadingQType(pi, qk, val)}
              />
            </CardContent>
          </Card>
        ))}
      </div>
    );
  };

  /* ── render: writing ── */

  const renderWritingConfig = () => (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <p className="text-sm text-muted-foreground">
          {w.writing.tasks.length} task{w.writing.tasks.length !== 1 ? "s" : ""} &middot; Scored by rubric &middot; Exam type: <strong className="text-foreground capitalize">{w.exam_type}</strong>
        </p>
        <Button type="button" variant="outline" size="sm" onClick={addWritingTask}>
          <Plus className="h-3.5 w-3.5 mr-1" /> Add Task
        </Button>
      </div>
      {w.writing.tasks.map((task, ti) => (
        <Card key={ti} className="border">
          <CardHeader className="py-3 px-4">
            <div className="flex items-center gap-2">
              <GripVertical className="h-4 w-4 text-muted-foreground/50" />
              <Input
                className="h-7 text-xs font-semibold w-28"
                value={task.label}
                onChange={(e) => updateWritingTask(ti, { label: e.target.value })}
              />
              <div className="flex-1" />
              {w.writing.tasks.length > 1 && (
                <Button type="button" variant="ghost" size="icon" className="h-7 w-7 text-destructive shrink-0" onClick={() => removeWritingTask(ti)}>
                  <Trash2 className="h-3.5 w-3.5" />
                </Button>
              )}
            </div>
          </CardHeader>
          <CardContent className="px-4 pb-3 space-y-3">
            <div className="space-y-1.5">
              <Label className="text-xs">Type</Label>
              <Select value={task.type} onValueChange={(v) => updateWritingTask(ti, { type: v })}>
                <SelectTrigger className="h-8 text-sm"><SelectValue /></SelectTrigger>
                <SelectContent>
                  <SelectItem value="_header_academic" disabled className="text-xs font-semibold text-muted-foreground">— Academic —</SelectItem>
                  {ALL_WRITING_TASK_TYPES.filter((t) => t.group === "Academic").map((o) => (
                    <SelectItem key={o.key} value={o.key}>{o.label}</SelectItem>
                  ))}
                  <SelectItem value="_header_general" disabled className="text-xs font-semibold text-muted-foreground">— General —</SelectItem>
                  {ALL_WRITING_TASK_TYPES.filter((t) => t.group === "General").map((o) => (
                    <SelectItem key={o.key} value={o.key}>{o.label}</SelectItem>
                  ))}
                  <SelectItem value="_header_both" disabled className="text-xs font-semibold text-muted-foreground">— Both —</SelectItem>
                  {ALL_WRITING_TASK_TYPES.filter((t) => t.group === "Both").map((o) => (
                    <SelectItem key={o.key} value={o.key}>{o.label}</SelectItem>
                  ))}
                  <SelectItem value="_header_essay" disabled className="text-xs font-semibold text-muted-foreground">— Essay —</SelectItem>
                  {ALL_WRITING_TASK_TYPES.filter((t) => t.group === "Essay").map((o) => (
                    <SelectItem key={o.key} value={o.key}>{o.label}</SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            <div className="space-y-1.5">
              <Label className="text-xs">Min words</Label>
              <Input type="number" min={50} className="h-8 w-28 text-sm" value={task.min_words}
                onChange={(e) => updateWritingTask(ti, { min_words: Number(e.target.value) || 100 })} />
            </div>
          </CardContent>
        </Card>
      ))}
    </div>
  );

  /* ── render: speaking ── */

  const renderSpeakingConfig = () => {
    const totalMin = w.speaking.parts.reduce((s, p) => s + p.duration_min, 0);
    const totalMax = w.speaking.parts.reduce((s, p) => s + p.duration_max, 0);

    return (
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <p className="text-sm text-muted-foreground">
            {w.speaking.parts.length} part{w.speaking.parts.length !== 1 ? "s" : ""} &middot; {totalMin}-{totalMax} minutes total
          </p>
          <Button type="button" variant="outline" size="sm" onClick={addSpeakingPart}>
            <Plus className="h-3.5 w-3.5 mr-1" /> Add Part
          </Button>
        </div>
        {w.speaking.parts.map((part, i) => (
          <Card key={`${part.key}-${i}`} className="border">
            <CardHeader className="py-3 px-4">
              <div className="flex items-center gap-2">
                <GripVertical className="h-4 w-4 text-muted-foreground/50" />
                <span className="text-xs text-muted-foreground font-medium shrink-0">Part {i + 1}</span>
                <Select value={part.key} onValueChange={(v) => updateSpeakingPartType(i, v)}>
                  <SelectTrigger className="h-7 text-xs flex-1">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {ALL_SPEAKING_PART_TYPES.map((t) => (
                      <SelectItem key={t.key} value={t.key}>{t.label}</SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                {w.speaking.parts.length > 1 && (
                  <Button type="button" variant="ghost" size="icon" className="h-7 w-7 text-destructive shrink-0" onClick={() => removeSpeakingPart(i)}>
                    <Trash2 className="h-3.5 w-3.5" />
                  </Button>
                )}
              </div>
            </CardHeader>
            <CardContent className="px-4 pb-3">
              <div className="flex items-center gap-4">
                <div className="space-y-1">
                  <Label className="text-xs">Duration min (min)</Label>
                  <Input type="number" min={1} max={15} className="w-20 h-7 text-xs text-center"
                    value={part.duration_min}
                    onChange={(e) => updateSpeakingPart(i, { duration_min: Number(e.target.value) || 1 })} />
                </div>
                <div className="space-y-1">
                  <Label className="text-xs">Duration max (min)</Label>
                  <Input type="number" min={1} max={15} className="w-20 h-7 text-xs text-center"
                    value={part.duration_max}
                    onChange={(e) => updateSpeakingPart(i, { duration_max: Number(e.target.value) || 1 })} />
                </div>
              </div>
              <Input
                className="mt-2 h-7 text-xs"
                placeholder="Custom label (optional)"
                value={part.label}
                onChange={(e) => updateSpeakingPart(i, { label: e.target.value })}
              />
            </CardContent>
          </Card>
        ))}
      </div>
    );
  };

  /* ── render: level ── */

  const renderLevelConfig = () => {
    const total = w.level.reduce((s, e) => s + e.quantity, 0);
    const usedTypes = new Set(w.level.map((e) => e.type));
    const canAdd = LEVEL_EXERCISE_TYPE_POOL.some((t) => !usedTypes.has(t.key));

    return (
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <p className="text-sm text-muted-foreground">{total} total questions &middot; {w.level.length} exercise type{w.level.length !== 1 ? "s" : ""}</p>
          <Button type="button" variant="outline" size="sm" onClick={addLevelEntry} disabled={!canAdd}>
            <Plus className="h-3.5 w-3.5 mr-1" /> Add Type
          </Button>
        </div>
        <Card className="border">
          <CardContent className="p-4 space-y-3">
            {w.level.map((entry, i) => (
              <div key={i} className="flex items-center gap-2">
                <Select value={entry.type} onValueChange={(v) => updateLevelEntry(i, { type: v })}>
                  <SelectTrigger className="h-8 text-sm flex-1">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {LEVEL_EXERCISE_TYPE_POOL.map((t) => (
                      <SelectItem key={t.key} value={t.key} disabled={usedTypes.has(t.key) && t.key !== entry.type}>
                        {t.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Input type="number" min={0} max={50} className="w-20 h-8 text-sm text-center"
                  value={entry.quantity}
                  onChange={(e) => updateLevelEntry(i, { quantity: Number(e.target.value) || 0 })} />
                {w.level.length > 1 && (
                  <Button type="button" variant="ghost" size="icon" className="h-8 w-8 text-destructive shrink-0" onClick={() => removeLevelEntry(i)}>
                    <Trash2 className="h-3.5 w-3.5" />
                  </Button>
                )}
              </div>
            ))}
            <div className="border-t pt-2 flex justify-between text-sm font-medium">
              <span>Total</span>
              <span>{total} questions</span>
            </div>
          </CardContent>
        </Card>
      </div>
    );
  };

  /* ── render: industry ── */

  const renderIndustryConfig = () => {
    const total = w.industryModule.entries.reduce((s, e) => s + e.quantity, 0);
    const usedTypes = new Set(w.industryModule.entries.map((e) => e.type));
    const canAdd = INDUSTRY_EXERCISE_TYPE_POOL.some((t) => !usedTypes.has(t.key));

    return (
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <p className="text-sm text-muted-foreground">{total} total questions &middot; Difficulty: {w.industryModule.difficulty}</p>
          <Button type="button" variant="outline" size="sm" onClick={addIndustryEntry} disabled={!canAdd}>
            <Plus className="h-3.5 w-3.5 mr-1" /> Add Type
          </Button>
        </div>
        <div className="space-y-1.5">
          <Label className="text-xs">Difficulty</Label>
          <Select value={w.industryModule.difficulty}
            onValueChange={(v) => updateW("industryModule", { ...w.industryModule, difficulty: v })}>
            <SelectTrigger className="h-8 text-sm w-44"><SelectValue /></SelectTrigger>
            <SelectContent>
              {INDUSTRY_DIFFICULTY_OPTIONS.map((d) => (
                <SelectItem key={d} value={d}>{d}</SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
        <Card className="border">
          <CardContent className="p-4 space-y-3">
            {w.industryModule.entries.map((entry, i) => (
              <div key={i} className="flex items-center gap-2">
                <Select value={entry.type} onValueChange={(v) => updateIndustryEntry(i, { type: v })}>
                  <SelectTrigger className="h-8 text-sm flex-1">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {INDUSTRY_EXERCISE_TYPE_POOL.map((t) => (
                      <SelectItem key={t.key} value={t.key} disabled={usedTypes.has(t.key) && t.key !== entry.type}>
                        {t.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Input type="number" min={0} max={50} className="w-20 h-8 text-sm text-center"
                  value={entry.quantity}
                  onChange={(e) => updateIndustryEntry(i, { quantity: Number(e.target.value) || 0 })} />
                {w.industryModule.entries.length > 1 && (
                  <Button type="button" variant="ghost" size="icon" className="h-8 w-8 text-destructive shrink-0" onClick={() => removeIndustryEntry(i)}>
                    <Trash2 className="h-3.5 w-3.5" />
                  </Button>
                )}
              </div>
            ))}
            <div className="border-t pt-2 flex justify-between text-sm font-medium">
              <span>Total</span>
              <span>{total} questions</span>
            </div>
          </CardContent>
        </Card>
      </div>
    );
  };

  const MODULE_RENDERERS: Record<ModuleKey, () => React.ReactNode> = {
    listening: renderListeningConfig,
    reading: renderReadingConfig,
    writing: renderWritingConfig,
    speaking: renderSpeakingConfig,
    level: renderLevelConfig,
    industry: renderIndustryConfig,
  };

  /* ── render review step ── */

  const renderReview = () => {
    const listeningTotal = w.listening.parts.reduce((s, p) => s + sumQTypes(p.question_types), 0);
    const readingTotal = w.reading.passages.reduce((s, p) => s + sumQTypes(p.question_types), 0);
    const levelTotal = w.level.reduce((s, e) => s + e.quantity, 0);
    const industryTotal = w.industryModule.entries.reduce((s, e) => s + e.quantity, 0);
    const speakMin = w.speaking.parts.reduce((s, p) => s + p.duration_min, 0);
    const speakMax = w.speaking.parts.reduce((s, p) => s + p.duration_max, 0);

    return (
      <div className="space-y-4">
        <Card className="border">
          <CardContent className="p-4 space-y-2">
            <div className="flex justify-between text-sm"><span className="text-muted-foreground">Name</span><span className="font-medium">{w.name}</span></div>
            {w.industry && <div className="flex justify-between text-sm"><span className="text-muted-foreground">Industry</span><span className="font-medium">{w.industry}</span></div>}
            <div className="flex justify-between text-sm"><span className="text-muted-foreground">Exam Type</span><Badge variant="outline" className="capitalize">{w.exam_type}</Badge></div>
            <div className="flex justify-between text-sm items-center">
              <span className="text-muted-foreground">Modules</span>
              <div className="flex gap-1 flex-wrap">{selectedModules.map((m) => <Badge key={m.key} variant="secondary" className="capitalize text-xs">{m.label}</Badge>)}</div>
            </div>
          </CardContent>
        </Card>

        {w.modules.has("listening") && (
          <Card className="border">
            <CardHeader className="py-2 px-4"><CardTitle className="text-sm flex items-center gap-1.5"><Headphones className="h-3.5 w-3.5" /> Listening</CardTitle></CardHeader>
            <CardContent className="px-4 pb-3 space-y-1">
              <p className="text-xs text-muted-foreground">{w.listening.parts.length} parts &middot; {listeningTotal} total questions</p>
              {w.listening.parts.map((p, i) => (
                <p key={i} className="text-xs">P{i + 1}: {p.label} — {sumQTypes(p.question_types)} questions</p>
              ))}
            </CardContent>
          </Card>
        )}
        {w.modules.has("reading") && (
          <Card className="border">
            <CardHeader className="py-2 px-4"><CardTitle className="text-sm flex items-center gap-1.5"><BookOpen className="h-3.5 w-3.5" /> Reading</CardTitle></CardHeader>
            <CardContent className="px-4 pb-3 space-y-1">
              <p className="text-xs text-muted-foreground">{w.reading.passages.length} passages &middot; {readingTotal} total questions</p>
              {w.reading.passages.map((p, i) => (
                <p key={i} className="text-xs">{w.exam_type === "academic" ? "Passage" : "Section"} {i + 1}: {p.style} — {sumQTypes(p.question_types)} questions</p>
              ))}
            </CardContent>
          </Card>
        )}
        {w.modules.has("writing") && (
          <Card className="border">
            <CardHeader className="py-2 px-4"><CardTitle className="text-sm flex items-center gap-1.5"><PenTool className="h-3.5 w-3.5" /> Writing</CardTitle></CardHeader>
            <CardContent className="px-4 pb-3 space-y-1">
              <p className="text-xs text-muted-foreground">{w.writing.tasks.length} task{w.writing.tasks.length !== 1 ? "s" : ""}</p>
              {w.writing.tasks.map((t, i) => (
                <p key={i} className="text-xs">{t.label}: {lookupLabel(t.type, ALL_WRITING_TASK_TYPES)} (min {t.min_words} words)</p>
              ))}
            </CardContent>
          </Card>
        )}
        {w.modules.has("speaking") && (
          <Card className="border">
            <CardHeader className="py-2 px-4"><CardTitle className="text-sm flex items-center gap-1.5"><Mic className="h-3.5 w-3.5" /> Speaking</CardTitle></CardHeader>
            <CardContent className="px-4 pb-3 space-y-1">
              <p className="text-xs text-muted-foreground">{w.speaking.parts.length} parts &middot; {speakMin}-{speakMax} minutes</p>
              {w.speaking.parts.map((p, i) => (
                <p key={i} className="text-xs">Part {i + 1}: {p.label} ({p.duration_min}-{p.duration_max} min)</p>
              ))}
            </CardContent>
          </Card>
        )}
        {w.modules.has("level") && (
          <Card className="border">
            <CardHeader className="py-2 px-4"><CardTitle className="text-sm flex items-center gap-1.5"><Layers className="h-3.5 w-3.5" /> Level</CardTitle></CardHeader>
            <CardContent className="px-4 pb-3 space-y-1">
              <p className="text-xs text-muted-foreground">{levelTotal} total questions &middot; {w.level.length} types</p>
              {w.level.map((e, i) => (
                <p key={i} className="text-xs">{lookupLabel(e.type, LEVEL_EXERCISE_TYPE_POOL)}: {e.quantity}</p>
              ))}
            </CardContent>
          </Card>
        )}
        {w.modules.has("industry") && (
          <Card className="border">
            <CardHeader className="py-2 px-4"><CardTitle className="text-sm flex items-center gap-1.5"><Briefcase className="h-3.5 w-3.5" /> Industry</CardTitle></CardHeader>
            <CardContent className="px-4 pb-3 space-y-1">
              <p className="text-xs text-muted-foreground">{industryTotal} total questions &middot; Difficulty: {w.industryModule.difficulty}</p>
              {w.industryModule.entries.map((e, i) => (
                <p key={i} className="text-xs">{lookupLabel(e.type, INDUSTRY_EXERCISE_TYPE_POOL)}: {e.quantity}</p>
              ))}
            </CardContent>
          </Card>
        )}
      </div>
    );
  };

  return (
    <Dialog
      open={open}
      onOpenChange={(v) => {
        if (!v) resetAndClose();
        else onOpenChange(v);
      }}
    >
      <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
        <DialogHeader>
          <DialogTitle>{title}</DialogTitle>
          <DialogDescription>
            {step === 0 && "Set the basic details for this exam structure."}
            {step === 1 && "Choose which skill modules to include."}
            {step === 2 && "Configure each module's question types, parts, and settings."}
            {step === 3 && "Review everything before saving."}
          </DialogDescription>
        </DialogHeader>

        <StepIndicator current={step} />

        {/* Step 0: Basics */}
        {step === 0 && (
          <div className="space-y-4">
            <div className="space-y-1.5">
              <Label>Structure Name <span className="text-destructive">*</span></Label>
              <Input placeholder="e.g. IELTS Academic Full Test" value={w.name} onChange={(e) => updateW("name", e.target.value)} autoFocus />
            </div>
            <div className="space-y-1.5">
              <Label>Industry</Label>
              <Select value={w.industry} onValueChange={(v) => updateW("industry", v)}>
                <SelectTrigger><SelectValue placeholder="Select industry" /></SelectTrigger>
                <SelectContent>
                  {INDUSTRY_OPTIONS.map((ind) => (
                    <SelectItem key={ind} value={ind}>{ind}</SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            <div className="space-y-1.5">
              <Label>Exam Type <span className="text-destructive">*</span></Label>
              <div className="grid grid-cols-2 gap-3">
                {(["academic", "general"] as const).map((et) => (
                  <button
                    key={et}
                    type="button"
                    onClick={() => handleExamTypeChange(et)}
                    className={`rounded-lg border-2 p-4 text-center transition-all ${
                      w.exam_type === et ? "border-primary bg-primary/5 shadow-sm" : "border-muted hover:border-muted-foreground/30"
                    }`}
                  >
                    <p className="font-semibold capitalize">{et}</p>
                    <p className="text-xs text-muted-foreground mt-0.5">
                      {et === "academic" ? "University / research context" : "Everyday / workplace context"}
                    </p>
                  </button>
                ))}
              </div>
            </div>
          </div>
        )}

        {/* Step 1: Modules */}
        {step === 1 && (
          <div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
            {ALL_MODULES.map((m) => {
              const selected = w.modules.has(m.key);
              return (
                <button
                  key={m.key}
                  type="button"
                  onClick={() => toggleModule(m.key)}
                  className={`rounded-lg border-2 p-4 text-center transition-all ${
                    selected ? "border-primary bg-primary/5 shadow-sm" : "border-muted hover:border-muted-foreground/30"
                  }`}
                >
                  <div className={`mx-auto mb-1.5 ${m.color}`}>{m.icon}</div>
                  <p className="text-sm font-medium">{m.label}</p>
                  {selected && <Check className="h-3.5 w-3.5 text-primary mx-auto mt-1" />}
                </button>
              );
            })}
          </div>
        )}

        {/* Step 2: Module Configuration */}
        {step === 2 && selectedModules.length > 0 && (
          <Tabs defaultValue={selectedModules[0].key} className="w-full">
            <TabsList className="w-full flex-wrap h-auto gap-1 bg-muted/50 p-1">
              {selectedModules.map((m) => (
                <TabsTrigger key={m.key} value={m.key} className="text-xs capitalize gap-1">
                  {m.icon} {m.label}
                </TabsTrigger>
              ))}
            </TabsList>
            {selectedModules.map((m) => (
              <TabsContent key={m.key} value={m.key} className="mt-4">
                {MODULE_RENDERERS[m.key]()}
              </TabsContent>
            ))}
          </Tabs>
        )}
        {step === 2 && selectedModules.length === 0 && (
          <p className="text-sm text-muted-foreground text-center py-8">No modules selected. Go back and select at least one.</p>
        )}

        {/* Step 3: Review */}
        {step === 3 && renderReview()}

        <DialogFooter className="flex justify-between sm:justify-between gap-2 pt-2">
          <Button variant="outline" disabled={step === 0} onClick={() => setStep((s) => s - 1)}>
            <ChevronLeft className="h-4 w-4 mr-1" /> Back
          </Button>
          {step < 3 ? (
            <Button disabled={!canNext} onClick={() => setStep((s) => s + 1)}>
              Next <ChevronRight className="h-4 w-4 ml-1" />
            </Button>
          ) : (
            <Button disabled={saving} onClick={() => onSave(wizardToPayload(w))}>
              {saving ? <Loader2 className="h-4 w-4 mr-2 animate-spin" /> : <Check className="h-4 w-4 mr-1" />}
              {saving ? "Saving..." : "Save Structure"}
            </Button>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

/* ═══════════════════════════ Main page ═══════════════════════════ */

export default function ExamStructuresPage() {
  const { toast } = useToast();
  const queryClient = useQueryClient();
  const [search, setSearch] = useState("");
  const [entityFilter, setEntityFilter] = useState("all");

  const [createOpen, setCreateOpen] = useState(false);
  const [editTarget, setEditTarget] = useState<ExamStructure | null>(null);

  const { data, isLoading, error } = useQuery({
    queryKey: ["exam-structures", entityFilter],
    queryFn: () => examsService.listStructures(entityFilter !== "all" ? { entity_id: Number(entityFilter) } : {}),
  });

  const structures: ExamStructure[] = data?.items ?? [];

  const createMut = useMutation({
    mutationFn: (payload: ReturnType<typeof wizardToPayload>) =>
      examsService.createStructure(payload as unknown as Partial<ExamStructure>),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["exam-structures"] });
      setCreateOpen(false);
      toast({ title: "Structure created" });
    },
    onError: (err: Error) => toast({ variant: "destructive", title: "Failed", description: err.message }),
  });

  const updateMut = useMutation({
    mutationFn: ({ id, payload }: { id: number; payload: ReturnType<typeof wizardToPayload> }) =>
      examsService.updateStructure(id, payload as unknown as Partial<ExamStructure>),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["exam-structures"] });
      setEditTarget(null);
      toast({ title: "Structure updated" });
    },
    onError: (err: Error) => toast({ variant: "destructive", title: "Update failed", description: err.message }),
  });

  const deleteMut = useMutation({
    mutationFn: (id: number) => examsService.deleteStructure(id),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["exam-structures"] });
      toast({ title: "Structure deleted" });
    },
    onError: (err: Error) => toast({ variant: "destructive", title: "Failed", description: err.message }),
  });

  const filtered = structures.filter((s) => {
    if (!search) return true;
    const q = search.toLowerCase();
    return s.name?.toLowerCase().includes(q) || s.industry?.toLowerCase().includes(q);
  });

  const getExamTypeBadge = (s: ExamStructure) => {
    const cfg = s.config as ExamStructureConfig | undefined;
    return cfg?.exam_type;
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl font-bold tracking-tight">Exam Structures</h1>
          <p className="text-muted-foreground">Define exam structure templates by entity and industry.</p>
        </div>
        <Button size="sm" onClick={() => setCreateOpen(true)}>
          <Plus className="h-4 w-4 mr-1" /> Create Structure
        </Button>
      </div>

      <AiTipBanner context="exam-structures" variant="insight" />

      <div className="flex gap-3 items-center">
        <div className="relative flex-1 max-w-sm">
          <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
          <Input placeholder="Search structures..." className="pl-9" value={search} onChange={(e) => setSearch(e.target.value)} />
        </div>
        <Select value={entityFilter} onValueChange={setEntityFilter}>
          <SelectTrigger className="w-[140px]"><SelectValue placeholder="Entity" /></SelectTrigger>
          <SelectContent><SelectItem value="all">All Entities</SelectItem></SelectContent>
        </Select>
      </div>

      {isLoading && (
        <div className="flex items-center justify-center py-12">
          <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
        </div>
      )}

      {error && (
        <Card className="border-destructive">
          <CardContent className="p-4 text-sm text-destructive">Failed to load structures.</CardContent>
        </Card>
      )}

      {!isLoading && !error && filtered.length === 0 && (
        <Card className="border-dashed">
          <CardContent className="p-8 text-center text-muted-foreground">No exam structures found. Create one to get started.</CardContent>
        </Card>
      )}

      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
        {filtered.map((s) => {
          const examType = getExamTypeBadge(s);
          return (
            <Card key={s.id} className="border-0 shadow-sm hover:shadow-md transition-shadow cursor-pointer" onClick={() => setEditTarget(s)}>
              <CardHeader className="pb-3">
                <div className="flex items-center justify-between">
                  <CardTitle className="text-base font-semibold flex items-center gap-2">
                    <Layers className="h-4 w-4 text-primary" />{s.name}
                  </CardTitle>
                  <div className="flex gap-1">
                    <Button variant="ghost" size="icon" className="h-8 w-8 text-muted-foreground hover:text-primary"
                      onClick={(e) => { e.stopPropagation(); setEditTarget(s); }}>
                      <Pencil className="h-4 w-4" />
                    </Button>
                    <Button variant="ghost" size="icon" className="h-8 w-8 text-destructive"
                      onClick={(e) => { e.stopPropagation(); deleteMut.mutate(s.id); }} disabled={deleteMut.isPending}>
                      <Trash2 className="h-4 w-4" />
                    </Button>
                  </div>
                </div>
              </CardHeader>
              <CardContent>
                <div className="flex items-center gap-3 text-sm text-muted-foreground mb-3 flex-wrap">
                  {s.entity_name && <span>Entity: <span className="text-foreground font-medium">{s.entity_name}</span></span>}
                  {s.industry && <span>Industry: <span className="text-foreground font-medium">{s.industry}</span></span>}
                  {examType && <Badge variant="outline" className="capitalize text-xs">{examType}</Badge>}
                </div>
                <div className="flex gap-1.5 flex-wrap">
                  {(Array.isArray(s.modules) ? s.modules : []).map((m) => (
                    <Badge key={m} variant="secondary" className="capitalize text-xs">{m}</Badge>
                  ))}
                </div>
              </CardContent>
            </Card>
          );
        })}
      </div>

      {/* Create wizard */}
      <ExamStructureWizard
        open={createOpen}
        onOpenChange={setCreateOpen}
        initial={emptyWizard()}
        onSave={(payload) => createMut.mutate(payload)}
        saving={createMut.isPending}
        title="Create Exam Structure"
      />

      {/* Edit wizard */}
      {editTarget && (
        <ExamStructureWizard
          key={editTarget.id}
          open={!!editTarget}
          onOpenChange={(v) => { if (!v) setEditTarget(null); }}
          initial={wizardFromStructure(editTarget)}
          onSave={(payload) => updateMut.mutate({ id: editTarget.id, payload })}
          saving={updateMut.isPending}
          title="Edit Exam Structure"
        />
      )}
    </div>
  );
}
