import { createId } from "@paralleldrive/cuid2"
import type { Dispatch, SetStateAction } from "react"
import { toast } from "sonner"
import type { useTranslations } from "next-intl"
import { previewAiExamAction, type AiPreviewData } from "../actions"
import type { ExamNode } from "./assembly/selected-question-list"
import type { Question } from "@/modules/questions/types"
import type { EditableQuestionContent, ExamFormValues, PreviewBackgroundTask, PreviewSnapshotMeta } from "./exam-form-types"
import { previewTaskStorageKey } from "./exam-form-types"

type Translator = ReturnType<typeof useTranslations>

export function buildPreviewNodes(data: AiPreviewData): ExamNode[] {
  const now = new Date()
  const toQuestionNode = (q: { id: string; type: Question["type"]; difficulty: number; score: number; content: Question["content"] }): ExamNode => ({
    id: q.id,
    type: "question",
    questionId: q.id,
    score: q.score,
    question: {
      id: q.id,
      content: q.content,
      type: q.type,
      difficulty: q.difficulty,
      createdAt: now,
      updatedAt: now,
      author: null,
      knowledgePoints: [],
    } satisfies Question,
  })

  if (data.sections && data.sections.length > 0) {
    return data.sections.map((section) => ({
      id: section.id || createId(),
      type: "group",
      title: section.title,
      children: section.questions.map((q) => toQuestionNode(q)),
    }))
  }

  return (data.questions ?? []).map((q) => toQuestionNode(q))
}

export function parseEditableContent(raw: unknown): EditableQuestionContent {
  const parseFromObject = (value: unknown): EditableQuestionContent => {
    if (!value || typeof value !== "object") return { text: "", options: [], subQuestions: [] }
    const record = value as { text?: unknown; options?: unknown; subQuestions?: unknown }
    const text = typeof record.text === "string" ? record.text : ""
    const options = Array.isArray(record.options)
      ? record.options.map((opt, index) => {
        const item = opt && typeof opt === "object" ? opt as { id?: unknown; text?: unknown; isCorrect?: unknown } : {}
        return {
          id: typeof item.id === "string" && item.id.trim().length > 0 ? item.id : String.fromCharCode(65 + index),
          text: typeof item.text === "string" ? item.text : "",
          isCorrect: typeof item.isCorrect === "boolean" ? item.isCorrect : false,
        }
      })
      : []
    const subQuestions = Array.isArray(record.subQuestions)
      ? record.subQuestions.map((item, index) => {
        const row = item && typeof item === "object"
          ? item as { id?: unknown; text?: unknown; answer?: unknown; score?: unknown }
          : {}
        const rawScore = typeof row.score === "number" ? row.score : Number.parseInt(String(row.score ?? ""), 10)
        return {
          id: typeof row.id === "string" && row.id.trim().length > 0 ? row.id : String(index + 1),
          text: typeof row.text === "string" ? row.text : "",
          answer: typeof row.answer === "string" ? row.answer : "",
          score: Number.isFinite(rawScore) ? rawScore : undefined,
        }
      })
      : []
    return { text, options, subQuestions }
  }

  if (typeof raw === "string") {
    try {
      return parseFromObject(JSON.parse(raw))
    } catch {
      return { text: raw, options: [], subQuestions: [] }
    }
  }
  return parseFromObject(raw)
}

export function flattenPreviewQuestions(nodes: ExamNode[]) {
  const rows: Array<{ node: ExamNode; sectionTitle?: string }> = []
  const walk = (items: ExamNode[], sectionTitle?: string) => {
    items.forEach((node) => {
      if (node.type === "question" && node.questionId && node.question) {
        rows.push({ node, sectionTitle })
        return
      }
      if (node.type === "group" && node.children) {
        walk(node.children, node.title || sectionTitle)
      }
      if (node.type === "section" && node.children) {
        walk(node.children, node.title || sectionTitle)
      }
    })
  }
  walk(nodes)
  return rows
}

export function findPreviewQuestionNode(nodes: ExamNode[], questionId: string): ExamNode | null {
  for (const node of nodes) {
    if (node.type === "question" && node.questionId === questionId && node.question) {
      return node
    }
    if ((node.type === "group" || node.type === "section") && node.children) {
      const found = findPreviewQuestionNode(node.children, questionId)
      if (found) return found
    }
  }
  return null
}

export function updatePreviewQuestionNodeInList(questionId: string, items: ExamNode[], updater: (node: ExamNode) => ExamNode): ExamNode[] {
  return items.map((node) => {
    if (node.type === "question" && node.questionId === questionId && node.question) {
      return updater(node)
    }
    if ((node.type === "group" || node.type === "section") && node.children) {
      return { ...node, children: updatePreviewQuestionNodeInList(questionId, node.children, updater) }
    }
    return node
  })
}

export function buildPreviewSignature(values: ExamFormValues) {
  return JSON.stringify({
    sourceText: values.aiSourceText?.trim() || "",
    questionCount: values.aiQuestionCount ?? null,
    providerId: values.aiProviderId ?? "",
    title: values.title?.trim() || "",
    subject: values.subject?.trim() || "",
    grade: values.grade?.trim() || "",
    difficulty: values.difficulty ?? "",
    totalScore: values.totalScore ?? "",
    durationMin: values.durationMin ?? "",
  })
}

export function buildPreviewPayload(nodes: ExamNode[]) {
  const questions: Array<{
    id: string
    type: Question["type"]
    difficulty: number
    score: number
    content: Question["content"]
  }> = []
  const seen = new Set<string>()
  const collect = (items: ExamNode[]) => {
    items.forEach((node) => {
      if (node.type === "question" && node.questionId && node.question) {
        if (!seen.has(node.questionId)) {
          seen.add(node.questionId)
          questions.push({
            id: node.questionId,
            type: node.question.type,
            difficulty: node.question.difficulty ?? 3,
            score: node.score ?? 0,
            content: node.question.content,
          })
        }
        return
      }
      if ((node.type === "group" || node.type === "section") && node.children) collect(node.children)
    })
  }
  collect(nodes)

  const cleanStructure = (items: ExamNode[]): Array<Omit<ExamNode, "question"> & { children?: unknown[] }> => {
    return items.map((node) => {
      const { question, ...rest } = node
      void question
      if (node.type === "group" || node.type === "section") {
        return { ...rest, children: cleanStructure(node.children || []) }
      }
      return rest
    })
  }

  return {
    questions,
    structure: cleanStructure(nodes),
  }
}

export function buildPreviewRequestData(values: ExamFormValues) {
  const sourceText = values.aiSourceText?.trim()
  if (!sourceText) return null
  const formData = new FormData()
  if (values.title?.trim()) formData.append("title", values.title.trim())
  if (values.subject?.trim()) formData.append("subject", values.subject.trim())
  if (values.grade?.trim()) formData.append("grade", values.grade.trim())
  const previewDifficulty = Number.parseInt(String(values.difficulty ?? "3"), 10)
  const previewTotalScore = typeof values.totalScore === "number" && values.totalScore > 0 ? values.totalScore : 100
  const previewDurationMin = typeof values.durationMin === "number" && values.durationMin > 0 ? values.durationMin : 90
  formData.append("difficulty", Number.isFinite(previewDifficulty) && previewDifficulty >= 1 && previewDifficulty <= 5 ? String(previewDifficulty) : "3")
  formData.append("totalScore", String(previewTotalScore))
  formData.append("durationMin", String(previewDurationMin))
  formData.append("aiSourceText", sourceText)
  if (values.aiQuestionCount) formData.append("aiQuestionCount", values.aiQuestionCount.toString())
  if (values.aiProviderId) formData.append("aiProviderId", values.aiProviderId)
  const meta: PreviewSnapshotMeta = {
    subject: values.subject?.trim() || "—",
    grade: values.grade?.trim() || "—",
    durationMin: previewDurationMin,
    totalScore: previewTotalScore,
  }
  return { formData, meta, signature: buildPreviewSignature(values) }
}

export function buildTaskFormValues(values: ExamFormValues): NonNullable<PreviewBackgroundTask["result"]>["formValues"] {
  return {
    title: values.title,
    subject: values.subject,
    grade: values.grade,
    difficulty: values.difficulty,
    totalScore: values.totalScore,
    durationMin: values.durationMin,
    aiSourceText: values.aiSourceText,
    aiQuestionCount: values.aiQuestionCount,
    aiProviderId: values.aiProviderId,
  }
}

export function isPreviewBackgroundTask(v: unknown): v is PreviewBackgroundTask {
  if (!v || typeof v !== "object") return false
  const obj = v as Record<string, unknown>
  return typeof obj.id === "string"
    && (obj.status === "queued" || obj.status === "running" || obj.status === "success" || obj.status === "failed")
    && typeof obj.createdAt === "number"
    && typeof obj.title === "string"
}

export function persistPreviewTasksToStorage(tasks: PreviewBackgroundTask[]) {
  try {
    window.localStorage.setItem(previewTaskStorageKey, JSON.stringify(tasks.slice(0, 20)))
  } catch (error) {
    console.error("[useExamPreview]", error instanceof Error ? error.message : String(error))
  }
}

export function parseStoredPreviewTasks(raw: string, t: Translator): PreviewBackgroundTask[] | null {
  let parsed: unknown = null
  try {
    parsed = JSON.parse(raw)
  } catch (error) {
    console.error("[useExamPreview]", error instanceof Error ? error.message : String(error))
    return null
  }
  if (!Array.isArray(parsed)) return null
  return parsed.filter(isPreviewBackgroundTask).map((task) =>
    (task.status === "queued" || task.status === "running")
      ? { ...task, status: "failed" as const, message: t("exam.previewHook.taskInterrupted") }
      : task)
}

export async function executeBackgroundPreviewTask(
  taskId: string,
  taskTitle: string,
  values: ExamFormValues,
  requestData: NonNullable<ReturnType<typeof buildPreviewRequestData>>,
  t: Translator,
  setPreviewTasks: Dispatch<SetStateAction<PreviewBackgroundTask[]>>,
) {
  setPreviewTasks((prev) => prev.map((task) => task.id === taskId ? { ...task, status: "running" } : task))
  try {
    const result = await previewAiExamAction(null, requestData.formData)
    const data = result.data
    if (result.success && data) {
      const nextNodes = buildPreviewNodes(data)
      setPreviewTasks((prev) => prev.map((task) => task.id === taskId
        ? { ...task, status: "success", result: { title: data.title, nodes: nextNodes, rawOutput: data.rawOutput ?? "", meta: requestData.meta, formValues: buildTaskFormValues(values) } }
        : task))
      toast.success(t("exam.previewHook.backgroundComplete", { title: taskTitle }))
      return
    }
    setPreviewTasks((prev) => prev.map((task) => task.id === taskId
      ? { ...task, status: "failed", message: result.message || t("exam.previewHook.generatePreviewFailed") }
      : task))
    toast.error(t("exam.previewHook.backgroundFailed", { title: taskTitle }))
  } catch (error) {
    console.error("[useExamPreview]", error instanceof Error ? error.message : String(error))
    setPreviewTasks((prev) => prev.map((task) => task.id === taskId
      ? { ...task, status: "failed", message: t("exam.previewHook.generatePreviewFailed") }
      : task))
    toast.error(t("exam.previewHook.backgroundFailed", { title: taskTitle }))
  }
}
