/**
 * 结构生成
 *
 * 职责：
 * - 将 AI 解析结果转换为预览数据（buildPreviewPayload）
 * - 将预览数据转换为持久化草稿（previewToDraft）
 * - 拆分结构条目（splitStructureItems）
 * - 并发映射工具（mapWithConcurrency）
 * - 依赖 parse.ts 中的类型与纯函数
 */

import { createId } from "@paralleldrive/cuid2"
import { z } from "zod"

import {
  AiExamResponseSchema,
  AiQuestionSchema,
  AiStructureResponseSchema,
  buildQuestionContent,
  normalizeScores,
  type AiGeneratedQuestion,
  type AiGeneratedStructureNode,
  type AiPreviewData,
  type AiPreviewQuestion,
} from "./parse"
import type { SplitQuestionItem } from "./request"

// ---------------------------------------------------------------------------
// Structure splitting
// ---------------------------------------------------------------------------

export const splitStructureItems = (
  draft: z.infer<typeof AiStructureResponseSchema>
): SplitQuestionItem[] => {
  const hasSections = Array.isArray(draft.sections) && draft.sections.length > 0
  if (!hasSections) {
    return (draft.questions ?? []).map((q) => ({
      sectionIndex: null,
      sectionTitle: undefined,
      text: q.text,
      score: q.score,
    } satisfies SplitQuestionItem))
  }
  const rows: SplitQuestionItem[] = []
  const sections = draft.sections
  if (sections) {
    sections.forEach((section, sectionIndex) => {
      section.questions.forEach((q) => {
        rows.push({
          sectionIndex,
          sectionTitle: section.title,
          text: q.text,
          score: q.score,
        })
      })
    })
  }
  return rows
}

// ---------------------------------------------------------------------------
// Concurrency utility
// ---------------------------------------------------------------------------

export const mapWithConcurrency = async <T, R>(
  items: T[],
  concurrency: number,
  worker: (item: T, index: number) => Promise<R>
): Promise<R[]> => {
  const results = new Array<R>(items.length)
  let cursor = 0
  const runWorker = async () => {
    while (cursor < items.length) {
      const index = cursor
      cursor += 1
      try {
        results[index] = await worker(items[index], index)
      } catch (error) {
        // Catch per-item errors so a single failure doesn't reject the whole batch.
        // The result slot stays undefined; callers should handle missing entries.
        console.error("[mapWithConcurrency] worker error at index", index, error instanceof Error ? error.message : String(error))
      }
    }
  }
  const workers = Array.from({ length: Math.max(1, Math.min(concurrency, items.length)) }, () => runWorker())
  await Promise.all(workers)
  return results
}

// ---------------------------------------------------------------------------
// Preview payload building
// ---------------------------------------------------------------------------

export const buildPreviewPayload = (
  aiParsed: z.infer<typeof AiExamResponseSchema>,
  input: {
    title: string
    difficulty: number
    totalScore: number
    questionCount?: number
  }
): AiPreviewData => {
  const hasSections = Array.isArray(aiParsed.sections) && aiParsed.sections.length > 0
  const baseQuestions = hasSections ? (aiParsed.sections ?? []).flatMap((s) => s.questions) : aiParsed.questions ?? []
  const limit = input.questionCount
  let sections = aiParsed.sections
  let flatQuestions = baseQuestions

  if (typeof limit === "number" && limit > 0) {
    if (hasSections) {
      const parsedSections = aiParsed.sections
      let remaining = limit
      sections = (parsedSections ?? []).map((s) => {
        if (remaining <= 0) return { ...s, questions: [] }
        const sliced = s.questions.slice(0, remaining)
        remaining -= sliced.length
        return { ...s, questions: sliced }
      }).filter((s) => s.questions.length > 0)
      flatQuestions = sections.flatMap((s) => s.questions)
    } else {
      flatQuestions = baseQuestions.slice(0, limit)
    }
  }

  const scores = normalizeScores(
    flatQuestions.map((q) => q.score ?? 0),
    input.totalScore
  )

  let scoreIndex = 0
  const toPreviewQuestion = (q: z.infer<typeof AiQuestionSchema>): AiPreviewQuestion => ({
    id: createId(),
    type: q.type,
    difficulty: q.difficulty ?? input.difficulty,
    score: scores[scoreIndex++] ?? 0,
    content: buildQuestionContent(q),
  })

  if (hasSections && sections && sections.length > 0) {
    return {
      title: aiParsed.title ?? input.title,
      sections: sections.map((section) => ({
        id: createId(),
        title: section.title,
        questions: section.questions.map((q) => toPreviewQuestion(q)),
      })),
    }
  }

  return {
    title: aiParsed.title ?? input.title,
    questions: flatQuestions.map((q) => toPreviewQuestion(q)),
  }
}

// ---------------------------------------------------------------------------
// Preview → Draft conversion
// ---------------------------------------------------------------------------

export const previewToDraft = (preview: AiPreviewData): {
  generated: AiGeneratedQuestion[]
  structure: AiGeneratedStructureNode[]
} => {
  const generated: AiGeneratedQuestion[] = []
  const structure: AiGeneratedStructureNode[] = []
  if (Array.isArray(preview.sections) && preview.sections.length > 0) {
    for (const section of preview.sections) {
      const children: AiGeneratedStructureNode[] = []
      for (const question of section.questions) {
        generated.push({
          id: question.id,
          type: question.type,
          difficulty: question.difficulty,
          score: question.score,
          content: question.content,
        })
        children.push({
          id: createId(),
          type: "question",
          questionId: question.id,
          score: question.score,
        })
      }
      structure.push({
        id: section.id || createId(),
        type: "group",
        title: section.title,
        children,
      })
    }
    return { generated, structure }
  }
  for (const question of preview.questions ?? []) {
    generated.push({
      id: question.id,
      type: question.type,
      difficulty: question.difficulty,
      score: question.score,
      content: question.content,
    })
    structure.push({
      id: createId(),
      type: "question",
      questionId: question.id,
      score: question.score,
    })
  }
  return { generated, structure }
}
