import { describe, it, expect, beforeEach, beforeAll } from "vitest"
import { prisma } from "@/lib/prisma"

/**
 * Integration tests for collections functionality
 * These tests focus on database operations and data integrity
 */

describe("Collections Database Operations", () => {
  let testUser: any
  let englishLanguage: any

  beforeEach(async () => {
    // Note: Global beforeEach in vitest.integration.setup.ts clears all data
    // So we need to recreate user and language in each test

    // Create English language
    englishLanguage = await prisma.language.create({
      data: {
        code: "en",
        name: "English",
        nativeName: "English",
        isActive: true,
      },
    })

    // Create test user
    testUser = await prisma.user.create({
      data: {
        email: "testcollections@example.com",
        name: "Test User",
        password: "dummy",
        role: "USER",
        isActive: true,
      },
    })
  })

  describe("Collection CRUD Operations", () => {
    it("should create a collection", async () => {
      const collection = await prisma.collection.create({
        data: {
          name: "Test Collection",
          description: "Test description",
          isPublic: false,
          isGlobal: false,
          createdBy: testUser.id,
        },
      })

      expect(collection.id).toBeDefined()
      expect(collection.name).toBe("Test Collection")
      expect(collection.createdBy).toBe(testUser.id)
    })

    it("should update a collection", async () => {
      const collection = await prisma.collection.create({
        data: {
          name: "Test Collection",
          isPublic: false,
          isGlobal: false,
          createdBy: testUser.id,
        },
      })

      const updated = await prisma.collection.update({
        where: { id: collection.id },
        data: {
          name: "Updated Name",
          description: "New description",
        },
      })

      expect(updated.name).toBe("Updated Name")
      expect(updated.description).toBe("New description")
    })

    it("should delete a collection", async () => {
      const collection = await prisma.collection.create({
        data: {
          name: "Test Collection",
          isPublic: false,
          isGlobal: false,
          createdBy: testUser.id,
        },
      })

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

      const deleted = await prisma.collection.findUnique({
        where: { id: collection.id },
      })

      expect(deleted).toBeNull()
    })

    it("should get user collections", async () => {
      await prisma.collection.createMany({
        data: [
          {
            name: "Collection 1",
            isPublic: false,
            isGlobal: false,
            createdBy: testUser.id,
          },
          {
            name: "Collection 2",
            isPublic: true,
            isGlobal: false,
            createdBy: testUser.id,
          },
        ],
      })

      const collections = await prisma.collection.findMany({
        where: { createdBy: testUser.id },
      })

      expect(collections.length).toBe(2)
    })

    it("should get global collections", async () => {
      await prisma.collection.create({
        data: {
          name: "HSK 1",
          isPublic: true,
          isGlobal: true,
          createdBy: null,
        },
      })

      const globalCollections = await prisma.collection.findMany({
        where: { isGlobal: true },
      })

      expect(globalCollections.length).toBe(1)
      expect(globalCollections[0].name).toBe("HSK 1")
    })
  })

  describe("Collection Items and OrderIndex", () => {
    let collection: any
    let hanzi1: any
    let hanzi2: any
    let hanzi3: any

    beforeEach(async () => {
      // Create collection
      collection = await prisma.collection.create({
        data: {
          name: "Test Collection",
          isPublic: false,
          isGlobal: false,
          createdBy: testUser.id,
        },
      })

      // Create hanzi
      hanzi1 = await prisma.hanzi.create({
        data: { simplified: "好", radical: "女", frequency: 100 },
      })
      hanzi2 = await prisma.hanzi.create({
        data: { simplified: "爱", radical: "爫", frequency: 200 },
      })
      hanzi3 = await prisma.hanzi.create({
        data: { simplified: "你", radical: "人", frequency: 50 },
      })
    })

    it("should add hanzi to collection with correct orderIndex", async () => {
      await prisma.collectionItem.createMany({
        data: [
          { collectionId: collection.id, hanziId: hanzi1.id, orderIndex: 0 },
          { collectionId: collection.id, hanziId: hanzi2.id, orderIndex: 1 },
          { collectionId: collection.id, hanziId: hanzi3.id, orderIndex: 2 },
        ],
      })

      const items = await prisma.collectionItem.findMany({
        where: { collectionId: collection.id },
        orderBy: { orderIndex: "asc" },
        include: { hanzi: true },
      })

      expect(items.length).toBe(3)
      expect(items[0].hanzi.simplified).toBe("好")
      expect(items[0].orderIndex).toBe(0)
      expect(items[1].hanzi.simplified).toBe("爱")
      expect(items[1].orderIndex).toBe(1)
      expect(items[2].hanzi.simplified).toBe("你")
      expect(items[2].orderIndex).toBe(2)
    })

    it("should preserve orderIndex after removing middle item", async () => {
      // Add three items
      await prisma.collectionItem.createMany({
        data: [
          { collectionId: collection.id, hanziId: hanzi1.id, orderIndex: 0 },
          { collectionId: collection.id, hanziId: hanzi2.id, orderIndex: 1 },
          { collectionId: collection.id, hanziId: hanzi3.id, orderIndex: 2 },
        ],
      })

      // Remove middle item
      await prisma.collectionItem.deleteMany({
        where: {
          collectionId: collection.id,
          hanziId: hanzi2.id,
        },
      })

      const items = await prisma.collectionItem.findMany({
        where: { collectionId: collection.id },
        orderBy: { orderIndex: "asc" },
        include: { hanzi: true },
      })

      expect(items.length).toBe(2)
      expect(items[0].hanzi.simplified).toBe("好")
      expect(items[0].orderIndex).toBe(0)
      expect(items[1].hanzi.simplified).toBe("你")
      expect(items[1].orderIndex).toBe(2) // Should keep original orderIndex
    })

    it("should prevent duplicate hanzi in collection (unique constraint)", async () => {
      await prisma.collectionItem.create({
        data: {
          collectionId: collection.id,
          hanziId: hanzi1.id,
          orderIndex: 0,
        },
      })

      // Try to add same hanzi again
      await expect(
        prisma.collectionItem.create({
          data: {
            collectionId: collection.id,
            hanziId: hanzi1.id,
            orderIndex: 1,
          },
        })
      ).rejects.toThrow()
    })

    it("should allow same hanzi in different collections", async () => {
      const collection2 = await prisma.collection.create({
        data: {
          name: "Second Collection",
          isPublic: false,
          isGlobal: false,
          createdBy: testUser.id,
        },
      })

      await prisma.collectionItem.createMany({
        data: [
          { collectionId: collection.id, hanziId: hanzi1.id, orderIndex: 0 },
          { collectionId: collection2.id, hanziId: hanzi1.id, orderIndex: 0 },
        ],
      })

      const items1 = await prisma.collectionItem.count({
        where: { collectionId: collection.id },
      })
      const items2 = await prisma.collectionItem.count({
        where: { collectionId: collection2.id },
      })

      expect(items1).toBe(1)
      expect(items2).toBe(1)
    })

    it("should delete collection items when collection is deleted (cascade)", async () => {
      await prisma.collectionItem.createMany({
        data: [
          { collectionId: collection.id, hanziId: hanzi1.id, orderIndex: 0 },
          { collectionId: collection.id, hanziId: hanzi2.id, orderIndex: 1 },
        ],
      })

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

      const items = await prisma.collectionItem.findMany({
        where: { collectionId: collection.id },
      })

      expect(items.length).toBe(0)
    })
  })

  describe("Hanzi Search Operations", () => {
    beforeEach(async () => {
      // Create hanzi with forms, transcriptions, and meanings
      const hanzi1 = await prisma.hanzi.create({
        data: { simplified: "好", radical: "女", frequency: 100 },
      })
      const form1 = await prisma.hanziForm.create({
        data: {
          hanziId: hanzi1.id,
          traditional: "好",
          isDefault: true,
        },
      })
      await prisma.hanziTranscription.create({
        data: {
          formId: form1.id,
          type: "pinyin",
          value: "hǎo",
        },
      })
      await prisma.hanziMeaning.create({
        data: {
          formId: form1.id,
          languageId: englishLanguage.id,
          meaning: "good",
          orderIndex: 0,
        },
      })

      const hanzi2 = await prisma.hanzi.create({
        data: { simplified: "你", radical: "人", frequency: 50 },
      })
      const form2 = await prisma.hanziForm.create({
        data: {
          hanziId: hanzi2.id,
          traditional: "你",
          isDefault: true,
        },
      })
      await prisma.hanziTranscription.create({
        data: {
          formId: form2.id,
          type: "pinyin",
          value: "nǐ",
        },
      })
      await prisma.hanziMeaning.create({
        data: {
          formId: form2.id,
          languageId: englishLanguage.id,
          meaning: "you",
          orderIndex: 0,
        },
      })

      const hanzi3 = await prisma.hanzi.create({
        data: { simplified: "中国", radical: null, frequency: 300 },
      })
      const form3 = await prisma.hanziForm.create({
        data: {
          hanziId: hanzi3.id,
          traditional: "中國",
          isDefault: true,
        },
      })
      await prisma.hanziTranscription.create({
        data: {
          formId: form3.id,
          type: "pinyin",
          value: "zhōng guó",
        },
      })
      await prisma.hanziMeaning.create({
        data: {
          formId: form3.id,
          languageId: englishLanguage.id,
          meaning: "China",
          orderIndex: 0,
        },
      })
    })

    it("should search hanzi by simplified character", async () => {
      const results = await prisma.hanzi.findMany({
        where: {
          simplified: { contains: "好" },
        },
        include: {
          forms: {
            where: { isDefault: true },
            include: {
              transcriptions: { where: { type: "pinyin" }, take: 1 },
              meanings: { orderBy: { orderIndex: "asc" }, take: 1 },
            },
            take: 1,
          },
        },
      })

      expect(results.length).toBeGreaterThan(0)
      expect(results.some((h) => h.simplified === "好")).toBe(true)
    })

    it("should search hanzi by pinyin transcription", async () => {
      const results = await prisma.hanzi.findMany({
        where: {
          forms: {
            some: {
              transcriptions: {
                some: {
                  value: { contains: "hǎo" },
                },
              },
            },
          },
        },
        include: {
          forms: {
            where: { isDefault: true },
            include: {
              transcriptions: { where: { type: "pinyin" }, take: 1 },
            },
            take: 1,
          },
        },
      })

      expect(results.length).toBeGreaterThan(0)
      expect(results.some((h) => h.simplified === "好")).toBe(true)
    })

    it("should search hanzi by meaning", async () => {
      const results = await prisma.hanzi.findMany({
        where: {
          forms: {
            some: {
              meanings: {
                some: {
                  meaning: { contains: "good" },
                },
              },
            },
          },
        },
        include: {
          forms: {
            where: { isDefault: true },
            include: {
              meanings: { orderBy: { orderIndex: "asc" }, take: 1 },
            },
            take: 1,
          },
        },
      })

      expect(results.length).toBeGreaterThan(0)
      expect(results.some((h) => h.simplified === "好")).toBe(true)
    })

    it("should support multi-character hanzi in database", async () => {
      const multiChar = await prisma.hanzi.findUnique({
        where: { simplified: "中国" },
        include: {
          forms: {
            where: { isDefault: true },
            include: {
              transcriptions: true,
              meanings: true,
            },
            take: 1,
          },
        },
      })

      expect(multiChar).toBeDefined()
      expect(multiChar?.simplified).toBe("中国")
      expect(multiChar?.forms[0].traditional).toBe("中國")
      expect(multiChar?.forms[0].transcriptions[0].value).toBe("zhōng guó")
      expect(multiChar?.forms[0].meanings[0].meaning).toBe("China")
    })
  })

  describe("Hanzi List Parsing Logic", () => {
    beforeEach(async () => {
      // Create test hanzi
      await prisma.hanzi.createMany({
        data: [
          { simplified: "好", radical: "女", frequency: 100 },
          { simplified: "爱", radical: "爫", frequency: 200 },
          { simplified: "你", radical: "人", frequency: 50 },
          { simplified: "中国", radical: null, frequency: 300 },
        ],
      })
    })

    it("should parse newline-separated list", () => {
      const input = "好\n爱\n你"
      const parsed = input
        .split(/[\n,\s]+/)
        .map((s) => s.trim())
        .filter((s) => s.length > 0)

      expect(parsed).toEqual(["好", "爱", "你"])
    })

    it("should parse comma-separated list", () => {
      const input = "好, 爱, 你"
      const parsed = input
        .split(/[\n,\s]+/)
        .map((s) => s.trim())
        .filter((s) => s.length > 0)

      expect(parsed).toEqual(["好", "爱", "你"])
    })

    it("should parse space-separated list", () => {
      const input = "好 爱 你"
      const parsed = input
        .split(/[\n,\s]+/)
        .map((s) => s.trim())
        .filter((s) => s.length > 0)

      expect(parsed).toEqual(["好", "爱", "你"])
    })

    it("should handle multi-character words", () => {
      const input = "好 中国 你"
      const parsed = input
        .split(/[\n,\s]+/)
        .map((s) => s.trim())
        .filter((s) => s.length > 0)

      expect(parsed).toEqual(["好", "中国", "你"])
    })

    it("should detect duplicates", () => {
      const input = "好 爱 好 你"
      const chars = input
        .split(/[\n,\s]+/)
        .map((s) => s.trim())
        .filter((s) => s.length > 0)

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

      expect(unique).toEqual(["好", "爱", "你"])
      expect(duplicates).toEqual(["好"])
    })

    it("should validate all hanzi exist (strict mode)", async () => {
      const input = ["好", "不存在", "你"]

      const foundHanzi = await prisma.hanzi.findMany({
        where: {
          simplified: { in: input },
        },
      })

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

      expect(notFound).toEqual(["不存在"])
    })

    it("should preserve order from input", async () => {
      const input = ["你", "爱", "好"]

      const foundHanzi = await prisma.hanzi.findMany({
        where: {
          simplified: { in: input },
        },
      })

      // Create a map for quick lookup
      const hanziMap = new Map(foundHanzi.map((h) => [h.simplified, h]))

      // Preserve input order
      const ordered = input
        .map((char) => hanziMap.get(char))
        .filter((h): h is NonNullable<typeof h> => h !== undefined)

      expect(ordered[0].simplified).toBe("你")
      expect(ordered[1].simplified).toBe("爱")
      expect(ordered[2].simplified).toBe("好")
    })
  })
})
