import { type ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { ArrowLeft, ChevronDown, ChevronRight, Image as ImageIcon, Loader2, Mic, Plus, Trash2, Upload } from "lucide-react"
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card"
import { Button } from "../../components/ui/button"
import { Input } from "../../components/ui/input"
import { Textarea } from "../../components/ui/textarea"
import { Stepper } from "../../components/ui/stepper"
import {
  addQuestionToSet,
  deleteQuestion,
  getCourseCategories,
  getCoursesByCategory,
  getQuestionById,
  getSubCoursesByCourse,
  createQuestion,
  createQuestionSet,
  // getQuestions,
  getPracticeQuestionsByPractice,
  getQuestionSets,
  updateQuestion,
} from "../../api/courses.api"
import { resolveFileUrl, uploadAudioFile, uploadImageFile, uploadVideoFile } from "../../api/files.api"
import { SpinnerIcon } from "../../components/ui/spinner-icon"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuTrigger,
} from "../../components/ui/dropdown-menu"
import type { Course, CourseCategory, QuestionDetail, QuestionSet, SubCourse } from "../../types/course.types"
import { toast } from "sonner"

const MAX_AUDIO_SIZE_BYTES = 50 * 1024 * 1024
const ALLOWED_AUDIO_EXTENSIONS = new Set(["mp3", "wav", "ogg", "m4a", "aac", "webm", "flac"])
const MAX_IMAGE_SIZE_BYTES = 10 * 1024 * 1024
const ALLOWED_IMAGE_EXTENSIONS = new Set(["jpg", "jpeg", "png", "webp", "gif"])
const SPEAKING_STEPS = ["Practice", "Questions", "Review"]

type SubCourseOption = {
  id: number
  title: string
  courseTitle: string
  categoryName: string
}

type RecordingField = "voice_prompt" | "sample_answer_voice_prompt"

type AudioQuestionDraft = {
  questionText: string
  difficulty: "EASY" | "MEDIUM" | "HARD"
  points: number
  voicePrompt: string
  sampleAnswerVoicePrompt: string
  audioCorrectAnswerText: string
  imageUrl: string
  tips: string
  explanation: string
  voicePromptPreviewUrl: string
  samplePromptPreviewUrl: string
  imagePreviewUrl: string
  uploadingVoicePrompt: boolean
  uploadingSamplePrompt: boolean
  uploadingImage: boolean
  recordingVoicePrompt: boolean
  recordingSamplePrompt: boolean
}

type PracticeFilterOption = {
  id: number
  title: string
  description?: string
  persona?: string
  status?: string
}

type AudioListQuestion = QuestionDetail & {
  practice_id: number | null
  practice_title: string | null
}

const createEmptyDraft = (): AudioQuestionDraft => ({
  questionText: "",
  difficulty: "EASY",
  points: 1,
  voicePrompt: "",
  sampleAnswerVoicePrompt: "",
  audioCorrectAnswerText: "",
  imageUrl: "",
  tips: "",
  explanation: "",
  voicePromptPreviewUrl: "",
  samplePromptPreviewUrl: "",
  imagePreviewUrl: "",
  uploadingVoicePrompt: false,
  uploadingSamplePrompt: false,
  uploadingImage: false,
  recordingVoicePrompt: false,
  recordingSamplePrompt: false,
})

function normalizeObjectKey(value: string) {
  const trimmed = value.trim()
  if (!trimmed) return ""
  if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed
  const protocolMatch = trimmed.match(/^[a-z]+:\/\//i)
  if (protocolMatch) {
    return trimmed.replace(/^[a-z]+:\/\//i, "")
  }
  return trimmed
}

/** Prefer direct storage URL; for Vimeo pipeline match SubCourseContentPage player URL shape. */
function introVideoUrlFromUploadResponse(data: { url?: string; embed_url?: string } | undefined): string | null {
  if (!data) return null
  const pageUrl = data.url?.trim()
  const embedUrl = data.embed_url?.trim()
  if (embedUrl) {
    const hashFromUrl = pageUrl ? pageUrl.split("/").filter(Boolean).at(-1) : undefined
    return hashFromUrl ? `${embedUrl}?h=${hashFromUrl}` : embedUrl
  }
  return pageUrl || null
}

function toVimeoEmbedUrl(rawUrl: string): string | null {
  try {
    const parsed = new URL(rawUrl.trim())
    const host = parsed.hostname.toLowerCase()
    if (!host.includes("vimeo.com")) return null

    if (host.includes("player.vimeo.com") && parsed.pathname.includes("/video/")) {
      return parsed.toString()
    }

    const segments = parsed.pathname.split("/").filter(Boolean)
    const videoId = segments.find((segment) => /^\d+$/.test(segment))
    if (!videoId) return null

    const hash = parsed.searchParams.get("h")
    return hash
      ? `https://player.vimeo.com/video/${videoId}?h=${encodeURIComponent(hash)}`
      : `https://player.vimeo.com/video/${videoId}`
  } catch {
    return null
  }
}

export function SpeakingPage() {
  const [audioQuestions, setAudioQuestions] = useState<AudioListQuestion[]>([])
  const [audioTotalCount, setAudioTotalCount] = useState(0)
  const [audioPage, setAudioPage] = useState(1)
  const [audioPageSize] = useState(12)
  const [practiceOptions, setPracticeOptions] = useState<PracticeFilterOption[]>([])
  const [selectedPracticeId, setSelectedPracticeId] = useState<string>("")
  const [practiceFilterOpen, setPracticeFilterOpen] = useState(false)
  const [practiceFilterSearch, setPracticeFilterSearch] = useState("")
  const [audioPreviewByQuestionId, setAudioPreviewByQuestionId] = useState<Record<number, string>>({})
  const [imagePreviewByQuestionId, setImagePreviewByQuestionId] = useState<Record<number, string>>({})
  const [searchQuery, setSearchQuery] = useState("")
  const [selectedQuestionIds, setSelectedQuestionIds] = useState<number[]>([])
  const [bulkDeleting, setBulkDeleting] = useState(false)
  const [collapsedPracticeIds, setCollapsedPracticeIds] = useState<number[]>([])
  const [loading, setLoading] = useState(false)
  const [saving, setSaving] = useState(false)
  const [openCreate, setOpenCreate] = useState(false)

  const [setTitle, setSetTitle] = useState("")
  const [setDescription, setSetDescription] = useState("")
  const [introVideoUrl, setIntroVideoUrl] = useState("")
  const [uploadingIntroVideo, setUploadingIntroVideo] = useState(false)
  const introVideoFileInputRef = useRef<HTMLInputElement>(null)
  const [subCourseId, setSubCourseId] = useState("")
  const [subCourseOptions, setSubCourseOptions] = useState<SubCourseOption[]>([])
  const [subCourseLoading, setSubCourseLoading] = useState(false)
  const [subCourseSearch, setSubCourseSearch] = useState("")
  const [subCourseMenuOpen, setSubCourseMenuOpen] = useState(false)
  const [setStatus, setSetStatus] = useState<"DRAFT" | "PUBLISHED">("PUBLISHED")
  const [createdSetId, setCreatedSetId] = useState<number | null>(null)
  const [creatingSet, setCreatingSet] = useState(false)
  const [currentStep, setCurrentStep] = useState(1)
  const [detailOpen, setDetailOpen] = useState(false)
  const [detailLoading, setDetailLoading] = useState(false)
  const [selectedQuestionDetail, setSelectedQuestionDetail] = useState<QuestionDetail | null>(null)
  const [detailVoiceUrl, setDetailVoiceUrl] = useState("")
  const [detailSampleVoiceUrl, setDetailSampleVoiceUrl] = useState("")
  const [detailImageUrl, setDetailImageUrl] = useState("")
  const [detailEditing, setDetailEditing] = useState(false)
  const [detailSaving, setDetailSaving] = useState(false)
  const [detailDeleting, setDetailDeleting] = useState(false)
  const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false)
  const [deleteTarget, setDeleteTarget] = useState<{ id: number; text: string } | null>(null)
  const [detailForm, setDetailForm] = useState({
    question_text: "",
    question_type: "AUDIO" as const,
    difficulty_level: "EASY" as "EASY" | "MEDIUM" | "HARD",
    points: 1,
    explanation: "",
    tips: "",
    voice_prompt: "",
    sample_answer_voice_prompt: "",
    image_url: "",
    status: "DRAFT" as "DRAFT" | "PUBLISHED" | "INACTIVE",
    audio_correct_answer_text: "",
  })

  const [questionDrafts, setQuestionDrafts] = useState<AudioQuestionDraft[]>([createEmptyDraft()])
  const [recordingModal, setRecordingModal] = useState<{
    draftIndex: number
    field: RecordingField
    levels: number[]
    label: string
    elapsedSeconds: number
    isPaused: boolean
  } | null>(null)
  const activeRecordingRef = useRef<{
    recorder: MediaRecorder
    stream: MediaStream
    chunks: BlobPart[]
    audioContext: AudioContext
    analyser: AnalyserNode
    rafId: number | null
    draftIndex: number
    field: RecordingField
    shouldUpload: boolean
    isPaused: boolean
    startedAtMs: number
    pausedTotalMs: number
    pauseStartedAtMs: number | null
  } | null>(null)

  const resolvePreviewUrl = useCallback(async (value: string) => {
    if (!value.trim()) return ""
    if (value.startsWith("http://") || value.startsWith("https://")) return value

    const key = normalizeObjectKey(value)
    if (!key) return ""

    const res = await resolveFileUrl(key)
    return res.data?.data?.url ?? ""
  }, [])

  const fetchAudioQuestions = useCallback(async (page: number = audioPage) => {
    setLoading(true)
    try {
      const safePage = page < 1 ? 1 : page
      let rows: AudioListQuestion[] = []
      let total = 0

      if (selectedPracticeId) {
        const offset = (safePage - 1) * audioPageSize
        const practiceRes = await getPracticeQuestionsByPractice(Number(selectedPracticeId), {
          limit: audioPageSize,
          offset,
          question_type: "AUDIO",
        })
        const practiceData = practiceRes.data?.data
        const selectedPractice = practiceOptions.find((p) => p.id === Number(selectedPracticeId))
        rows = (practiceData?.questions ?? []).map((q) => ({
          id: q.question_id || q.id,
          question_text: q.question_text,
          question_type: q.question_type,
          difficulty_level: q.difficulty_level ?? undefined,
          points: q.points ?? 0,
          explanation: q.explanation ?? undefined,
          tips: q.tips ?? undefined,
          voice_prompt: q.voice_prompt ?? undefined,
          sample_answer_voice_prompt: q.sample_answer_voice_prompt ?? undefined,
          image_url: q.image_url ?? undefined,
          status: q.question_status ?? "DRAFT",
          created_at: "",
          audio_correct_answer_text: q.audio_correct_answer_text ?? undefined,
          practice_id: Number(selectedPracticeId),
          practice_title: selectedPractice?.title ?? `Practice #${selectedPracticeId}`,
        }))
        const q = searchQuery.trim().toLowerCase()
        if (q) {
          rows = rows.filter((question) => {
            const haystack =
              `${question.question_text} ${question.audio_correct_answer_text ?? ""} ${question.practice_title ?? ""}`.toLowerCase()
            return haystack.includes(q)
          })
        }
        total = searchQuery.trim() ? rows.length : (practiceData?.total_count ?? rows.length)
      } else {
        const groupedRows = await Promise.all(
          practiceOptions.map(async (practice) => {
            try {
              const res = await getPracticeQuestionsByPractice(practice.id, {
                limit: 100,
                offset: 0,
                question_type: "AUDIO",
              })
              const questions = res.data?.data?.questions ?? []
              return questions.map((q) => ({
                id: q.question_id || q.id,
                question_text: q.question_text,
                question_type: q.question_type,
                difficulty_level: q.difficulty_level ?? undefined,
                points: q.points ?? 0,
                explanation: q.explanation ?? undefined,
                tips: q.tips ?? undefined,
                voice_prompt: q.voice_prompt ?? undefined,
                sample_answer_voice_prompt: q.sample_answer_voice_prompt ?? undefined,
                image_url: q.image_url ?? undefined,
                status: q.question_status ?? "DRAFT",
                created_at: "",
                audio_correct_answer_text: q.audio_correct_answer_text ?? undefined,
                practice_id: practice.id,
                practice_title: practice.title,
              }))
            } catch {
              return []
            }
          }),
        )
        rows = groupedRows.flat()
        const q = searchQuery.trim().toLowerCase()
        if (q) {
          rows = rows.filter((question) => {
            const haystack =
              `${question.question_text} ${question.audio_correct_answer_text ?? ""} ${question.practice_title ?? ""}`.toLowerCase()
            return haystack.includes(q)
          })
        }
        total = rows.length
        const offset = (safePage - 1) * audioPageSize
        rows = rows.slice(offset, offset + audioPageSize)
      }

      setAudioQuestions(rows)
      setAudioTotalCount(total)
      setAudioPage(safePage)
    } catch (error) {
      console.error("Failed to fetch audio questions:", error)
      setAudioQuestions([])
      setAudioTotalCount(0)
    } finally {
      setLoading(false)
    }
  }, [audioPage, audioPageSize, selectedPracticeId, practiceOptions, searchQuery])

  useEffect(() => {
    fetchAudioQuestions()
  }, [fetchAudioQuestions, audioPageSize, selectedPracticeId])

  const fetchPracticeOptions = useCallback(async () => {
    const batchSize = 100
    let offset = 0
    let total = Number.POSITIVE_INFINITY
    const all: QuestionSet[] = []
    while (all.length < total) {
      const res = await getQuestionSets({
        set_type: "PRACTICE",
        limit: batchSize,
        offset,
      })
      const payload = res.data?.data
      let chunk: QuestionSet[] = []
      let chunkTotal = 0
      if (Array.isArray(payload)) {
        chunk = payload
        chunkTotal = payload.length
      } else if (payload && typeof payload === "object") {
        chunk = payload.question_sets ?? []
        chunkTotal = payload.total_count ?? chunk.length
      }
      all.push(...chunk)
      total = chunkTotal
      if (chunk.length < batchSize) break
      offset += chunk.length
    }
    // Speaking page should only offer practices that already contain AUDIO questions.
    const checks = await Promise.all(
      all.map(async (practice) => {
        try {
          const res = await getPracticeQuestionsByPractice(practice.id, {
            limit: 20,
            offset: 0,
            question_type: "AUDIO",
          })
          const questions = res.data?.data?.questions ?? []
          const hasAudioQuestion = questions.some(
            (question) => (question.question_type ?? "").toUpperCase() === "AUDIO",
          )
          return hasAudioQuestion ? practice : null
        } catch {
          return null
        }
      }),
    )

    const speakingPractices = checks.filter((p): p is QuestionSet => p !== null)
    setPracticeOptions(
      speakingPractices.map((p) => ({
        id: p.id,
        title: p.title,
        description: p.description ?? "",
        persona: p.persona ?? "",
        status: p.status ?? "",
      })),
    )
  }, [])

  useEffect(() => {
    fetchPracticeOptions().catch(() => {
      setPracticeOptions([])
    })
  }, [fetchPracticeOptions])

  useEffect(() => {
    if (!selectedPracticeId) return
    const exists = practiceOptions.some((option) => option.id === Number(selectedPracticeId))
    if (!exists) setSelectedPracticeId("")
  }, [practiceOptions, selectedPracticeId])

  useEffect(() => {
    let cancelled = false
    const fetchSubCourseOptions = async () => {
      setSubCourseLoading(true)
      try {
        const categoriesRes = await getCourseCategories()
        const categories = categoriesRes.data?.data?.categories ?? []
        if (categories.length === 0) {
          if (!cancelled) setSubCourseOptions([])
          return
        }

        const coursesByCategoryResponses = await Promise.all(
          categories.map(async (category: CourseCategory) => {
            const res = await getCoursesByCategory(category.id)
            return {
              category,
              courses: res.data?.data?.courses ?? [],
            }
          }),
        )

        const courseRecords = coursesByCategoryResponses.flatMap(({ category, courses }) =>
          courses.map((course: Course) => ({ category, course })),
        )

        const subCourseResponses = await Promise.all(
          courseRecords.map(async ({ category, course }) => {
            const res = await getSubCoursesByCourse(course.id)
            const subCourses = res.data?.data?.sub_courses ?? []
            return subCourses.map((subCourse: SubCourse) => ({
              id: subCourse.id,
              title: subCourse.title,
              courseTitle: course.title,
              categoryName: category.name,
            }))
          }),
        )

        const options = subCourseResponses
          .flat()
          .sort((a, b) => a.title.localeCompare(b.title))
        if (!cancelled) setSubCourseOptions(options)
      } catch (error) {
        console.error("Failed to load course options:", error)
        if (!cancelled) setSubCourseOptions([])
      } finally {
        if (!cancelled) setSubCourseLoading(false)
      }
    }
    fetchSubCourseOptions()
    return () => {
      cancelled = true
    }
  }, [])

  useEffect(() => {
    let cancelled = false
    const withVoice = audioQuestions.filter((q) => Boolean(q.voice_prompt))
    if (withVoice.length === 0) {
      setAudioPreviewByQuestionId({})
      return
    }

    const resolveAll = async () => {
      const entries = await Promise.all(
        withVoice.map(async (question) => {
          try {
            const url = await resolvePreviewUrl(question.voice_prompt ?? "")
            return [question.id, url] as const
          } catch {
            return [question.id, ""] as const
          }
        }),
      )
      if (!cancelled) {
        setAudioPreviewByQuestionId(Object.fromEntries(entries))
      }
    }

    resolveAll()
    return () => {
      cancelled = true
    }
  }, [audioQuestions, resolvePreviewUrl])

  useEffect(() => {
    let cancelled = false
    const withImages = audioQuestions.filter((q) => Boolean(q.image_url))
    if (withImages.length === 0) {
      setImagePreviewByQuestionId({})
      return
    }

    const resolveAll = async () => {
      const entries = await Promise.all(
        withImages.map(async (question) => {
          try {
            const url = await resolvePreviewUrl(question.image_url ?? "")
            return [question.id, url] as const
          } catch {
            return [question.id, ""] as const
          }
        }),
      )
      if (!cancelled) {
        setImagePreviewByQuestionId(Object.fromEntries(entries))
      }
    }

    resolveAll()
    return () => {
      cancelled = true
    }
  }, [audioQuestions, resolvePreviewUrl])

  useEffect(() => {
    setSelectedQuestionIds([])
  }, [selectedPracticeId, audioPage, searchQuery])

  const resetCreateForm = () => {
    setSetTitle("")
    setSetDescription("")
    setIntroVideoUrl("")
    setSubCourseId("")
    setSetStatus("PUBLISHED")
    setCreatedSetId(null)
    setQuestionDrafts([createEmptyDraft()])
  }

  const handleProceedToQuestions = async () => {
    if (!canProceedToQuestions) return
    if (createdSetId) {
      setCurrentStep(2)
      return
    }

    const parsedSubCourseId = Number(subCourseId)
    if (!Number.isFinite(parsedSubCourseId) || parsedSubCourseId <= 0) {
      toast.error("Please select a valid sub-course")
      return
    }

    setCreatingSet(true)
    try {
      const setRes = await createQuestionSet({
        title: setTitle.trim(),
        ...(setDescription.trim() ? { description: setDescription.trim() } : {}),
        set_type: "PRACTICE",
        owner_type: "SUB_COURSE",
        owner_id: parsedSubCourseId,
        status: setStatus,
        ...(introVideoUrl.trim() ? { intro_video_url: introVideoUrl.trim() } : {}),
      })
      const setId = setRes.data?.data?.id
      if (!setId) throw new Error("Question set creation failed: missing set ID")
      setCreatedSetId(setId)
      setCurrentStep(2)
      toast.success("Practice created. Continue adding questions.")
    } catch (error) {
      console.error("Failed to create speaking practice set:", error)
      toast.error("Failed to create practice set")
    } finally {
      setCreatingSet(false)
    }
  }

  const handleIntroVideoFileChange = async (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0]
    event.target.value = ""
    if (!file) return

    setUploadingIntroVideo(true)
    try {
      const uploadRes = await uploadVideoFile(file, {
        title: setTitle.trim() || file.name.replace(/\.[^.]+$/, "") || "Speaking intro",
        description: setDescription.trim() || undefined,
      })
      const finalUrl = introVideoUrlFromUploadResponse(uploadRes.data?.data)
      if (!finalUrl) throw new Error("Missing uploaded video url")
      setIntroVideoUrl(finalUrl)
      toast.success("Intro video uploaded", { description: "The URL has been filled in for you." })
    } catch (error) {
      console.error("Failed to upload intro video:", error)
      toast.error("Failed to upload intro video")
    } finally {
      setUploadingIntroVideo(false)
    }
  }

  const canCreate = useMemo(() => {
    const hasQuestionWithText = questionDrafts.some((draft) => draft.questionText.trim().length > 0)
    return setTitle.trim().length > 0 && subCourseId.trim().length > 0 && hasQuestionWithText
  }, [setTitle, subCourseId, questionDrafts])
  const canProceedToQuestions = useMemo(
    () => setTitle.trim().length > 0 && subCourseId.trim().length > 0,
    [setTitle, subCourseId],
  )
  const questionsWithText = useMemo(
    () => questionDrafts.filter((draft) => draft.questionText.trim().length > 0),
    [questionDrafts],
  )
  const canProceedToReview = questionsWithText.length > 0
  const selectedSubCourseOption = useMemo(
    () => subCourseOptions.find((option) => option.id === Number(subCourseId)),
    [subCourseId, subCourseOptions],
  )
  const filteredSubCourseOptions = useMemo(() => {
    const q = subCourseSearch.trim().toLowerCase()
    if (!q) return subCourseOptions
    return subCourseOptions.filter((option) => {
      const haystack = `${option.title} ${option.courseTitle} ${option.categoryName} ${option.id}`.toLowerCase()
      return haystack.includes(q)
    })
  }, [subCourseOptions, subCourseSearch])
  const introVideoPreview = useMemo(() => {
    const value = introVideoUrl.trim()
    if (!value || !/^https?:\/\//i.test(value)) return null
    const vimeoEmbedUrl = toVimeoEmbedUrl(value)
    if (vimeoEmbedUrl) return { kind: "iframe" as const, src: vimeoEmbedUrl }
    return { kind: "video" as const, src: value }
  }, [introVideoUrl])
  const filteredPracticeOptions = useMemo(() => {
    const query = practiceFilterSearch.trim().toLowerCase()
    if (!query) return practiceOptions
    return practiceOptions.filter((practice) => {
      const haystack = `${practice.title} ${practice.id}`.toLowerCase()
      return haystack.includes(query)
    })
  }, [practiceOptions, practiceFilterSearch])

  const updateDraft = (index: number, updater: (draft: AudioQuestionDraft) => AudioQuestionDraft) => {
    setQuestionDrafts((prev) => prev.map((draft, idx) => (idx === index ? updater(draft) : draft)))
  }

  const uploadAudioForDraft = useCallback(
    async (file: File, field: "voice_prompt" | "sample_answer_voice_prompt", draftIndex: number) => {
      const extension = file.name.split(".").pop()?.toLowerCase() ?? ""
      if (!ALLOWED_AUDIO_EXTENSIONS.has(extension)) {
        toast.error("Unsupported audio format")
        return
      }
      if (file.size > MAX_AUDIO_SIZE_BYTES) {
        toast.error("Audio file must be 50MB or less")
        return
      }

      updateDraft(draftIndex, (draft) =>
        field === "voice_prompt"
          ? { ...draft, uploadingVoicePrompt: true }
          : { ...draft, uploadingSamplePrompt: true },
      )

      try {
        const res = await uploadAudioFile(file)
        const objectKey = res.data?.data?.object_key?.trim()
        const immediateUrl = res.data?.data?.url?.trim() ?? ""
        if (!objectKey) throw new Error("Missing uploaded audio object key")

        if (field === "voice_prompt") {
          updateDraft(draftIndex, (draft) => ({
            ...draft,
            voicePrompt: objectKey,
            voicePromptPreviewUrl: immediateUrl || draft.voicePromptPreviewUrl,
          }))
          if (!immediateUrl) {
            const resolvedUrl = await resolvePreviewUrl(objectKey)
            updateDraft(draftIndex, (draft) => ({ ...draft, voicePromptPreviewUrl: resolvedUrl }))
          }
        } else {
          updateDraft(draftIndex, (draft) => ({
            ...draft,
            sampleAnswerVoicePrompt: objectKey,
            samplePromptPreviewUrl: immediateUrl || draft.samplePromptPreviewUrl,
          }))
          if (!immediateUrl) {
            const resolvedUrl = await resolvePreviewUrl(objectKey)
            updateDraft(draftIndex, (draft) => ({ ...draft, samplePromptPreviewUrl: resolvedUrl }))
          }
        }
        toast.success("Audio uploaded successfully")
      } catch (error) {
        console.error("Failed to upload audio:", error)
        toast.error("Failed to upload audio file")
      } finally {
        updateDraft(draftIndex, (draft) =>
          field === "voice_prompt"
            ? { ...draft, uploadingVoicePrompt: false }
            : { ...draft, uploadingSamplePrompt: false },
        )
      }
    },
    [resolvePreviewUrl],
  )

  const handleAudioUpload = async (
    event: ChangeEvent<HTMLInputElement>,
    field: "voice_prompt" | "sample_answer_voice_prompt",
    draftIndex: number,
  ) => {
    const file = event.target.files?.[0]
    if (!file) return
    await uploadAudioForDraft(file, field, draftIndex)
    event.target.value = ""
  }

  const handleImageUpload = async (event: ChangeEvent<HTMLInputElement>, draftIndex: number) => {
    const file = event.target.files?.[0]
    if (!file) return

    const extension = file.name.split(".").pop()?.toLowerCase() ?? ""
    if (!ALLOWED_IMAGE_EXTENSIONS.has(extension)) {
      toast.error("Unsupported image format")
      event.target.value = ""
      return
    }
    if (file.size > MAX_IMAGE_SIZE_BYTES) {
      toast.error("Image file must be 10MB or less")
      event.target.value = ""
      return
    }

    updateDraft(draftIndex, (draft) => ({ ...draft, uploadingImage: true }))
    try {
      const res = await uploadImageFile(file)
      const uploadedUrl = res.data?.data?.url?.trim()
      if (!uploadedUrl) throw new Error("Missing uploaded image url")
      const resolved = uploadedUrl
      updateDraft(draftIndex, (draft) => ({
        ...draft,
        imageUrl: uploadedUrl,
        imagePreviewUrl: resolved,
      }))
      toast.success("Image uploaded successfully")
    } catch (error) {
      console.error("Failed to upload image:", error)
      toast.error("Failed to upload image")
    } finally {
      updateDraft(draftIndex, (draft) => ({ ...draft, uploadingImage: false }))
      event.target.value = ""
    }
  }

  const handleResolveManualAudioPreview = async (
    field: "voice_prompt" | "sample_answer_voice_prompt",
    draftIndex: number,
  ) => {
    const selectedDraft = questionDrafts[draftIndex]
    if (!selectedDraft) return
    const rawValue = field === "voice_prompt" ? selectedDraft.voicePrompt : selectedDraft.sampleAnswerVoicePrompt
    if (!rawValue.trim()) {
      updateDraft(draftIndex, (draft) =>
        field === "voice_prompt"
          ? { ...draft, voicePromptPreviewUrl: "" }
          : { ...draft, samplePromptPreviewUrl: "" },
      )
      return
    }
    try {
      const trimmedValue = rawValue.trim()
      const isURL = /^https?:\/\//i.test(trimmedValue)

      if (isURL) {
        updateDraft(draftIndex, (draft) =>
          field === "voice_prompt"
            ? { ...draft, uploadingVoicePrompt: true }
            : { ...draft, uploadingSamplePrompt: true },
        )

        const res = await uploadAudioFile(trimmedValue)
        const objectKey = res.data?.data?.object_key?.trim()
        const immediateUrl = res.data?.data?.url?.trim() ?? ""
        if (!objectKey) throw new Error("Missing uploaded audio object key")

        if (field === "voice_prompt") {
          updateDraft(draftIndex, (draft) => ({
            ...draft,
            voicePrompt: objectKey,
            voicePromptPreviewUrl: immediateUrl || draft.voicePromptPreviewUrl,
          }))
          if (!immediateUrl) {
            const resolvedUrl = await resolvePreviewUrl(objectKey)
            updateDraft(draftIndex, (draft) => ({ ...draft, voicePromptPreviewUrl: resolvedUrl }))
          }
        } else {
          updateDraft(draftIndex, (draft) => ({
            ...draft,
            sampleAnswerVoicePrompt: objectKey,
            samplePromptPreviewUrl: immediateUrl || draft.samplePromptPreviewUrl,
          }))
          if (!immediateUrl) {
            const resolvedUrl = await resolvePreviewUrl(objectKey)
            updateDraft(draftIndex, (draft) => ({ ...draft, samplePromptPreviewUrl: resolvedUrl }))
          }
        }
        toast.success("Audio URL imported successfully")
        return
      }

      const url = await resolvePreviewUrl(rawValue)
      updateDraft(draftIndex, (draft) =>
        field === "voice_prompt"
          ? { ...draft, voicePromptPreviewUrl: url }
          : { ...draft, samplePromptPreviewUrl: url },
      )
    } catch (error) {
      console.error("Failed to resolve audio preview URL:", error)
      toast.error("Could not import/resolve audio URL")
    } finally {
      updateDraft(draftIndex, (draft) =>
        field === "voice_prompt"
          ? { ...draft, uploadingVoicePrompt: false }
          : { ...draft, uploadingSamplePrompt: false },
      )
    }
  }

  const handleResolveManualImagePreview = async (draftIndex: number) => {
    const draft = questionDrafts[draftIndex]
    if (!draft) return
    if (!draft.imageUrl.trim()) {
      updateDraft(draftIndex, (current) => ({ ...current, imagePreviewUrl: "" }))
      return
    }
    try {
      const resolved = await resolvePreviewUrl(draft.imageUrl)
      updateDraft(draftIndex, (current) => ({ ...current, imagePreviewUrl: resolved }))
    } catch (error) {
      console.error("Failed to resolve image preview URL:", error)
      toast.error("Could not resolve image preview URL")
    }
  }

  const stopActiveRecording = async (saveRecording: boolean) => {
    const active = activeRecordingRef.current
    if (!active) return
    active.shouldUpload = saveRecording
    if (active.recorder.state !== "inactive") {
      active.recorder.stop()
      return
    }
    if (!saveRecording) {
      active.stream.getTracks().forEach((track) => track.stop())
      if (active.rafId) cancelAnimationFrame(active.rafId)
      await active.audioContext.close().catch(() => undefined)
      activeRecordingRef.current = null
      setRecordingModal(null)
    }
  }

  const togglePauseRecording = () => {
    const active = activeRecordingRef.current
    if (!active) return
    if (active.recorder.state === "recording") {
      active.recorder.pause()
      active.isPaused = true
      active.pauseStartedAtMs = Date.now()
      setRecordingModal((prev) => (prev ? { ...prev, isPaused: true } : prev))
      return
    }
    if (active.recorder.state === "paused") {
      active.recorder.resume()
      if (active.pauseStartedAtMs) {
        active.pausedTotalMs += Date.now() - active.pauseStartedAtMs
      }
      active.pauseStartedAtMs = null
      active.isPaused = false
      setRecordingModal((prev) => (prev ? { ...prev, isPaused: false } : prev))
    }
  }

  const startRecording = async (field: RecordingField, draftIndex: number) => {
    if (activeRecordingRef.current) {
      toast.error("Finish current recording first")
      return
    }
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
      const recorder = new MediaRecorder(stream)
      const chunks: BlobPart[] = []
      const audioContext = new AudioContext()
      const analyser = audioContext.createAnalyser()
      analyser.fftSize = 256
      const source = audioContext.createMediaStreamSource(stream)
      source.connect(analyser)
      const dataArray = new Uint8Array(analyser.frequencyBinCount)
      const label = field === "voice_prompt" ? "Voice Prompt" : "Sample Answer"
      const animateLevels = () => {
        const active = activeRecordingRef.current
        if (!active) return
        const now = Date.now()
        const effectiveElapsedMs =
          now -
          active.startedAtMs -
          active.pausedTotalMs -
          (active.isPaused && active.pauseStartedAtMs ? now - active.pauseStartedAtMs : 0)
        const elapsedSeconds = Math.max(0, Math.floor(effectiveElapsedMs / 1000))

        if (!active.isPaused) {
          analyser.getByteFrequencyData(dataArray)
          const barCount = 32
          const binsPerBar = Math.max(1, Math.floor(dataArray.length / barCount))
          const nextLevels = Array.from({ length: barCount }, (_, barIdx) => {
            const start = barIdx * binsPerBar
            const end = Math.min(dataArray.length, start + binsPerBar)
            if (start >= end) return 0.05
            let sum = 0
            for (let i = start; i < end; i += 1) sum += dataArray[i]
            const avg = sum / (end - start)
            // Slight boost curve for clearer realtime movement.
            const normalized = Math.min(1, Math.pow(avg / 180, 0.85))
            return Math.max(0.04, normalized)
          })
          setRecordingModal((prev) => {
            if (!prev || prev.draftIndex !== draftIndex || prev.field !== field) return prev
            return { ...prev, levels: nextLevels, elapsedSeconds }
          })
        } else {
          setRecordingModal((prev) => {
            if (!prev || prev.draftIndex !== draftIndex || prev.field !== field) return prev
            return { ...prev, elapsedSeconds }
          })
        }

        if (activeRecordingRef.current) {
          activeRecordingRef.current.rafId = requestAnimationFrame(animateLevels)
        }
      }

      recorder.ondataavailable = (event) => {
        if (event.data.size > 0) chunks.push(event.data)
      }

      recorder.onstop = async () => {
        const active = activeRecordingRef.current
        if (!active) return
        const shouldUpload = active.shouldUpload
        active.stream.getTracks().forEach((track) => track.stop())
        if (active.rafId) cancelAnimationFrame(active.rafId)
        await active.audioContext.close().catch(() => undefined)
        activeRecordingRef.current = null
        setRecordingModal(null)

        if (shouldUpload) {
          const blob = new Blob(chunks, { type: "audio/webm" })
          const fileName =
            field === "voice_prompt"
              ? `voice-prompt-${Date.now()}.webm`
              : `sample-answer-${Date.now()}.webm`
          const recordedFile = new File([blob], fileName, { type: "audio/webm" })
          await uploadAudioForDraft(recordedFile, field, draftIndex)
        }

        updateDraft(draftIndex, (draft) =>
          field === "voice_prompt"
            ? { ...draft, recordingVoicePrompt: false }
            : { ...draft, recordingSamplePrompt: false },
        )
      }

      setRecordingModal({
        draftIndex,
        field,
        levels: Array.from({ length: 32 }, () => 0.05),
        label,
        elapsedSeconds: 0,
        isPaused: false,
      })
      recorder.start()
      activeRecordingRef.current = {
        recorder,
        stream,
        chunks,
        audioContext,
        analyser,
        rafId: null,
        draftIndex,
        field,
        shouldUpload: true,
        isPaused: false,
        startedAtMs: Date.now(),
        pausedTotalMs: 0,
        pauseStartedAtMs: null,
      }
      animateLevels()
      updateDraft(draftIndex, (draft) =>
        field === "voice_prompt"
          ? { ...draft, recordingVoicePrompt: true }
          : { ...draft, recordingSamplePrompt: true },
      )
    } catch (error) {
      console.error("Microphone access failed:", error)
      toast.error("Unable to access microphone")
    }
  }

  useEffect(() => {
    return () => {
      const active = activeRecordingRef.current
      if (!active) return
      active.shouldUpload = false
      if (active.recorder.state !== "inactive") active.recorder.stop()
      active.stream.getTracks().forEach((track) => track.stop())
      if (active.rafId) cancelAnimationFrame(active.rafId)
      active.audioContext.close().catch(() => undefined)
    }
  }, [])

  const handleCreateSpeakingPractice = async () => {
    if (!canCreate) return
    const parsedSubCourseId = Number(subCourseId)
    if (!Number.isFinite(parsedSubCourseId) || parsedSubCourseId <= 0) {
      toast.error("Please enter a valid Course ID")
      return
    }
    const draftsToCreate = questionDrafts.filter((draft) => draft.questionText.trim().length > 0)
    if (draftsToCreate.length === 0) {
      toast.error("Add at least one AUDIO question")
      return
    }

    setSaving(true)
    try {
      const setId = createdSetId
      if (!setId) throw new Error("Question set creation failed: missing set ID")

      // 2) Create all AUDIO questions then attach in sequence.
      for (const [idx, draft] of draftsToCreate.entries()) {
        const questionRes = await createQuestion({
          question_text: draft.questionText.trim(),
          question_type: "AUDIO",
          status: setStatus,
          difficulty_level: draft.difficulty || undefined,
          points: Number.isFinite(draft.points) && draft.points > 0 ? draft.points : 1,
          voice_prompt: draft.voicePrompt.trim() || undefined,
          sample_answer_voice_prompt: draft.sampleAnswerVoicePrompt.trim() || undefined,
          audio_correct_answer_text: draft.audioCorrectAnswerText.trim() || undefined,
          image_url: draft.imageUrl.trim() || undefined,
          explanation: draft.explanation.trim() || undefined,
          tips: draft.tips.trim() || undefined,
        })

        const questionId = questionRes.data?.data?.id
        if (!questionId) throw new Error("Question creation failed: missing question ID")

        // 3) Attach each question to created set with display order.
        await addQuestionToSet(setId, {
          question_id: questionId,
          display_order: idx + 1,
        })
      }

      setOpenCreate(false)
      setCurrentStep(1)
      resetCreateForm()
      await fetchPracticeOptions()
      setSelectedPracticeId(String(setId))
      toast.success(`Speaking practice created with ${draftsToCreate.length} AUDIO question(s)`)
      await fetchAudioQuestions()
    } catch (error) {
      console.error("Failed to create speaking practice:", error)
      toast.error("Failed to create speaking practice")
    } finally {
      setSaving(false)
    }
  }

  const handleOpenQuestionDetail = async (questionId: number) => {
    setDetailOpen(true)
    setDetailLoading(true)
    setSelectedQuestionDetail(null)
    setDetailVoiceUrl("")
    setDetailSampleVoiceUrl("")
    setDetailImageUrl("")
    try {
      const res = await getQuestionById(questionId)
      const detail = res.data?.data ?? null
      setSelectedQuestionDetail(detail)
      if (detail) {
        setDetailForm({
          question_text: detail.question_text ?? "",
          question_type: "AUDIO",
          difficulty_level:
            (detail.difficulty_level as "EASY" | "MEDIUM" | "HARD") || "EASY",
          points: Number(detail.points) > 0 ? Number(detail.points) : 1,
          explanation: detail.explanation ?? "",
          tips: detail.tips ?? "",
          voice_prompt: detail.voice_prompt ?? "",
          sample_answer_voice_prompt: detail.sample_answer_voice_prompt ?? "",
          image_url: detail.image_url ?? "",
          status:
            (detail.status as "DRAFT" | "PUBLISHED" | "INACTIVE") || "DRAFT",
          audio_correct_answer_text: detail.audio_correct_answer_text ?? "",
        })
        setDetailEditing(false)
        const [voice, sample, image] = await Promise.all([
          resolvePreviewUrl(detail.voice_prompt ?? ""),
          resolvePreviewUrl(detail.sample_answer_voice_prompt ?? ""),
          resolvePreviewUrl(detail.image_url ?? ""),
        ])
        setDetailVoiceUrl(voice)
        setDetailSampleVoiceUrl(sample)
        setDetailImageUrl(image)
      }
    } catch (error) {
      console.error("Failed to fetch question detail:", error)
      toast.error("Failed to load question detail")
      setDetailOpen(false)
    } finally {
      setDetailLoading(false)
    }
  }

  const refreshSelectedQuestionDetail = async (questionId: number) => {
    const res = await getQuestionById(questionId)
    const detail = res.data?.data ?? null
    setSelectedQuestionDetail(detail)
    if (detail) {
      const [voice, sample, image] = await Promise.all([
        resolvePreviewUrl(detail.voice_prompt ?? ""),
        resolvePreviewUrl(detail.sample_answer_voice_prompt ?? ""),
        resolvePreviewUrl(detail.image_url ?? ""),
      ])
      setDetailVoiceUrl(voice)
      setDetailSampleVoiceUrl(sample)
      setDetailImageUrl(image)
      setDetailForm({
        question_text: detail.question_text ?? "",
        question_type: "AUDIO",
        difficulty_level:
          (detail.difficulty_level as "EASY" | "MEDIUM" | "HARD") || "EASY",
        points: Number(detail.points) > 0 ? Number(detail.points) : 1,
        explanation: detail.explanation ?? "",
        tips: detail.tips ?? "",
        voice_prompt: detail.voice_prompt ?? "",
        sample_answer_voice_prompt: detail.sample_answer_voice_prompt ?? "",
        image_url: detail.image_url ?? "",
        status:
          (detail.status as "DRAFT" | "PUBLISHED" | "INACTIVE") || "DRAFT",
        audio_correct_answer_text: detail.audio_correct_answer_text ?? "",
      })
    }
  }

  const handleUpdateQuestionDetail = async () => {
    if (!selectedQuestionDetail) return
    setDetailSaving(true)
    try {
      await updateQuestion(selectedQuestionDetail.id, {
        question_text: detailForm.question_text.trim(),
        question_type: "AUDIO",
        difficulty_level: detailForm.difficulty_level,
        points: Number.isFinite(detailForm.points) && detailForm.points > 0 ? detailForm.points : 1,
        explanation: detailForm.explanation.trim() || undefined,
        tips: detailForm.tips.trim() || undefined,
        voice_prompt: detailForm.voice_prompt.trim() || undefined,
        sample_answer_voice_prompt: detailForm.sample_answer_voice_prompt.trim() || undefined,
        image_url: detailForm.image_url.trim() || undefined,
        status: detailForm.status,
        audio_correct_answer_text: detailForm.audio_correct_answer_text.trim() || undefined,
      })
      await refreshSelectedQuestionDetail(selectedQuestionDetail.id)
      await fetchAudioQuestions()
      setDetailEditing(false)
      toast.success("AUDIO question updated")
    } catch (error) {
      console.error("Failed to update AUDIO question:", error)
      toast.error("Failed to update AUDIO question")
    } finally {
      setDetailSaving(false)
    }
  }

  const handleDeleteQuestionDetail = async () => {
    const targetId = deleteTarget?.id ?? selectedQuestionDetail?.id
    if (!targetId) return
    setDetailDeleting(true)
    try {
      await deleteQuestion(targetId)
      setConfirmDeleteOpen(false)
      setDeleteTarget(null)
      setDetailOpen(false)
      setSelectedQuestionDetail(null)
      await fetchAudioQuestions()
      toast.success("AUDIO question deleted")
    } catch (error) {
      console.error("Failed to delete AUDIO question:", error)
      toast.error("Failed to delete AUDIO question")
    } finally {
      setDetailDeleting(false)
    }
  }

  const toggleQuestionSelection = (questionId: number) => {
    setSelectedQuestionIds((prev) =>
      prev.includes(questionId) ? prev.filter((id) => id !== questionId) : [...prev, questionId],
    )
  }

  const toggleSelectAllCurrent = () => {
    const currentIds = audioQuestions.map((q) => q.id)
    if (currentIds.length === 0) return
    const allSelected = currentIds.every((id) => selectedQuestionIds.includes(id))
    if (allSelected) {
      setSelectedQuestionIds((prev) => prev.filter((id) => !currentIds.includes(id)))
    } else {
      setSelectedQuestionIds((prev) => Array.from(new Set([...prev, ...currentIds])))
    }
  }

  const handleBulkDeleteSelected = async () => {
    if (selectedQuestionIds.length === 0) return
    setBulkDeleting(true)
    try {
      for (const id of selectedQuestionIds) {
        await deleteQuestion(id)
      }
      setSelectedQuestionIds([])
      await fetchAudioQuestions()
      toast.success(`Deleted ${selectedQuestionIds.length} AUDIO question(s)`)
    } catch (error) {
      console.error("Failed to delete selected AUDIO questions:", error)
      toast.error("Failed to delete selected AUDIO questions")
    } finally {
      setBulkDeleting(false)
    }
  }

  const togglePracticeCollapsed = (practiceId: number | null) => {
    if (!practiceId) return
    setCollapsedPracticeIds((prev) =>
      prev.includes(practiceId) ? prev.filter((id) => id !== practiceId) : [...prev, practiceId],
    )
  }

  const togglePracticeSelection = (practiceId: number | null) => {
    if (!practiceId) return
    const questionIds = audioQuestions.filter((q) => q.practice_id === practiceId).map((q) => q.id)
    if (questionIds.length === 0) return
    const allSelected = questionIds.every((id) => selectedQuestionIds.includes(id))
    setSelectedQuestionIds((prev) =>
      allSelected ? prev.filter((id) => !questionIds.includes(id)) : Array.from(new Set([...prev, ...questionIds])),
    )
  }

  const groupedAudioQuestions = useMemo(() => {
    const groups = new Map<string, { practiceId: number | null; practiceTitle: string; questions: AudioListQuestion[] }>()
    for (const q of audioQuestions) {
      const key = q.practice_id ? String(q.practice_id) : "unassigned"
      const title = q.practice_title || "Unknown practice"
      if (!groups.has(key)) {
        groups.set(key, { practiceId: q.practice_id, practiceTitle: title, questions: [] })
      }
      groups.get(key)?.questions.push(q)
    }
    return Array.from(groups.values())
  }, [audioQuestions])

  return (
    <div className="mx-auto w-full max-w-7xl space-y-6 pb-10 sm:space-y-8 sm:pb-12">
      <div className="flex flex-col gap-4 border-b border-grayScale-100 pb-6 sm:flex-row sm:items-end sm:justify-between sm:pb-8">
        <div>
          <h1 className="text-2xl font-bold tracking-tight text-grayScale-900 sm:text-3xl">
            Speaking
          </h1>
          <p className="mt-2 max-w-2xl text-sm leading-relaxed text-grayScale-500 sm:text-[15px]">
            Create and manage speaking practice sessions for your learners.
          </p>
        </div>
        <Button
          className="h-11 w-full shrink-0 bg-brand-500 px-5 shadow-sm hover:bg-brand-600 sm:w-auto"
          onClick={() => {
            resetCreateForm()
            setOpenCreate(true)
            setCurrentStep(1)
          }}
        >
          <Plus className="h-4 w-4" />
          Add New Speaking Practice
        </Button>
      </div>

      {!openCreate && (
        <Card className="overflow-hidden border-grayScale-200/80 shadow-sm">
          <CardHeader className="border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/80 to-white px-5 py-4 sm:px-6 sm:py-5">
            <CardTitle className="text-base font-semibold text-grayScale-900 sm:text-lg">
              AUDIO questions
            </CardTitle>
            <p className="mt-1 text-xs font-normal text-grayScale-500 sm:text-sm">
              Tap a row to view details. Speaking practices create AUDIO question sets linked to a sub-course.
            </p>
            <div className="mt-3 max-w-md space-y-1.5">
              <label className="text-xs font-medium uppercase tracking-wide text-grayScale-500">
                Filter by practice
              </label>
              <DropdownMenu open={practiceFilterOpen} onOpenChange={setPracticeFilterOpen}>
                <DropdownMenuTrigger asChild>
                  <Button
                    type="button"
                    variant="outline"
                    className="h-10 w-full justify-between rounded-md border border-grayScale-200 bg-white px-3 text-sm font-normal text-grayScale-700 hover:bg-grayScale-50"
                  >
                    <span className="truncate">
                      {selectedPracticeId
                        ? `${practiceOptions.find((p) => p.id === Number(selectedPracticeId))?.title ?? "Practice"} (#${selectedPracticeId})`
                        : "All practices"}
                    </span>
                    <ChevronDown className="h-4 w-4 text-grayScale-400" />
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent align="start" className="w-[420px] max-w-[92vw] p-2">
                  <Input
                    value={practiceFilterSearch}
                    onChange={(e) => setPracticeFilterSearch(e.target.value)}
                    placeholder="Search practices..."
                    className="mb-2 h-9"
                  />
                  <div className="max-h-64 overflow-auto">
                    <DropdownMenuRadioGroup
                      value={selectedPracticeId}
                      onValueChange={(value) => {
                        setSelectedPracticeId(value)
                        setAudioPage(1)
                        setPracticeFilterOpen(false)
                      }}
                    >
                      <DropdownMenuRadioItem value="">All practices</DropdownMenuRadioItem>
                      {filteredPracticeOptions.map((practice) => (
                        <DropdownMenuRadioItem key={practice.id} value={String(practice.id)}>
                          {practice.title} (#{practice.id})
                        </DropdownMenuRadioItem>
                      ))}
                    </DropdownMenuRadioGroup>
                  </div>
                </DropdownMenuContent>
              </DropdownMenu>
            </div>
            <div className="mt-3 max-w-md space-y-1.5">
              <label className="text-xs font-medium uppercase tracking-wide text-grayScale-500">Search</label>
              <Input
                value={searchQuery}
                onChange={(e) => {
                  setSearchQuery(e.target.value)
                  setAudioPage(1)
                }}
                placeholder="Search question text, answer text, or practice..."
                className="h-10"
              />
            </div>
            <p className="mt-1 text-xs font-normal text-grayScale-400 sm:text-sm">
              Showing page {audioPage} of {Math.max(1, Math.ceil(audioTotalCount / audioPageSize))} ({audioTotalCount} total)
            </p>
          </CardHeader>
          <CardContent className="px-4 pb-6 pt-5 sm:px-6">
            {loading ? (
              <div className="flex flex-col items-center gap-2 py-14 text-center text-sm text-grayScale-500">
                <SpinnerIcon className="h-5 w-5" />
                Loading audio questions...
              </div>
            ) : audioQuestions.length === 0 ? (
              <div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-grayScale-200 bg-grayScale-50/30 py-16 text-center">
                <div className="mb-5 grid h-16 w-16 place-items-center rounded-2xl bg-gradient-to-br from-brand-100 to-brand-200 shadow-sm ring-4 ring-brand-500/10">
                  <Mic className="h-8 w-8 text-brand-600" />
                </div>
                <h3 className="text-base font-semibold text-grayScale-800">No audio questions yet</h3>
                <p className="mt-2 max-w-md text-sm leading-relaxed text-grayScale-500">
                  Create a speaking practice to automatically create and attach an AUDIO question.
                </p>
              </div>
            ) : (
              <div className="space-y-2.5">
                <div className="flex flex-wrap items-center justify-between gap-2 rounded-xl border border-grayScale-200 bg-grayScale-50/40 px-3 py-2">
                  <label className="inline-flex items-center gap-2 text-sm text-grayScale-700">
                    <input
                      type="checkbox"
                      checked={
                        audioQuestions.length > 0 && audioQuestions.every((question) => selectedQuestionIds.includes(question.id))
                      }
                      onChange={toggleSelectAllCurrent}
                    />
                    Select all on this page
                  </label>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    className="border-red-200 text-red-600 hover:bg-red-50 hover:text-red-700"
                    disabled={selectedQuestionIds.length === 0 || bulkDeleting}
                    onClick={handleBulkDeleteSelected}
                  >
                    {bulkDeleting ? "Deleting..." : `Delete selected (${selectedQuestionIds.length})`}
                  </Button>
                </div>
                {groupedAudioQuestions.map((group) => (
                  <div key={group.practiceId ?? "unknown"} className="space-y-2">
                    <div className="rounded-lg border border-grayScale-200 bg-gradient-to-r from-grayScale-50 to-white px-3 py-2">
                      <div className="flex flex-wrap items-center justify-between gap-2">
                        <div className="inline-flex items-center gap-2">
                          <button
                            type="button"
                            className="rounded-md border border-grayScale-200 bg-white p-1 text-grayScale-600 hover:bg-grayScale-50"
                            onClick={() => togglePracticeCollapsed(group.practiceId)}
                          >
                            {group.practiceId && collapsedPracticeIds.includes(group.practiceId) ? (
                              <ChevronRight className="h-4 w-4" />
                            ) : (
                              <ChevronDown className="h-4 w-4" />
                            )}
                          </button>
                          <label className="inline-flex items-center gap-2 text-sm font-semibold text-grayScale-700">
                            <input
                              type="checkbox"
                              checked={
                                group.questions.length > 0 &&
                                group.questions.every((question) => selectedQuestionIds.includes(question.id))
                              }
                              onChange={() => togglePracticeSelection(group.practiceId)}
                            />
                            {group.practiceTitle} {group.practiceId ? `(#${group.practiceId})` : ""}
                          </label>
                        </div>
                      </div>
                    </div>
                    {(group.practiceId && collapsedPracticeIds.includes(group.practiceId) ? [] : group.questions).map((question, idx) => (
                      <div
                        key={question.id}
                        className={`cursor-pointer rounded-xl border px-4 py-3.5 transition-all sm:px-5 ${
                          idx % 2 === 0 ? "border-grayScale-200 bg-white" : "border-grayScale-100 bg-grayScale-50/80"
                        } hover:border-brand-300 hover:bg-brand-50/40 hover:shadow-sm`}
                        onClick={() => handleOpenQuestionDetail(question.id)}
                      >
                        <div className="flex items-start justify-between gap-3">
                          <div className="flex items-start gap-3">
                            <input
                              type="checkbox"
                              checked={selectedQuestionIds.includes(question.id)}
                              onClick={(event) => event.stopPropagation()}
                              onChange={() => toggleQuestionSelection(question.id)}
                              className="mt-1"
                            />
                            <p className="text-sm font-medium leading-snug text-grayScale-800">{question.question_text}</p>
                          </div>
                          <Button
                            variant="ghost"
                            size="icon"
                            className="h-8 w-8 shrink-0 text-red-500 hover:bg-red-50 hover:text-red-600"
                            onClick={(event) => {
                              event.stopPropagation()
                              setDeleteTarget({ id: question.id, text: question.question_text })
                              setConfirmDeleteOpen(true)
                            }}
                          >
                            <Trash2 className="h-4 w-4" />
                          </Button>
                        </div>
                        <div className="mt-2 flex flex-wrap items-center gap-2 text-xs">
                          <span className="rounded-md bg-brand-100 px-2 py-0.5 font-medium text-brand-800">AUDIO</span>
                          <span className="rounded-md bg-grayScale-100 px-2 py-1 text-grayScale-600">
                            Difficulty: {question.difficulty_level || "—"}
                          </span>
                          <span className="rounded-md bg-grayScale-100 px-2 py-1 text-grayScale-600">
                            Points: {question.points ?? 0}
                          </span>
                          <span className="rounded-md bg-grayScale-100 px-2 py-1 text-grayScale-600">
                            Status: {question.status || "—"}
                          </span>
                        </div>
                        {question.image_url && imagePreviewByQuestionId[question.id] ? (
                          <div className="mt-3">
                            <p className="mb-2 text-xs font-medium text-grayScale-500">Image preview</p>
                            <img
                              src={imagePreviewByQuestionId[question.id]}
                              alt="Question visual"
                              className="h-20 w-32 rounded-md border border-grayScale-200 object-cover"
                            />
                          </div>
                        ) : null}
                        {question.voice_prompt ? (
                          <div className="mt-3 space-y-2">
                            <p className="text-xs font-medium text-grayScale-500">Voice prompt preview</p>
                            {audioPreviewByQuestionId[question.id] ? (
                              <audio controls src={audioPreviewByQuestionId[question.id]} className="h-10 w-full max-w-sm" />
                            ) : (
                              <p className="text-xs text-grayScale-400">Unable to resolve audio URL.</p>
                            )}
                          </div>
                        ) : null}
                        {question.audio_correct_answer_text ? (
                          <p className="mt-2 text-xs text-grayScale-500">
                            <span className="font-medium text-grayScale-600">Correct answer text:</span>{" "}
                            {question.audio_correct_answer_text}
                          </p>
                        ) : null}
                      </div>
                    ))}
                  </div>
                ))}
                {audioTotalCount > audioPageSize ? (
                  <div className="mt-4 flex items-center justify-between rounded-xl border border-grayScale-200 bg-white px-3 py-2">
                    <p className="text-xs text-grayScale-500 sm:text-sm">
                      Page {audioPage} of {Math.max(1, Math.ceil(audioTotalCount / audioPageSize))}
                    </p>
                    <div className="flex items-center gap-2">
                      <Button
                        type="button"
                        variant="outline"
                        size="sm"
                        disabled={audioPage <= 1 || loading}
                        onClick={() => fetchAudioQuestions(audioPage - 1)}
                      >
                        Previous
                      </Button>
                      <Button
                        type="button"
                        variant="outline"
                        size="sm"
                        disabled={audioPage >= Math.ceil(audioTotalCount / audioPageSize) || loading}
                        onClick={() => fetchAudioQuestions(audioPage + 1)}
                      >
                        Next
                      </Button>
                    </div>
                  </div>
                ) : null}
              </div>
            )}
          </CardContent>
        </Card>
      )}

      {detailOpen && (
        <div
          className="fixed inset-0 z-50 flex items-center justify-center bg-black/45 p-4 backdrop-blur-sm"
          onClick={() => setDetailOpen(false)}
        >
          <div
            className="flex max-h-[90vh] w-full max-w-3xl flex-col overflow-hidden rounded-2xl border border-grayScale-200/80 bg-white shadow-2xl"
            onClick={(e) => e.stopPropagation()}
          >
            <div className="flex shrink-0 items-center justify-between border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/90 to-white px-5 py-4 sm:px-6">
              <h3 className="text-base font-semibold text-grayScale-900 sm:text-lg">AUDIO question</h3>
              <div className="flex items-center gap-2">
                {!detailLoading && selectedQuestionDetail && !detailEditing ? (
                  <Button variant="outline" size="sm" onClick={() => setDetailEditing(true)}>
                    Edit
                  </Button>
                ) : null}
                {!detailLoading && selectedQuestionDetail ? (
                  <Button
                    variant="outline"
                    size="sm"
                    className="border-red-200 text-red-600 hover:bg-red-50 hover:text-red-700"
                    onClick={() => {
                      setDeleteTarget({
                        id: selectedQuestionDetail.id,
                        text: selectedQuestionDetail.question_text,
                      })
                      setConfirmDeleteOpen(true)
                    }}
                  >
                    Delete
                  </Button>
                ) : null}
                <Button variant="outline" size="sm" onClick={() => setDetailOpen(false)}>
                  Close
                </Button>
              </div>
            </div>
            <div className="min-h-0 flex-1 space-y-4 overflow-y-auto px-5 py-5 sm:px-6">
              {detailLoading ? (
                <div className="flex items-center justify-center py-10">
                  <SpinnerIcon className="h-5 w-5" />
                </div>
              ) : selectedQuestionDetail && detailEditing ? (
                <>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Question Text</label>
                    <Textarea
                      value={detailForm.question_text}
                      onChange={(e) =>
                        setDetailForm((prev) => ({ ...prev, question_text: e.target.value }))
                      }
                      rows={2}
                    />
                  </div>
                  <div className="grid grid-cols-2 gap-3">
                    <div className="space-y-1.5">
                      <label className="text-xs font-medium text-grayScale-500">Difficulty</label>
                      <select
                        value={detailForm.difficulty_level}
                        onChange={(e) =>
                          setDetailForm((prev) => ({
                            ...prev,
                            difficulty_level: e.target.value as "EASY" | "MEDIUM" | "HARD",
                          }))
                        }
                        className="h-10 w-full rounded-md border border-grayScale-200 px-3 text-sm"
                      >
                        <option value="EASY">EASY</option>
                        <option value="MEDIUM">MEDIUM</option>
                        <option value="HARD">HARD</option>
                      </select>
                    </div>
                    <div className="space-y-1.5">
                      <label className="text-xs font-medium text-grayScale-500">Points</label>
                      <Input
                        type="number"
                        min={1}
                        value={detailForm.points}
                        onChange={(e) =>
                          setDetailForm((prev) => ({ ...prev, points: Number(e.target.value) || 1 }))
                        }
                      />
                    </div>
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Voice Prompt URL</label>
                    <Input
                      value={detailForm.voice_prompt}
                      onChange={(e) =>
                        setDetailForm((prev) => ({ ...prev, voice_prompt: e.target.value }))
                      }
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Sample Answer Voice Prompt URL</label>
                    <Input
                      value={detailForm.sample_answer_voice_prompt}
                      onChange={(e) =>
                        setDetailForm((prev) => ({
                          ...prev,
                          sample_answer_voice_prompt: e.target.value,
                        }))
                      }
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Image URL</label>
                    <Input
                      value={detailForm.image_url}
                      onChange={(e) =>
                        setDetailForm((prev) => ({ ...prev, image_url: e.target.value }))
                      }
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Audio Correct Answer Text</label>
                    <Textarea
                      value={detailForm.audio_correct_answer_text}
                      onChange={(e) =>
                        setDetailForm((prev) => ({
                          ...prev,
                          audio_correct_answer_text: e.target.value,
                        }))
                      }
                      rows={2}
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Explanation</label>
                    <Textarea
                      value={detailForm.explanation}
                      onChange={(e) =>
                        setDetailForm((prev) => ({ ...prev, explanation: e.target.value }))
                      }
                      rows={2}
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Tips</label>
                    <Textarea
                      value={detailForm.tips}
                      onChange={(e) =>
                        setDetailForm((prev) => ({ ...prev, tips: e.target.value }))
                      }
                      rows={2}
                    />
                  </div>
                  <div className="space-y-1.5">
                    <label className="text-xs font-medium text-grayScale-500">Status</label>
                    <select
                      value={detailForm.status}
                      onChange={(e) =>
                        setDetailForm((prev) => ({
                          ...prev,
                          status: e.target.value as "DRAFT" | "PUBLISHED" | "INACTIVE",
                        }))
                      }
                      className="h-10 w-full rounded-md border border-grayScale-200 px-3 text-sm"
                    >
                      <option value="DRAFT">DRAFT</option>
                      <option value="PUBLISHED">PUBLISHED</option>
                      <option value="INACTIVE">INACTIVE</option>
                    </select>
                  </div>
                  <div className="flex justify-end gap-2 border-t border-grayScale-100 pt-3">
                    <Button
                      variant="outline"
                      onClick={() => setDetailEditing(false)}
                      disabled={detailSaving}
                    >
                      Cancel
                    </Button>
                    <Button
                      className="bg-brand-500 hover:bg-brand-600"
                      onClick={handleUpdateQuestionDetail}
                      disabled={detailSaving || !detailForm.question_text.trim()}
                    >
                      {detailSaving ? "Saving..." : "Save Changes"}
                    </Button>
                  </div>
                </>
              ) : selectedQuestionDetail ? (
                <>
                  <div>
                    <p className="text-xs font-medium text-grayScale-500">Question Text</p>
                    <p className="mt-1 text-sm text-grayScale-700">{selectedQuestionDetail.question_text}</p>
                  </div>
                  <div className="grid grid-cols-2 gap-3 text-sm">
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Status</p>
                      <p className="mt-1 text-grayScale-700">{selectedQuestionDetail.status || "—"}</p>
                    </div>
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Points</p>
                      <p className="mt-1 text-grayScale-700">{selectedQuestionDetail.points ?? "—"}</p>
                    </div>
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Difficulty</p>
                      <p className="mt-1 text-grayScale-700">{selectedQuestionDetail.difficulty_level || "—"}</p>
                    </div>
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Type</p>
                      <p className="mt-1 text-grayScale-700">{selectedQuestionDetail.question_type || "—"}</p>
                    </div>
                  </div>
                  {selectedQuestionDetail.audio_correct_answer_text ? (
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Audio Correct Answer Text</p>
                      <p className="mt-1 text-sm text-grayScale-700">
                        {selectedQuestionDetail.audio_correct_answer_text}
                      </p>
                    </div>
                  ) : null}
                  {selectedQuestionDetail.explanation ? (
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Explanation</p>
                      <p className="mt-1 text-sm text-grayScale-700">{selectedQuestionDetail.explanation}</p>
                    </div>
                  ) : null}
                  {selectedQuestionDetail.tips ? (
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Tips</p>
                      <p className="mt-1 text-sm text-grayScale-700">{selectedQuestionDetail.tips}</p>
                    </div>
                  ) : null}
                  {detailVoiceUrl ? (
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Voice Prompt</p>
                      <audio controls src={detailVoiceUrl} className="mt-1 h-10 w-full" />
                    </div>
                  ) : null}
                  {detailSampleVoiceUrl ? (
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Sample Answer Voice Prompt</p>
                      <audio controls src={detailSampleVoiceUrl} className="mt-1 h-10 w-full" />
                    </div>
                  ) : null}
                  {detailImageUrl ? (
                    <div>
                      <p className="text-xs font-medium text-grayScale-500">Image</p>
                      <img
                        src={detailImageUrl}
                        alt="Question reference"
                        className="mt-1 h-32 w-32 rounded-md border border-grayScale-200 object-cover"
                      />
                    </div>
                  ) : null}
                </>
              ) : (
                <p className="py-6 text-sm text-grayScale-500">Question details unavailable.</p>
              )}
            </div>
          </div>
        </div>
      )}

      {confirmDeleteOpen && (deleteTarget || selectedQuestionDetail) && (
        <div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/45 p-4 backdrop-blur-sm">
          <div className="w-full max-w-md overflow-hidden rounded-2xl border border-grayScale-200/80 bg-white shadow-2xl">
            <div className="border-b border-grayScale-100 bg-gradient-to-r from-red-50/50 to-white px-5 py-4">
              <h3 className="text-base font-semibold text-grayScale-900">Delete AUDIO question</h3>
            </div>
            <div className="space-y-2 px-5 py-4">
              <p className="text-sm leading-relaxed text-grayScale-600">
                This action cannot be undone. The question will be removed permanently.
              </p>
              <p className="line-clamp-3 rounded-lg bg-grayScale-50 px-3 py-2 text-xs text-grayScale-600">
                {deleteTarget?.text ?? selectedQuestionDetail?.question_text}
              </p>
            </div>
            <div className="flex justify-end gap-2 border-t border-grayScale-100 bg-grayScale-50/30 px-5 py-4">
              <Button
                variant="outline"
                onClick={() => {
                  setConfirmDeleteOpen(false)
                  setDeleteTarget(null)
                }}
                disabled={detailDeleting}
              >
                Cancel
              </Button>
              <Button
                className="bg-red-500 hover:bg-red-600"
                onClick={handleDeleteQuestionDetail}
                disabled={detailDeleting}
              >
                {detailDeleting ? "Deleting..." : "Delete"}
              </Button>
            </div>
          </div>
        </div>
      )}

      {openCreate && (
        <div className="space-y-6">
          <Card className="overflow-hidden border-grayScale-200/80 shadow-sm">
            <div className="flex flex-col gap-4 border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/80 to-white px-4 py-4 sm:flex-row sm:items-center sm:justify-between sm:px-6 sm:py-4">
              <Button
                type="button"
                variant="outline"
                className="h-10 w-full shrink-0 border-grayScale-200 text-grayScale-700 sm:w-auto"
                onClick={() => {
                  resetCreateForm()
                  setOpenCreate(false)
                }}
                disabled={saving}
              >
                <ArrowLeft className="h-4 w-4" />
                Back to list
              </Button>
              <p className="text-center text-xs text-grayScale-500 sm:text-left sm:text-sm">
                New speaking practice · {SPEAKING_STEPS[currentStep - 1] ?? ""}
              </p>
            </div>
            <div className="px-4 py-5 sm:px-6 sm:py-6">
              <div className="rounded-2xl border border-grayScale-100 bg-grayScale-50/40 px-3 py-4 sm:px-5">
                <Stepper steps={SPEAKING_STEPS} currentStep={currentStep} />
              </div>
            </div>
          </Card>

          {currentStep === 1 && (
            <Card className="w-full overflow-hidden border-grayScale-200/80 shadow-sm">
              <div className="border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/80 to-white px-5 py-5 sm:px-8 sm:py-6">
                <h2 className="text-lg font-semibold tracking-tight text-grayScale-900 sm:text-xl">Step 1: Practice context</h2>
                <p className="mt-1.5 max-w-3xl text-sm text-grayScale-500">
                  Title, description, optional intro video, sub-course, and publish status for this speaking practice.
                </p>
              </div>
              <div className="p-5 sm:p-8 lg:p-10">
                <div className="grid grid-cols-1 gap-8 lg:grid-cols-12 lg:gap-10">
                  <div className="space-y-5 lg:col-span-7">
                    <div className="space-y-1.5">
                      <label className="text-sm font-medium text-grayScale-700">Practice title</label>
                      <Input
                        value={setTitle}
                        onChange={(e) => setSetTitle(e.target.value)}
                        placeholder="Speaking practice title"
                        className="h-11"
                      />
                    </div>
                    <div className="space-y-1.5">
                      <label className="text-sm font-medium text-grayScale-700">Description (optional)</label>
                      <Textarea
                        value={setDescription}
                        onChange={(e) => setSetDescription(e.target.value)}
                        rows={3}
                        placeholder="Brief description"
                        className="min-h-[88px]"
                      />
                    </div>
                    <div className="space-y-2">
                      <label className="text-sm font-medium text-grayScale-700">
                        Intro video URL <span className="font-normal text-grayScale-400">(optional)</span>
                      </label>
                      <Input
                        value={introVideoUrl}
                        onChange={(e) => setIntroVideoUrl(e.target.value)}
                        placeholder="https://…"
                        type="url"
                        inputMode="url"
                        autoComplete="off"
                        className="h-11 font-mono text-[13px]"
                      />
                      <input
                        ref={introVideoFileInputRef}
                        type="file"
                        accept="video/*"
                        className="hidden"
                        onChange={handleIntroVideoFileChange}
                        disabled={uploadingIntroVideo}
                      />
                      <div className="flex flex-wrap items-center gap-2">
                        <Button
                          type="button"
                          variant="outline"
                          size="sm"
                          disabled={uploadingIntroVideo}
                          onClick={() => introVideoFileInputRef.current?.click()}
                          className="gap-1.5"
                        >
                          {uploadingIntroVideo ? (
                            <Loader2 className="h-4 w-4 animate-spin" />
                          ) : (
                            <Upload className="h-4 w-4" />
                          )}
                          {uploadingIntroVideo ? "Uploading…" : "Upload video from computer"}
                        </Button>
                        {introVideoUrl.trim() ? (
                          <Button type="button" variant="ghost" size="sm" onClick={() => setIntroVideoUrl("")}>
                            Clear URL
                          </Button>
                        ) : null}
                      </div>
                      <p className="text-xs leading-relaxed text-grayScale-500">
                        Paste a link or upload from your computer; uploads use the same file service as elsewhere. Optional, not tied to sub-course video rows.
                      </p>
                      {introVideoPreview ? (
                        <div className="rounded-xl border border-grayScale-200 bg-black/95 p-2">
                          <p className="mb-2 text-xs font-medium uppercase tracking-wide text-grayScale-300">Preview</p>
                          {introVideoPreview.kind === "iframe" ? (
                            <iframe
                              src={introVideoPreview.src}
                              title="Intro video preview"
                              className="aspect-video w-full rounded-md"
                              allow="autoplay; fullscreen; picture-in-picture"
                              allowFullScreen
                            />
                          ) : (
                            <video src={introVideoPreview.src} controls className="aspect-video w-full rounded-md bg-black" />
                          )}
                        </div>
                      ) : null}
                    </div>
                  </div>
                  <aside className="space-y-4 lg:col-span-5">
                    <div className="rounded-xl border border-grayScale-200 bg-grayScale-50/40 p-5 shadow-sm ring-1 ring-grayScale-100/80">
                      <h3 className="text-xs font-semibold uppercase tracking-wider text-grayScale-500">Sub-course & status</h3>
                      <p className="mt-1 text-xs text-grayScale-400">Choose where this practice is attached.</p>
                      <div className="mt-4 space-y-4">
                <div className="space-y-1.5">
                  <label className="text-sm font-medium text-grayScale-700">Sub-course</label>
                  <DropdownMenu open={subCourseMenuOpen} onOpenChange={setSubCourseMenuOpen}>
                    <DropdownMenuTrigger asChild>
                      <Button
                        type="button"
                        variant="outline"
                        className="h-11 w-full justify-between rounded-xl border-grayScale-200 bg-white px-3 text-sm font-normal text-grayScale-600 hover:bg-grayScale-50"
                      >
                        <span className="truncate">
                          {selectedSubCourseOption
                            ? `${selectedSubCourseOption.title} (#${selectedSubCourseOption.id})`
                            : subCourseLoading
                              ? "Loading courses..."
                              : "Select course"}
                        </span>
                        <ChevronDown className="h-4 w-4 text-grayScale-400" />
                      </Button>
                    </DropdownMenuTrigger>
                    <DropdownMenuContent align="start" className="w-[420px] max-w-[90vw] p-2">
                      <Input
                        value={subCourseSearch}
                        onChange={(e) => setSubCourseSearch(e.target.value)}
                        placeholder="Search course, course, category..."
                        className="mb-2 h-9"
                      />
                      <div className="max-h-64 overflow-auto">
                        {filteredSubCourseOptions.length === 0 ? (
                          <p className="px-2 py-2 text-sm text-grayScale-400">No courses found</p>
                        ) : (
                          <DropdownMenuRadioGroup
                            value={subCourseId}
                            onValueChange={(value) => {
                              setSubCourseId(value)
                              setSubCourseMenuOpen(false)
                            }}
                          >
                            {filteredSubCourseOptions.map((option) => (
                              <DropdownMenuRadioItem key={option.id} value={String(option.id)}>
                                <div className="flex min-w-0 flex-col">
                                  <span className="truncate">{option.title}</span>
                                  <span className="text-xs text-grayScale-400">
                                    {option.categoryName} / {option.courseTitle} / ID: {option.id}
                                  </span>
                                </div>
                              </DropdownMenuRadioItem>
                            ))}
                          </DropdownMenuRadioGroup>
                        )}
                      </div>
                    </DropdownMenuContent>
                  </DropdownMenu>
                </div>
                <div className="space-y-1.5">
                  <label className="text-sm font-medium text-grayScale-700">Status</label>
                  <DropdownMenu>
                    <DropdownMenuTrigger asChild>
                      <Button
                        type="button"
                        variant="outline"
                        className="h-11 w-full justify-between rounded-xl border-grayScale-200 bg-white px-3 text-sm font-normal text-grayScale-600 hover:bg-grayScale-50"
                      >
                        <span>{setStatus}</span>
                        <ChevronDown className="h-4 w-4 text-grayScale-400" />
                      </Button>
                    </DropdownMenuTrigger>
                    <DropdownMenuContent align="start" className="w-[220px]">
                      <DropdownMenuRadioGroup
                        value={setStatus}
                        onValueChange={(value) => setSetStatus(value as "DRAFT" | "PUBLISHED")}
                      >
                        <DropdownMenuRadioItem value="PUBLISHED">PUBLISHED</DropdownMenuRadioItem>
                        <DropdownMenuRadioItem value="DRAFT">DRAFT</DropdownMenuRadioItem>
                      </DropdownMenuRadioGroup>
                    </DropdownMenuContent>
                  </DropdownMenu>
                </div>
                      </div>
                    </div>
                  </aside>
                </div>
                <div className="mt-8 flex flex-col-reverse gap-3 border-t border-grayScale-100 bg-grayScale-50/30 px-0 py-4 sm:flex-row sm:justify-end sm:px-0 sm:py-5">
                <Button
                  variant="outline"
                  onClick={() => {
                    resetCreateForm()
                    setOpenCreate(false)
                  }}
                  disabled={saving}
                  className="sm:w-auto"
                >
                  Cancel
                </Button>
                <Button
                  className="bg-brand-500 hover:bg-brand-600 sm:min-w-[180px]"
                  onClick={handleProceedToQuestions}
                  disabled={!canProceedToQuestions || creatingSet}
                >
                  {creatingSet ? "Creating..." : "Next: Questions"}
                </Button>
              </div>
              </div>
            </Card>
          )}

          {currentStep === 2 && (
            <Card className="w-full overflow-hidden border-grayScale-200/80 shadow-sm">
              <div className="border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/80 to-white px-5 py-5 sm:px-8 sm:py-6">
                <div>
                  <h2 className="text-lg font-semibold tracking-tight text-grayScale-900 sm:text-xl">Step 2: AUDIO questions</h2>
                  <p className="mt-1 max-w-3xl text-sm text-grayScale-500">
                    Build each question with voice prompts, optional media, and answer guidance.
                  </p>
                </div>
              </div>
              <div className="p-5 sm:p-8">
              <div className="rounded-xl border border-grayScale-200 bg-grayScale-50/30 p-4 sm:p-5">
                <div className="space-y-5">
                  {questionDrafts.map((draft, draftIndex) => (
                    <div key={draftIndex} className="rounded-2xl border border-grayScale-200 border-l-4 border-l-brand-500 bg-white p-4 shadow-sm transition-shadow hover:shadow-md sm:p-6">
                      <div className="mb-4 flex items-center justify-between gap-2">
                        <div className="flex items-center gap-2">
                          <span className="rounded-md bg-brand-100 px-2 py-1 text-xs font-semibold tracking-wide text-brand-700">
                            AUDIO
                          </span>
                          <p className="text-sm font-semibold text-grayScale-900">Question {draftIndex + 1}</p>
                        </div>
                        {questionDrafts.length > 1 ? (
                          <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            className="text-red-600 hover:text-red-700"
                            onClick={() =>
                              setQuestionDrafts((prev) => prev.filter((_, idx) => idx !== draftIndex))
                            }
                            disabled={saving}
                          >
                            Remove
                          </Button>
                        ) : null}
                      </div>

                      <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
                        <div className="space-y-1.5 sm:col-span-2">
                          <label className="text-sm font-medium text-grayScale-600">Question Text</label>
                          <Textarea
                            value={draft.questionText}
                            onChange={(e) =>
                              updateDraft(draftIndex, (prev) => ({ ...prev, questionText: e.target.value }))
                            }
                            rows={2}
                          />
                        </div>
                        <div className="space-y-1.5">
                          <label className="text-sm font-medium text-grayScale-600">Difficulty</label>
                          <DropdownMenu>
                            <DropdownMenuTrigger asChild>
                              <Button
                                type="button"
                                variant="outline"
                                className="h-11 w-full justify-between rounded-xl border-grayScale-200 bg-white px-3 text-sm font-normal text-grayScale-600 hover:bg-grayScale-50"
                              >
                                <span>{draft.difficulty}</span>
                                <ChevronDown className="h-4 w-4 text-grayScale-400" />
                              </Button>
                            </DropdownMenuTrigger>
                            <DropdownMenuContent align="start" className="w-[220px]">
                              <DropdownMenuRadioGroup
                                value={draft.difficulty}
                                onValueChange={(value) =>
                                  updateDraft(draftIndex, (prev) => ({
                                    ...prev,
                                    difficulty: value as "EASY" | "MEDIUM" | "HARD",
                                  }))
                                }
                              >
                                <DropdownMenuRadioItem value="EASY">EASY</DropdownMenuRadioItem>
                                <DropdownMenuRadioItem value="MEDIUM">MEDIUM</DropdownMenuRadioItem>
                                <DropdownMenuRadioItem value="HARD">HARD</DropdownMenuRadioItem>
                              </DropdownMenuRadioGroup>
                            </DropdownMenuContent>
                          </DropdownMenu>
                        </div>
                        <div className="space-y-1.5">
                          <label className="text-sm font-medium text-grayScale-600">Points</label>
                          <Input
                            type="number"
                            min={1}
                            value={draft.points}
                            onChange={(e) =>
                              updateDraft(draftIndex, (prev) => ({
                                ...prev,
                                points: Number(e.target.value) || 1,
                              }))
                            }
                          />
                        </div>
                        <div className="space-y-1.5 sm:col-span-2">
                          <label className="text-sm font-medium text-grayScale-600">Voice Prompt (optional)</label>
                          <div className="flex flex-col gap-2">
                            <div className="flex flex-wrap items-center gap-2">
                              <label className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-grayScale-200 px-3 py-2 text-sm text-grayScale-600 hover:bg-grayScale-50">
                                {draft.uploadingVoicePrompt ? (
                                  <SpinnerIcon className="h-4 w-4" />
                                ) : (
                                  <Upload className="h-4 w-4" />
                                )}
                                Upload Voice Prompt
                                <input
                                  type="file"
                                  accept=".mp3,.wav,.ogg,.m4a,.aac,.webm,.flac,audio/*"
                                  className="hidden"
                                  onChange={(event) => handleAudioUpload(event, "voice_prompt", draftIndex)}
                                  disabled={draft.uploadingVoicePrompt || saving}
                                />
                              </label>
                              <Button
                                type="button"
                                variant="outline"
                                className="h-9 px-3 text-xs"
                                onClick={() => startRecording("voice_prompt", draftIndex)}
                                disabled={draft.uploadingVoicePrompt || saving || Boolean(recordingModal)}
                              >
                                {draft.recordingVoicePrompt ? "Recording..." : "Record Voice Prompt"}
                              </Button>
                              <span className="text-xs text-grayScale-400">Max 50MB</span>
                            </div>
                            <Input
                              value={draft.voicePrompt}
                              onChange={(e) =>
                                updateDraft(draftIndex, (prev) => ({ ...prev, voicePrompt: e.target.value }))
                              }
                              onBlur={() => handleResolveManualAudioPreview("voice_prompt", draftIndex)}
                              placeholder="Audio object_key (or URL)"
                            />
                            {draft.voicePromptPreviewUrl ? (
                              <audio controls src={draft.voicePromptPreviewUrl} className="h-10 w-full max-w-md" />
                            ) : null}
                          </div>
                        </div>
                        <div className="space-y-1.5 sm:col-span-2">
                          <label className="text-sm font-medium text-grayScale-600">Sample Answer Voice (optional)</label>
                          <div className="flex flex-col gap-2">
                            <div className="flex flex-wrap items-center gap-2">
                              <label className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-grayScale-200 px-3 py-2 text-sm text-grayScale-600 hover:bg-grayScale-50">
                                {draft.uploadingSamplePrompt ? (
                                  <SpinnerIcon className="h-4 w-4" />
                                ) : (
                                  <Upload className="h-4 w-4" />
                                )}
                                Upload Sample Answer
                                <input
                                  type="file"
                                  accept=".mp3,.wav,.ogg,.m4a,.aac,.webm,.flac,audio/*"
                                  className="hidden"
                                  onChange={(event) =>
                                    handleAudioUpload(event, "sample_answer_voice_prompt", draftIndex)
                                  }
                                  disabled={draft.uploadingSamplePrompt || saving}
                                />
                              </label>
                              <Button
                                type="button"
                                variant="outline"
                                className="h-9 px-3 text-xs"
                                onClick={() => startRecording("sample_answer_voice_prompt", draftIndex)}
                                disabled={draft.uploadingSamplePrompt || saving || Boolean(recordingModal)}
                              >
                                {draft.recordingSamplePrompt ? "Recording..." : "Record Sample Answer"}
                              </Button>
                              <span className="text-xs text-grayScale-400">Max 50MB</span>
                            </div>
                            <Input
                              value={draft.sampleAnswerVoicePrompt}
                              onChange={(e) =>
                                updateDraft(draftIndex, (prev) => ({
                                  ...prev,
                                  sampleAnswerVoicePrompt: e.target.value,
                                }))
                              }
                              onBlur={() =>
                                handleResolveManualAudioPreview("sample_answer_voice_prompt", draftIndex)
                              }
                              placeholder="Audio object_key (or URL)"
                            />
                            {draft.samplePromptPreviewUrl ? (
                              <audio controls src={draft.samplePromptPreviewUrl} className="h-10 w-full max-w-md" />
                            ) : null}
                          </div>
                        </div>
                        <div className="space-y-1.5 sm:col-span-2">
                          <label className="text-sm font-medium text-grayScale-600">Correct Answer Text (optional)</label>
                          <Textarea
                            value={draft.audioCorrectAnswerText}
                            onChange={(e) =>
                              updateDraft(draftIndex, (prev) => ({
                                ...prev,
                                audioCorrectAnswerText: e.target.value,
                              }))
                            }
                            rows={2}
                          />
                        </div>
                        <div className="space-y-1.5 sm:col-span-2">
                          <label className="text-sm font-medium text-grayScale-600">Image URL (optional)</label>
                          <div className="flex flex-col gap-2">
                            <div className="flex flex-wrap items-center gap-2">
                              <label className="inline-flex cursor-pointer items-center gap-2 rounded-md border border-grayScale-200 px-3 py-2 text-sm text-grayScale-600 hover:bg-grayScale-50">
                                {draft.uploadingImage ? (
                                  <SpinnerIcon className="h-4 w-4" />
                                ) : (
                                  <ImageIcon className="h-4 w-4" />
                                )}
                                Upload Image
                                <input
                                  type="file"
                                  accept=".jpg,.jpeg,.png,.webp,.gif,image/*"
                                  className="hidden"
                                  onChange={(event) => handleImageUpload(event, draftIndex)}
                                  disabled={draft.uploadingImage || saving}
                                />
                              </label>
                              <span className="text-xs text-grayScale-400">Max 10MB</span>
                            </div>
                            <Input
                              value={draft.imageUrl}
                              onChange={(e) =>
                                updateDraft(draftIndex, (prev) => ({ ...prev, imageUrl: e.target.value }))
                              }
                              onBlur={() => handleResolveManualImagePreview(draftIndex)}
                              placeholder="Image URL (https://...)"
                            />
                            {draft.imagePreviewUrl ? (
                              <img
                                src={draft.imagePreviewUrl}
                                alt="Uploaded preview"
                                className="h-28 w-28 rounded-md border border-grayScale-200 object-cover"
                              />
                            ) : null}
                          </div>
                        </div>
                        <div className="space-y-1.5 sm:col-span-2">
                          <label className="text-sm font-medium text-grayScale-600">Tips (optional)</label>
                          <Textarea
                            value={draft.tips}
                            onChange={(e) =>
                              updateDraft(draftIndex, (prev) => ({ ...prev, tips: e.target.value }))
                            }
                            rows={2}
                          />
                        </div>
                        <div className="space-y-1.5 sm:col-span-2">
                          <label className="text-sm font-medium text-grayScale-600">Explanation (optional)</label>
                          <Textarea
                            value={draft.explanation}
                            onChange={(e) =>
                              updateDraft(draftIndex, (prev) => ({ ...prev, explanation: e.target.value }))
                            }
                            rows={2}
                          />
                        </div>
                      </div>
                    </div>
                  ))}
                </div>
              </div>
              </div>
              <div className="border-t border-grayScale-100 bg-white px-5 py-4 sm:px-8">
                <div className="flex flex-col gap-3 rounded-xl border border-dashed border-grayScale-200 bg-grayScale-50/40 p-3 sm:flex-row sm:items-center sm:justify-between sm:p-4">
                  <p className="text-xs text-grayScale-500 sm:text-sm">
                    Need another item? Add a new AUDIO question to the end of the list.
                  </p>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    className="shrink-0 border-brand-200 text-brand-700 hover:bg-brand-50 hover:text-brand-800"
                    onClick={() => setQuestionDrafts((prev) => [...prev, createEmptyDraft()])}
                    disabled={saving}
                  >
                    <Plus className="h-4 w-4" />
                    Add question
                  </Button>
                </div>
              </div>
              <div className="flex flex-col-reverse gap-3 border-t border-grayScale-100 bg-grayScale-50/30 px-5 py-4 sm:flex-row sm:justify-end sm:px-8 sm:py-5">
                <Button variant="outline" onClick={() => setCurrentStep(1)} disabled={saving} className="sm:w-auto">
                  Back
                </Button>
                <Button
                  className="bg-brand-500 hover:bg-brand-600 sm:min-w-[180px]"
                  onClick={() => setCurrentStep(3)}
                  disabled={!canProceedToReview}
                >
                  Next: Review
                </Button>
              </div>
            </Card>
          )}

          {currentStep === 3 && (
            <Card className="w-full overflow-hidden border-grayScale-200/80 shadow-sm">
              <div className="border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/80 to-white px-5 py-5 sm:px-8 sm:py-6">
                <h2 className="text-lg font-semibold tracking-tight text-grayScale-900 sm:text-xl">Step 3: Review & publish</h2>
                <p className="mt-1.5 max-w-3xl text-sm text-grayScale-500">
                  Confirm practice metadata and each AUDIO question before publishing.
                </p>
              </div>
              <div className="space-y-6 p-5 sm:p-8">
                <div className="grid gap-6 lg:grid-cols-2 lg:items-start lg:gap-8">
                <div className="rounded-xl border border-grayScale-200 bg-white p-5 shadow-sm">
                  <h3 className="mb-3 text-sm font-semibold uppercase tracking-wide text-grayScale-500">Practice</h3>
                  <div className="grid grid-cols-1 gap-3 text-sm text-grayScale-600 sm:grid-cols-2">
                    <p><span className="font-medium">Title:</span> {setTitle || "—"}</p>
                    <p><span className="font-medium">Course ID:</span> {subCourseId || "—"}</p>
                    <p className="sm:col-span-2"><span className="font-medium">Description:</span> {setDescription || "—"}</p>
                    <p className="sm:col-span-2 break-all">
                      <span className="font-medium">Intro video URL:</span> {introVideoUrl.trim() || "—"}
                    </p>
                    <p><span className="font-medium">Status:</span> {setStatus}</p>
                  </div>
                </div>

                <div className="flex max-h-[min(70vh,48rem)] min-h-0 flex-col rounded-xl border border-grayScale-200 bg-grayScale-50/20 p-4 shadow-sm sm:p-5">
                  <h3 className="mb-3 shrink-0 text-sm font-semibold uppercase tracking-wide text-grayScale-500">
                    Questions ({questionsWithText.length})
                  </h3>
                  <div className="min-h-0 flex-1 space-y-3 overflow-y-auto pr-1">
                    {questionsWithText.map((draft, idx) => (
                      <div key={idx} className="rounded-lg border border-grayScale-200 bg-white p-3 text-sm shadow-sm">
                        <p className="font-medium text-grayScale-700">
                          {idx + 1}. {draft.questionText}
                        </p>
                        <div className="mt-2 grid grid-cols-1 gap-2 text-xs text-grayScale-600 sm:grid-cols-2">
                          <p><span className="font-medium text-grayScale-700">Question Type:</span> AUDIO</p>
                          <p><span className="font-medium text-grayScale-700">Difficulty:</span> {draft.difficulty}</p>
                          <p><span className="font-medium text-grayScale-700">Points:</span> {draft.points}</p>
                          <p><span className="font-medium text-grayScale-700">Status:</span> {setStatus}</p>
                          <p className="sm:col-span-2">
                            <span className="font-medium text-grayScale-700">Voice Prompt:</span>{" "}
                            {draft.voicePrompt.trim() || "—"}
                          </p>
                          <p className="sm:col-span-2">
                            <span className="font-medium text-grayScale-700">Sample Answer Voice Prompt:</span>{" "}
                            {draft.sampleAnswerVoicePrompt.trim() || "—"}
                          </p>
                          <p className="sm:col-span-2">
                            <span className="font-medium text-grayScale-700">Image URL:</span>{" "}
                            {draft.imageUrl.trim() || "—"}
                          </p>
                          <p className="sm:col-span-2">
                            <span className="font-medium text-grayScale-700">Audio Correct Answer Text:</span>{" "}
                            {draft.audioCorrectAnswerText.trim() || "—"}
                          </p>
                          <p className="sm:col-span-2">
                            <span className="font-medium text-grayScale-700">Tips:</span>{" "}
                            {draft.tips.trim() || "—"}
                          </p>
                          <p className="sm:col-span-2">
                            <span className="font-medium text-grayScale-700">Explanation:</span>{" "}
                            {draft.explanation.trim() || "—"}
                          </p>
                        </div>
                        <div className="mt-2 flex flex-wrap gap-3">
                          {draft.voicePromptPreviewUrl ? (
                            <audio controls src={draft.voicePromptPreviewUrl} className="h-9 w-full max-w-sm" />
                          ) : null}
                          {draft.samplePromptPreviewUrl ? (
                            <audio controls src={draft.samplePromptPreviewUrl} className="h-9 w-full max-w-sm" />
                          ) : null}
                          {draft.imagePreviewUrl ? (
                            <img
                              src={draft.imagePreviewUrl}
                              alt="Question preview"
                              className="h-20 w-20 rounded-md border border-grayScale-200 object-cover"
                            />
                          ) : null}
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
                </div>
              </div>
              <div className="flex flex-col-reverse gap-3 border-t border-grayScale-100 bg-grayScale-50/30 px-5 py-4 sm:flex-row sm:justify-end sm:px-8 sm:py-5">
              <Button variant="outline" onClick={() => setCurrentStep(2)} disabled={saving} className="sm:w-auto">
                Back
              </Button>
              <Button className="bg-brand-500 hover:bg-brand-600 sm:min-w-[200px]" disabled={!canCreate || saving} onClick={handleCreateSpeakingPractice}>
                {saving ? "Publishing..." : "Publish speaking practice"}
              </Button>
            </div>
            </Card>
          )}
        </div>
      )}

      {recordingModal ? (
        <div className="fixed inset-0 z-[70] flex items-center justify-center bg-black/40 backdrop-blur-sm">
          <div className="mx-4 w-full max-w-md overflow-hidden rounded-2xl border border-grayScale-200/80 bg-white p-6 shadow-2xl">
            <p className="text-center text-base font-semibold text-grayScale-900">
              Recording {recordingModal.label}
            </p>
            <p className="mt-1 text-center text-xs text-grayScale-500">
              Speak clearly. The bars reflect your input level in real time.
            </p>
            <div className="mt-3 flex justify-center">
              <div className="inline-flex items-center gap-2 rounded-full border border-brand-200 bg-brand-50 px-3 py-1 text-xs font-semibold text-brand-800">
                <span className="h-2 w-2 animate-pulse rounded-full bg-brand-500" />
                REC{" "}
                {`${Math.floor(recordingModal.elapsedSeconds / 60)
                  .toString()
                  .padStart(2, "0")}:${(recordingModal.elapsedSeconds % 60)
                  .toString()
                  .padStart(2, "0")}`}
              </div>
            </div>

            <div className="mt-5 flex items-center gap-4 rounded-xl border border-brand-100 bg-brand-50/50 px-4 py-4">
              <div className="grid h-12 w-12 shrink-0 place-items-center rounded-xl bg-brand-100 text-brand-700 shadow-sm">
                <Mic className="h-5 w-5" />
              </div>
              <div className="grid h-16 w-full grid-cols-32 items-end gap-[3px] overflow-hidden">
                {recordingModal.levels.map((level, idx) => {
                  const segmentCount = 9
                  const activeSegments = Math.max(1, Math.min(segmentCount, Math.round(level * segmentCount)))
                  return (
                    <div key={idx} className="flex h-full w-full flex-col-reverse gap-[2px]">
                      {Array.from({ length: segmentCount }, (_, segmentIdx) => (
                        <div
                          key={segmentIdx}
                          className="h-[5px] rounded-[2px]"
                          style={{
                            background:
                              segmentIdx < activeSegments
                                ? "linear-gradient(180deg, #9E2891 0%, #6A1B9A 100%)"
                                : "rgba(189, 189, 189, 0.35)",
                          }}
                        />
                      ))}
                    </div>
                  )
                })}
              </div>
            </div>

            <div className="mt-6 flex flex-wrap justify-end gap-2">
              <Button
                type="button"
                variant="outline"
                className="border-grayScale-200 text-grayScale-700 hover:bg-grayScale-50"
                onClick={() => void stopActiveRecording(false)}
              >
                Cancel
              </Button>
              <Button
                type="button"
                variant="outline"
                className="border-grayScale-200 text-grayScale-700 hover:bg-grayScale-50"
                onClick={togglePauseRecording}
              >
                {recordingModal.isPaused ? "Continue" : "Pause"}
              </Button>
              <Button
                type="button"
                className="bg-brand-500 text-white hover:bg-brand-600"
                onClick={() => void stopActiveRecording(true)}
              >
                Stop & Save
              </Button>
            </div>
          </div>
        </div>
      ) : null}
    </div>
  )
}
