"use server"

import { revalidatePath } from "next/cache"
import { prisma } from "@/lib/prisma"
import { auth } from "@/lib/auth"
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 createCollectionSchema = z.object({
  name: z.string().min(1, "Name is required").max(100),
  description: z.string().optional(),
  isPublic: z.boolean(),
})

const updateCollectionSchema = z.object({
  id: z.string().min(1),
  name: z.string().min(1).max(100).optional(),
  description: z.string().optional(),
  isPublic: z.boolean().optional(),
})

const deleteCollectionSchema = z.object({
  id: z.string().min(1),
})

const addHanziToCollectionSchema = z.object({
  collectionId: z.string().min(1),
  hanziIds: z.array(z.string().min(1)).min(1, "At least one hanzi ID is required"),
})

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

const removeMultipleHanziSchema = z.object({
  collectionId: z.string().min(1),
  hanziIds: z.array(z.string().min(1)).min(1, "At least one hanzi ID is required"),
})

const searchHanziSchema = z.object({
  query: z.string().min(1),
  excludeCollectionId: z.string().optional(),
  limit: z.number().int().positive().max(100).default(20),
  offset: z.number().int().min(0).default(0),
})

// ============================================================================
// HELPER FUNCTIONS
// ============================================================================

/**
 * Check if user owns a collection or is admin
 */
async function canModifyCollection(collectionId: string): Promise<boolean> {
  const session = await auth()
  if (!session?.user) return false

  const user = session.user as any
  const collection = await prisma.collection.findUnique({
    where: { id: collectionId },
    select: { createdBy: true },
  })

  if (!collection) return false

  // Global collections can only be modified by admins
  if (collection.createdBy === null) {
    return user.role === "ADMIN"
  }

  // User can modify their own collections or admins can modify any
  return collection.createdBy === user.id || user.role === "ADMIN"
}

/**
 * Get next orderIndex for a collection
 */
async function getNextOrderIndex(collectionId: string): Promise<number> {
  const maxItem = await prisma.collectionItem.findFirst({
    where: { collectionId },
    orderBy: { orderIndex: "desc" },
    select: { orderIndex: true },
  })

  return maxItem ? maxItem.orderIndex + 1 : 0
}

// ============================================================================
// COLLECTION CRUD
// ============================================================================

/**
 * Create a new empty collection
 */
export async function createCollection(
  name: string,
  description?: string,
  isPublic: boolean = false
): Promise<ActionResult<{ id: string }>> {
  try {
    const session = await auth()
    if (!session?.user) {
      return { success: false, message: "Unauthorized" }
    }

    const validation = createCollectionSchema.safeParse({ name, description, isPublic })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    const user = session.user as any
    const collection = await prisma.collection.create({
      data: {
        name,
        description,
        isPublic,
        isGlobal: false,
        createdBy: user.id,
      },
    })

    revalidatePath("/collections")

    return {
      success: true,
      data: { id: collection.id },
      message: "Collection created successfully",
    }
  } catch (error) {
    console.error("Failed to create collection:", error)
    return {
      success: false,
      message: "Failed to create collection",
    }
  }
}

/**
 * Create a collection with a list of hanzi (paste method)
 */
export async function createCollectionWithHanzi(
  name: string,
  description: string | undefined,
  isPublic: boolean,
  hanziList: string
): Promise<ActionResult<{ id: string; added: number; notFound: string[] }>> {
  try {
    const session = await auth()
    if (!session?.user) {
      return { success: false, message: "Unauthorized" }
    }

    const validation = createCollectionSchema.safeParse({ name, description, isPublic })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    // Parse and validate the hanzi list
    const parseResult = await parseHanziList(hanziList)
    if (!parseResult.success || !parseResult.data) {
      return {
        success: false,
        message: parseResult.message || "Failed to parse hanzi list",
        data: { id: "", added: 0, notFound: parseResult.data?.notFound || [] },
      }
    }

    if (!parseResult.data.valid) {
      return {
        success: false,
        message: `${parseResult.data.notFound.length} hanzi not found in database`,
        data: { id: "", added: 0, notFound: parseResult.data.notFound },
      }
    }

    // Create collection and add hanzi in a transaction
    const user = session.user as any
    const result = await prisma.$transaction(async (tx) => {
      const collection = await tx.collection.create({
        data: {
          name,
          description,
          isPublic,
          isGlobal: false,
          createdBy: user.id,
        },
      })

      // Add hanzi with preserved order
      const items = parseResult.data!.found.map((hanzi, index) => ({
        collectionId: collection.id,
        hanziId: hanzi.id,
        orderIndex: index,
      }))

      await tx.collectionItem.createMany({
        data: items,
      })

      return {
        id: collection.id,
        added: items.length,
      }
    })

    revalidatePath("/collections")

    return {
      success: true,
      data: { ...result, notFound: [] },
      message: `Collection created with ${result.added} hanzi`,
    }
  } catch (error) {
    console.error("Failed to create collection with hanzi:", error)
    return {
      success: false,
      message: "Failed to create collection",
      data: { id: "", added: 0, notFound: [] },
    }
  }
}

/**
 * Update collection details
 */
export async function updateCollection(
  id: string,
  data: { name?: string; description?: string; isPublic?: boolean }
): Promise<ActionResult> {
  try {
    const validation = updateCollectionSchema.safeParse({ id, ...data })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    const canModify = await canModifyCollection(id)
    if (!canModify) {
      return { success: false, message: "Unauthorized" }
    }

    await prisma.collection.update({
      where: { id },
      data,
    })

    revalidatePath("/collections")
    revalidatePath(`/collections/${id}`)

    return {
      success: true,
      message: "Collection updated successfully",
    }
  } catch (error) {
    console.error("Failed to update collection:", error)
    return {
      success: false,
      message: "Failed to update collection",
    }
  }
}

/**
 * Delete a collection
 */
export async function deleteCollection(id: string): Promise<ActionResult> {
  try {
    const validation = deleteCollectionSchema.safeParse({ id })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    const canModify = await canModifyCollection(id)
    if (!canModify) {
      return { success: false, message: "Unauthorized" }
    }

    // Delete collection (cascade will handle items)
    await prisma.collection.delete({
      where: { id },
    })

    revalidatePath("/collections")

    return {
      success: true,
      message: "Collection deleted successfully",
    }
  } catch (error) {
    console.error("Failed to delete collection:", error)
    return {
      success: false,
      message: "Failed to delete collection",
    }
  }
}

/**
 * Get collection details with hanzi (ordered by orderIndex)
 */
export async function getCollection(id: string): Promise<
  ActionResult<{
    id: string
    name: string
    description: string | null
    isPublic: boolean
    isGlobal: boolean
    createdBy: string | null
    hanziCount: number
    hanzi: Array<{
      id: string
      simplified: string
      pinyin: string | null
      meaning: string | null
      orderIndex: number
    }>
  }>
> {
  try {
    const session = await auth()
    if (!session?.user) {
      return { success: false, message: "Unauthorized" }
    }

    const user = session.user as any
    const collection = await prisma.collection.findUnique({
      where: { id },
      include: {
        items: {
          orderBy: { orderIndex: "asc" },
          include: {
            hanzi: {
              include: {
                forms: {
                  where: { isDefault: true },
                  include: {
                    transcriptions: {
                      where: { type: "pinyin" },
                      take: 1,
                    },
                    meanings: {
                      orderBy: { orderIndex: "asc" },
                      take: 1,
                    },
                  },
                  take: 1,
                },
              },
            },
          },
        },
      },
    })

    if (!collection) {
      return { success: false, message: "Collection not found" }
    }

    // Check access: global collections or own collections or public collections
    if (!collection.isGlobal && !collection.isPublic && collection.createdBy !== user.id) {
      return { success: false, message: "Unauthorized" }
    }

    const hanzi = collection.items.map((item) => ({
      id: item.hanzi.id,
      simplified: item.hanzi.simplified,
      pinyin: item.hanzi.forms[0]?.transcriptions[0]?.value || null,
      meaning: item.hanzi.forms[0]?.meanings[0]?.meaning || null,
      orderIndex: item.orderIndex,
    }))

    return {
      success: true,
      data: {
        id: collection.id,
        name: collection.name,
        description: collection.description,
        isPublic: collection.isPublic,
        isGlobal: collection.isGlobal,
        createdBy: collection.createdBy,
        hanziCount: hanzi.length,
        hanzi,
      },
    }
  } catch (error) {
    console.error("Failed to get collection:", error)
    return {
      success: false,
      message: "Failed to get collection",
    }
  }
}

/**
 * Get user's collections
 */
export async function getUserCollections(): Promise<
  ActionResult<
    Array<{
      id: string
      name: string
      description: string | null
      isPublic: boolean
      hanziCount: number
      createdAt: Date
    }>
  >
> {
  try {
    const session = await auth()
    if (!session?.user) {
      return { success: false, message: "Unauthorized" }
    }

    const user = session.user as any
    const collections = await prisma.collection.findMany({
      where: {
        createdBy: user.id,
      },
      include: {
        _count: {
          select: { items: true },
        },
      },
      orderBy: { createdAt: "desc" },
    })

    const data = collections.map((col) => ({
      id: col.id,
      name: col.name,
      description: col.description,
      isPublic: col.isPublic,
      hanziCount: col._count.items,
      createdAt: col.createdAt,
    }))

    return {
      success: true,
      data,
    }
  } catch (error) {
    console.error("Failed to get user collections:", error)
    return {
      success: false,
      message: "Failed to get collections",
    }
  }
}

/**
 * Get global (HSK) collections
 */
export async function getGlobalCollections(): Promise<
  ActionResult<
    Array<{
      id: string
      name: string
      description: string | null
      hanziCount: number
    }>
  >
> {
  try {
    const collections = await prisma.collection.findMany({
      where: {
        isGlobal: true,
      },
      include: {
        _count: {
          select: { items: true },
        },
      },
      orderBy: { name: "asc" },
    })

    const data = collections.map((col) => ({
      id: col.id,
      name: col.name,
      description: col.description,
      hanziCount: col._count.items,
    }))

    return {
      success: true,
      data,
    }
  } catch (error) {
    console.error("Failed to get global collections:", error)
    return {
      success: false,
      message: "Failed to get global collections",
    }
  }
}

// ============================================================================
// ADD/REMOVE HANZI
// ============================================================================

/**
 * Add hanzi to collection (bulk operation)
 * Skips duplicates and preserves order
 */
export async function addHanziToCollection(
  collectionId: string,
  hanziIds: string[]
): Promise<ActionResult<{ added: number; skipped: string[] }>> {
  try {
    const validation = addHanziToCollectionSchema.safeParse({ collectionId, hanziIds })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    const canModify = await canModifyCollection(collectionId)
    if (!canModify) {
      return { success: false, message: "Unauthorized" }
    }

    // Check for existing items
    const existing = await prisma.collectionItem.findMany({
      where: {
        collectionId,
        hanziId: { in: hanziIds },
      },
      select: { hanziId: true },
    })

    const existingIds = new Set(existing.map((item) => item.hanziId))
    const toAdd = hanziIds.filter((id) => !existingIds.has(id))
    const skipped = hanziIds.filter((id) => existingIds.has(id))

    if (toAdd.length === 0) {
      return {
        success: true,
        data: { added: 0, skipped },
        message: "All hanzi already in collection",
      }
    }

    // Get next orderIndex and add items
    const nextOrderIndex = await getNextOrderIndex(collectionId)
    const items = toAdd.map((hanziId, index) => ({
      collectionId,
      hanziId,
      orderIndex: nextOrderIndex + index,
    }))

    await prisma.collectionItem.createMany({
      data: items,
    })

    revalidatePath(`/collections/${collectionId}`)

    return {
      success: true,
      data: { added: toAdd.length, skipped },
      message:
        skipped.length > 0
          ? `Added ${toAdd.length} hanzi, skipped ${skipped.length} duplicates`
          : `Added ${toAdd.length} hanzi`,
    }
  } catch (error) {
    console.error("Failed to add hanzi to collection:", error)
    return {
      success: false,
      message: "Failed to add hanzi",
      data: { added: 0, skipped: [] },
    }
  }
}

/**
 * Remove a single hanzi from collection
 */
export async function removeHanziFromCollection(
  collectionId: string,
  hanziId: string
): Promise<ActionResult> {
  try {
    const validation = removeHanziFromCollectionSchema.safeParse({ collectionId, hanziId })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    const canModify = await canModifyCollection(collectionId)
    if (!canModify) {
      return { success: false, message: "Unauthorized" }
    }

    await prisma.collectionItem.deleteMany({
      where: {
        collectionId,
        hanziId,
      },
    })

    revalidatePath(`/collections/${collectionId}`)

    return {
      success: true,
      message: "Hanzi removed from collection",
    }
  } catch (error) {
    console.error("Failed to remove hanzi from collection:", error)
    return {
      success: false,
      message: "Failed to remove hanzi",
    }
  }
}

/**
 * Remove multiple hanzi from collection (bulk operation)
 */
export async function removeMultipleHanziFromCollection(
  collectionId: string,
  hanziIds: string[]
): Promise<ActionResult<{ removed: number }>> {
  try {
    const validation = removeMultipleHanziSchema.safeParse({ collectionId, hanziIds })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    const canModify = await canModifyCollection(collectionId)
    if (!canModify) {
      return { success: false, message: "Unauthorized" }
    }

    const result = await prisma.collectionItem.deleteMany({
      where: {
        collectionId,
        hanziId: { in: hanziIds },
      },
    })

    revalidatePath(`/collections/${collectionId}`)

    return {
      success: true,
      data: { removed: result.count },
      message: `Removed ${result.count} hanzi from collection`,
    }
  } catch (error) {
    console.error("Failed to remove hanzi from collection:", error)
    return {
      success: false,
      message: "Failed to remove hanzi",
      data: { removed: 0 },
    }
  }
}

// ============================================================================
// SEARCH AND PARSE
// ============================================================================

/**
 * Search hanzi for adding to collection
 * Optionally excludes hanzi already in a specific collection
 */
export async function searchHanziForCollection(
  query: string,
  excludeCollectionId?: string,
  limit: number = 20,
  offset: number = 0
): Promise<
  ActionResult<{
    hanzi: Array<{
      id: string
      simplified: string
      pinyin: string | null
      meaning: string | null
      inCollection: boolean
    }>
    total: number
    hasMore: boolean
  }>
> {
  try {
    const session = await auth()
    if (!session?.user) {
      return { success: false, message: "Unauthorized" }
    }

    const validation = searchHanziSchema.safeParse({ query, excludeCollectionId, limit, offset })
    if (!validation.success) {
      return {
        success: false,
        message: "Validation failed",
        errors: validation.error.flatten().fieldErrors,
      }
    }

    // Search hanzi by simplified, pinyin, or meaning
    const searchResults = await prisma.hanzi.findMany({
      where: {
        OR: [
          { simplified: { contains: query } },
          {
            forms: {
              some: {
                OR: [
                  { transcriptions: { some: { value: { contains: query } } } },
                  { meanings: { some: { meaning: { contains: query } } } },
                ],
              },
            },
          },
        ],
      },
      include: {
        forms: {
          where: { isDefault: true },
          include: {
            transcriptions: {
              where: { type: "pinyin" },
              take: 1,
            },
            meanings: {
              orderBy: { orderIndex: "asc" },
              take: 1,
            },
          },
          take: 1,
        },
      },
      skip: offset,
      take: limit + 1, // Take one extra to check if there are more
    })

    const hasMore = searchResults.length > limit
    const results = searchResults.slice(0, limit)

    // Check which hanzi are already in the collection
    let inCollectionIds = new Set<string>()
    if (excludeCollectionId) {
      const collectionItems = await prisma.collectionItem.findMany({
        where: {
          collectionId: excludeCollectionId,
          hanziId: { in: results.map((h) => h.id) },
        },
        select: { hanziId: true },
      })
      inCollectionIds = new Set(collectionItems.map((item) => item.hanziId))
    }

    const hanzi = results.map((h) => ({
      id: h.id,
      simplified: h.simplified,
      pinyin: h.forms[0]?.transcriptions[0]?.value || null,
      meaning: h.forms[0]?.meanings[0]?.meaning || null,
      inCollection: inCollectionIds.has(h.id),
    }))

    // Get total count for pagination
    const total = await prisma.hanzi.count({
      where: {
        OR: [
          { simplified: { contains: query } },
          {
            forms: {
              some: {
                OR: [
                  { transcriptions: { some: { value: { contains: query } } } },
                  { meanings: { some: { meaning: { contains: query } } } },
                ],
              },
            },
          },
        ],
      },
    })

    return {
      success: true,
      data: {
        hanzi,
        total,
        hasMore,
      },
    }
  } catch (error) {
    console.error("Failed to search hanzi:", error)
    return {
      success: false,
      message: "Failed to search hanzi",
    }
  }
}

/**
 * Parse pasted hanzi list and validate all exist (strict mode)
 * Supports newline, comma, space separated lists
 */
export async function parseHanziList(input: string): Promise<
  ActionResult<{
    valid: boolean
    found: Array<{ id: string; simplified: string; pinyin: string | null }>
    notFound: string[]
    duplicates: string[]
  }>
> {
  try {
    const session = await auth()
    if (!session?.user) {
      return { success: false, message: "Unauthorized" }
    }

    // Split by newline, comma, or space, then trim and filter empty
    const chars = input
      .split(/[\n,\s]+/)
      .map((s) => s.trim())
      .filter((s) => s.length > 0)

    if (chars.length === 0) {
      return {
        success: false,
        message: "No hanzi found in input",
        data: { valid: false, found: [], notFound: [], duplicates: [] },
      }
    }

    // Check for duplicates in input
    const seen = new Set<string>()
    const duplicates: string[] = []
    const uniqueChars = chars.filter((char) => {
      if (seen.has(char)) {
        if (!duplicates.includes(char)) {
          duplicates.push(char)
        }
        return false
      }
      seen.add(char)
      return true
    })

    // Query database for all hanzi
    const foundHanzi = await prisma.hanzi.findMany({
      where: {
        simplified: { in: uniqueChars },
      },
      include: {
        forms: {
          where: { isDefault: true },
          include: {
            transcriptions: {
              where: { type: "pinyin" },
              take: 1,
            },
          },
          take: 1,
        },
      },
    })

    const foundSet = new Set(foundHanzi.map((h) => h.simplified))
    const notFound = uniqueChars.filter((char) => !foundSet.has(char))

    // Preserve order from input
    const found = uniqueChars
      .map((char) => {
        const hanzi = foundHanzi.find((h) => h.simplified === char)
        if (!hanzi) return null
        return {
          id: hanzi.id,
          simplified: hanzi.simplified,
          pinyin: hanzi.forms[0]?.transcriptions[0]?.value || null,
        }
      })
      .filter((h): h is { id: string; simplified: string; pinyin: string | null } => h !== null)

    const valid = notFound.length === 0

    return {
      success: true,
      data: {
        valid,
        found,
        notFound,
        duplicates,
      },
      message: valid ? `Found ${found.length} hanzi` : `${notFound.length} hanzi not found`,
    }
  } catch (error) {
    console.error("Failed to parse hanzi list:", error)
    return {
      success: false,
      message: "Failed to parse hanzi list",
      data: { valid: false, found: [], notFound: [], duplicates: [] },
    }
  }
}
