"use server"

import { revalidatePath } from "next/cache"
import { prisma } from "@/lib/prisma"
import { auth } from "@/lib/auth"
import { Difficulty } from "@prisma/client"
import {
  selectCardsForSession,
  calculateCorrectAnswer,
  calculateIncorrectAnswer,
  generateWrongAnswers,
  shuffleOptions,
  INITIAL_PROGRESS,
  type SelectableCard,
  type HanziOption,
} from "@/lib/learning/sm2"
import { z } from "zod"

/**
 * Standard action result type
 */
export type ActionResult<T = void> = {
  success: boolean
  data?: T
  message?: string
  errors?: Record<string, string[]>
}

// ============================================================================
// VALIDATION SCHEMAS
// ============================================================================

const startLearningSessionSchema = z.object({
  collectionId: z.string().optional(),
  cardsCount: z.number().int().positive().default(20),
})

const submitAnswerSchema = z.object({
  sessionId: z.string().min(1),
  hanziId: z.string().min(1),
  selectedPinyin: z.string().min(1),
  correct: z.boolean(),
  timeSpentMs: z.number().int().min(0),
})

const endSessionSchema = z.object({
  sessionId: z.string().min(1),
})

const updateCardDifficultySchema = z.object({
  hanziId: z.string().min(1),
  difficulty: z.nativeEnum(Difficulty),
})

const removeFromLearningSchema = z.object({
  hanziId: z.string().min(1),
})

// ============================================================================
// LEARNING ACTIONS
// ============================================================================

/**
 * Start a learning session
 *
 * Selects due cards using SM-2 algorithm and creates a session.
 * If no collectionId provided, selects from all user's collections.
 *
 * @param collectionId - Optional collection to learn from
 * @param cardsCount - Number of cards to include (default: user preference)
 * @returns Session with cards and answer options
 */
export async function startLearningSession(
  collectionId?: string,
  cardsCount?: number
): Promise<ActionResult<{
  sessionId: string
  cards: Array<{
    hanziId: string
    simplified: string
    options: string[]
    correctPinyin: string
    meaning: string
  }>
}>> {
  try {
    const session = await auth()
    if (!session?.user?.id) {
      return {
        success: false,
        message: "Authentication required",
      }
    }

    // Get user preferences
    const preferences = await prisma.userPreference.findUnique({
      where: { userId: session.user.id },
    })

    const cardsPerSession = cardsCount || preferences?.cardsPerSession || 20

    const validation = startLearningSessionSchema.safeParse({
      collectionId,
      cardsCount: cardsPerSession,
    })

    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    // Get user's hanzi progress for due cards
    const whereClause: any = {
      userId: session.user.id,
    }

    // If collectionId provided, filter by collection
    if (collectionId) {
      whereClause.hanzi = {
        collectionItems: {
          some: {
            collectionId,
          },
        },
      }
    }

    const userProgress = await prisma.userHanziProgress.findMany({
      where: whereClause,
      include: {
        hanzi: {
          include: {
            forms: {
              where: { isDefault: true },
              include: {
                transcriptions: {
                  where: { type: "pinyin" },
                },
              },
            },
            hskLevels: true,
          },
        },
      },
    })

    // Convert to SelectableCard format for SM-2 algorithm
    const selectableCards: SelectableCard[] = userProgress.map(progress => ({
      id: progress.hanziId,
      nextReviewDate: progress.nextReviewDate,
      incorrectCount: progress.incorrectCount,
      consecutiveCorrect: progress.consecutiveCorrect,
      manualDifficulty: progress.manualDifficulty as Difficulty,
    }))

    // Select cards using SM-2 algorithm
    const selectedCards = selectCardsForSession(selectableCards, cardsPerSession)

    // If not enough due cards, add new cards from collection
    if (selectedCards.length < cardsPerSession) {
      const neededCards = cardsPerSession - selectedCards.length

      // Find hanzi not yet in user progress
      const newHanziWhereClause: any = {
        id: {
          notIn: userProgress.map(p => p.hanziId),
        },
      }

      if (collectionId) {
        newHanziWhereClause.collectionItems = {
          some: {
            collectionId,
          },
        }
      }

      const newHanzi = await prisma.hanzi.findMany({
        where: newHanziWhereClause,
        include: {
          forms: {
            where: { isDefault: true },
            include: {
              transcriptions: {
                where: { type: "pinyin" },
              },
            },
          },
          hskLevels: true,
        },
        take: neededCards,
      })

      // Create initial progress for new cards
      const now = new Date()
      for (const hanzi of newHanzi) {
        await prisma.userHanziProgress.create({
          data: {
            userId: session.user.id,
            hanziId: hanzi.id,
            ...INITIAL_PROGRESS,
            nextReviewDate: now,
          },
        })

        selectedCards.push({
          id: hanzi.id,
          nextReviewDate: now,
          incorrectCount: 0,
          consecutiveCorrect: 0,
          manualDifficulty: Difficulty.MEDIUM,
        })
      }
    }

    if (selectedCards.length === 0) {
      return {
        success: false,
        message: "No cards available to learn",
      }
    }

    // Create learning session
    const learningSession = await prisma.learningSession.create({
      data: {
        userId: session.user.id,
        collectionId: collectionId || null,
        cardsReviewed: selectedCards.length,
      },
    })

    // Get full hanzi details for selected cards
    const selectedHanziIds = selectedCards.map(c => c.id)
    const hanziDetails = await prisma.hanzi.findMany({
      where: {
        id: { in: selectedHanziIds },
      },
      include: {
        forms: {
          where: { isDefault: true },
          include: {
            transcriptions: {
              where: { type: "pinyin" },
            },
            meanings: true,
          },
        },
        hskLevels: true,
      },
    })

    // Generate answer options for each card
    const cards = []
    for (const hanzi of hanziDetails) {
      const defaultForm = hanzi.forms[0]
      if (!defaultForm) continue

      const pinyinTranscription = defaultForm.transcriptions[0]
      if (!pinyinTranscription) continue

      const correctPinyin = pinyinTranscription.value

      // Get HSK level for this hanzi
      const hskLevel = hanzi.hskLevels[0]?.level || "new-1"

      // Get other hanzi from same HSK level for wrong answers
      const sameHskHanzi = await prisma.hanzi.findMany({
        where: {
          id: { not: hanzi.id },
          hskLevels: {
            some: {
              level: hskLevel,
            },
          },
        },
        include: {
          forms: {
            where: { isDefault: true },
            include: {
              transcriptions: {
                where: { type: "pinyin" },
              },
            },
          },
        },
        take: 10, // Get extra to ensure enough unique options
      })

      // Convert to HanziOption format
      const hanziOptions: HanziOption[] = sameHskHanzi
        .map(h => {
          const form = h.forms[0]
          const pinyin = form?.transcriptions[0]
          if (!form || !pinyin) return null
          return {
            id: h.id,
            simplified: h.simplified,
            pinyin: pinyin.value,
            hskLevel: hskLevel,
          }
        })
        .filter((h): h is HanziOption => h !== null)

      // Generate wrong answers
      let wrongAnswers: string[] = []
      try {
        wrongAnswers = generateWrongAnswers(
          {
            id: hanzi.id,
            simplified: hanzi.simplified,
            pinyin: correctPinyin,
            hskLevel,
          },
          hanziOptions
        )
      } catch (error) {
        // If not enough options, use random ones
        wrongAnswers = hanziOptions
          .slice(0, 3)
          .map(o => o.pinyin)
          .filter(p => p !== correctPinyin)

        // Fill with placeholders if still not enough
        while (wrongAnswers.length < 3) {
          wrongAnswers.push(`Option ${wrongAnswers.length + 1}`)
        }
      }

      // Shuffle all options
      const allOptions = shuffleOptions([correctPinyin, ...wrongAnswers])

      // Get English meaning (first meaning)
      const meaning = defaultForm.meanings[0]?.meaning || ""

      cards.push({
        hanziId: hanzi.id,
        simplified: hanzi.simplified,
        options: allOptions,
        correctPinyin,
        meaning,
      })
    }

    return {
      success: true,
      data: {
        sessionId: learningSession.id,
        cards,
      },
      message: `Learning session started with ${cards.length} cards`,
    }
  } catch (error) {
    console.error("Start learning session error:", error)
    return {
      success: false,
      message: "Failed to start learning session",
    }
  }
}

/**
 * Submit an answer for a card
 *
 * Records the answer, updates SM-2 progress, and creates a session review.
 *
 * @param sessionId - Current session ID
 * @param hanziId - Hanzi being reviewed
 * @param selectedPinyin - User's selected answer
 * @param correct - Whether answer was correct
 * @param timeSpentMs - Time spent on this card in milliseconds
 * @returns Updated progress information
 */
export async function submitAnswer(
  sessionId: string,
  hanziId: string,
  selectedPinyin: string,
  correct: boolean,
  timeSpentMs: number
): Promise<ActionResult<{
  easeFactor: number
  interval: number
  consecutiveCorrect: number
  nextReviewDate: Date
}>> {
  try {
    const session = await auth()
    if (!session?.user?.id) {
      return {
        success: false,
        message: "Authentication required",
      }
    }

    const validation = submitAnswerSchema.safeParse({
      sessionId,
      hanziId,
      selectedPinyin,
      correct,
      timeSpentMs,
    })

    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    // Verify session belongs to user
    const learningSession = await prisma.learningSession.findUnique({
      where: { id: sessionId },
    })

    if (!learningSession || learningSession.userId !== session.user.id) {
      return {
        success: false,
        message: "Invalid session",
      }
    }

    // Get current progress
    const progress = await prisma.userHanziProgress.findUnique({
      where: {
        userId_hanziId: {
          userId: session.user.id,
          hanziId,
        },
      },
    })

    if (!progress) {
      return {
        success: false,
        message: "Progress not found",
      }
    }

    // Calculate new progress using SM-2 algorithm
    const reviewDate = new Date()
    const updatedProgress = correct
      ? calculateCorrectAnswer(
          {
            easeFactor: progress.easeFactor,
            interval: progress.interval,
            consecutiveCorrect: progress.consecutiveCorrect,
            incorrectCount: progress.incorrectCount,
            nextReviewDate: progress.nextReviewDate,
          },
          reviewDate
        )
      : calculateIncorrectAnswer(
          {
            easeFactor: progress.easeFactor,
            interval: progress.interval,
            consecutiveCorrect: progress.consecutiveCorrect,
            incorrectCount: progress.incorrectCount,
            nextReviewDate: progress.nextReviewDate,
          },
          reviewDate
        )

    // Update progress
    await prisma.userHanziProgress.update({
      where: {
        userId_hanziId: {
          userId: session.user.id,
          hanziId,
        },
      },
      data: {
        easeFactor: updatedProgress.easeFactor,
        interval: updatedProgress.interval,
        consecutiveCorrect: updatedProgress.consecutiveCorrect,
        incorrectCount: updatedProgress.incorrectCount,
        nextReviewDate: updatedProgress.nextReviewDate,
      },
    })

    // Create session review record
    await prisma.sessionReview.create({
      data: {
        sessionId,
        hanziId,
        isCorrect: correct,
        responseTime: timeSpentMs,
      },
    })

    // Update session counts
    await prisma.learningSession.update({
      where: { id: sessionId },
      data: {
        correctAnswers: correct
          ? { increment: 1 }
          : undefined,
        incorrectAnswers: !correct
          ? { increment: 1 }
          : undefined,
      },
    })

    return {
      success: true,
      data: {
        easeFactor: updatedProgress.easeFactor,
        interval: updatedProgress.interval,
        consecutiveCorrect: updatedProgress.consecutiveCorrect,
        nextReviewDate: updatedProgress.nextReviewDate,
      },
      message: correct ? "Correct answer!" : "Incorrect answer",
    }
  } catch (error) {
    console.error("Submit answer error:", error)
    return {
      success: false,
      message: "Failed to submit answer",
    }
  }
}

/**
 * End a learning session
 *
 * Marks the session as complete and returns summary statistics.
 *
 * @param sessionId - Session to end
 * @returns Session summary with stats
 */
export async function endSession(
  sessionId: string
): Promise<ActionResult<{
  totalCards: number
  correctCount: number
  incorrectCount: number
  accuracyPercent: number
  durationMinutes: number
}>> {
  try {
    const session = await auth()
    if (!session?.user?.id) {
      return {
        success: false,
        message: "Authentication required",
      }
    }

    const validation = endSessionSchema.safeParse({ sessionId })

    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    // Verify session belongs to user
    const learningSession = await prisma.learningSession.findUnique({
      where: { id: sessionId },
    })

    if (!learningSession || learningSession.userId !== session.user.id) {
      return {
        success: false,
        message: "Invalid session",
      }
    }

    if (learningSession.endedAt) {
      return {
        success: false,
        message: "Session already completed",
      }
    }

    // Mark session as complete
    const endedAt = new Date()
    await prisma.learningSession.update({
      where: { id: sessionId },
      data: { endedAt },
    })

    // Calculate summary stats
    const totalCards = learningSession.cardsReviewed
    const correctCount = learningSession.correctAnswers
    const incorrectCount = learningSession.incorrectAnswers
    const accuracyPercent =
      totalCards > 0 ? Math.round((correctCount / totalCards) * 100) : 0
    const durationMs = endedAt.getTime() - learningSession.startedAt.getTime()
    const durationMinutes = Math.round(durationMs / 1000 / 60)

    revalidatePath("/dashboard")
    revalidatePath("/progress")

    return {
      success: true,
      data: {
        totalCards,
        correctCount,
        incorrectCount,
        accuracyPercent,
        durationMinutes,
      },
      message: "Session completed successfully",
    }
  } catch (error) {
    console.error("End session error:", error)
    return {
      success: false,
      message: "Failed to end session",
    }
  }
}

/**
 * Get count of due cards
 *
 * Returns counts of cards due now, today, and this week.
 *
 * @returns Due card counts
 */
export async function getDueCards(): Promise<ActionResult<{
  dueNow: number
  dueToday: number
  dueThisWeek: number
}>> {
  try {
    const session = await auth()
    if (!session?.user?.id) {
      return {
        success: false,
        message: "Authentication required",
      }
    }

    const now = new Date()
    const endOfToday = new Date(now)
    endOfToday.setHours(23, 59, 59, 999)
    const endOfWeek = new Date(now)
    endOfWeek.setDate(endOfWeek.getDate() + 7)

    const [dueNow, dueToday, dueThisWeek] = await Promise.all([
      prisma.userHanziProgress.count({
        where: {
          userId: session.user.id,
          nextReviewDate: {
            lte: now,
          },
          manualDifficulty: {
            not: Difficulty.SUSPENDED,
          },
        },
      }),
      prisma.userHanziProgress.count({
        where: {
          userId: session.user.id,
          nextReviewDate: {
            lte: endOfToday,
          },
          manualDifficulty: {
            not: Difficulty.SUSPENDED,
          },
        },
      }),
      prisma.userHanziProgress.count({
        where: {
          userId: session.user.id,
          nextReviewDate: {
            lte: endOfWeek,
          },
          manualDifficulty: {
            not: Difficulty.SUSPENDED,
          },
        },
      }),
    ])

    return {
      success: true,
      data: {
        dueNow,
        dueToday,
        dueThisWeek,
      },
    }
  } catch (error) {
    console.error("Get due cards error:", error)
    return {
      success: false,
      message: "Failed to get due cards",
    }
  }
}

/**
 * Update manual difficulty for a card
 *
 * Allows user to manually mark cards as EASY, NORMAL, HARD, or SUSPENDED.
 *
 * @param hanziId - Hanzi to update
 * @param difficulty - New difficulty level
 * @returns Success status
 */
export async function updateCardDifficulty(
  hanziId: string,
  difficulty: Difficulty
): Promise<ActionResult> {
  try {
    const session = await auth()
    if (!session?.user?.id) {
      return {
        success: false,
        message: "Authentication required",
      }
    }

    const validation = updateCardDifficultySchema.safeParse({ hanziId, difficulty })

    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    await prisma.userHanziProgress.update({
      where: {
        userId_hanziId: {
          userId: session.user.id,
          hanziId,
        },
      },
      data: {
        manualDifficulty: difficulty,
      },
    })

    revalidatePath("/learn")
    revalidatePath("/dashboard")

    return {
      success: true,
      message: `Card difficulty updated to ${difficulty}`,
    }
  } catch (error) {
    console.error("Update card difficulty error:", error)
    return {
      success: false,
      message: "Failed to update card difficulty",
    }
  }
}

/**
 * Remove a card from learning
 *
 * Suspends the card so it won't appear in future sessions.
 *
 * @param hanziId - Hanzi to remove
 * @returns Success status
 */
export async function removeFromLearning(hanziId: string): Promise<ActionResult> {
  try {
    const session = await auth()
    if (!session?.user?.id) {
      return {
        success: false,
        message: "Authentication required",
      }
    }

    const validation = removeFromLearningSchema.safeParse({ hanziId })

    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    await prisma.userHanziProgress.update({
      where: {
        userId_hanziId: {
          userId: session.user.id,
          hanziId,
        },
      },
      data: {
        manualDifficulty: Difficulty.SUSPENDED,
      },
    })

    revalidatePath("/learn")
    revalidatePath("/dashboard")

    return {
      success: true,
      message: "Card removed from learning",
    }
  } catch (error) {
    console.error("Remove from learning error:", error)
    return {
      success: false,
      message: "Failed to remove card from learning",
    }
  }
}
