import { describe, it, expect, beforeEach, vi } from 'vitest'
import { getPreferences, updatePreferences, getAvailableLanguages } from './preferences'
import { register } from './auth'
import { prisma } from '@/lib/prisma'

// Mock the auth module
vi.mock('@/lib/auth', () => ({
  auth: vi.fn(),
}))

// Mock revalidatePath
vi.mock('next/cache', () => ({
  revalidatePath: vi.fn(),
}))

describe('Preferences Server Actions - Integration Tests', () => {
  let testUserId: string
  let testLanguageId: string

  beforeEach(async () => {
    // Create English language for tests
    const language = await prisma.language.create({
      data: {
        code: 'en',
        name: 'English',
        nativeName: 'English',
        isActive: true,
      },
    })
    testLanguageId = language.id

    // Create a test user
    const result = await register('test@example.com', 'password123', 'Test User')
    testUserId = result.data!.userId

    // Mock auth to return test user session
    const { auth } = await import('@/lib/auth')
    vi.mocked(auth).mockResolvedValue({
      user: { id: testUserId, email: 'test@example.com' },
    } as any)
  })

  describe('getPreferences', () => {
    it('should successfully get user preferences', async () => {
      const result = await getPreferences()

      expect(result.success).toBe(true)
      expect(result.data).toBeDefined()
      expect(result.data?.preferredLanguageId).toBe(testLanguageId)
      expect(result.data?.characterDisplay).toBe('SIMPLIFIED')
      expect(result.data?.transcriptionType).toBe('pinyin')
      expect(result.data?.cardsPerSession).toBe(20)
      expect(result.data?.dailyGoal).toBe(50)
      expect(result.data?.removalThreshold).toBe(10)
      expect(result.data?.allowManualDifficulty).toBe(true)
    })

    it('should reject when not logged in', async () => {
      const { auth } = await import('@/lib/auth')
      vi.mocked(auth).mockResolvedValue(null)

      const result = await getPreferences()

      expect(result.success).toBe(false)
      expect(result.message).toBe('You must be logged in to view preferences')
    })

    it('should handle missing preferences', async () => {
      // Delete the preferences
      await prisma.userPreference.delete({ where: { userId: testUserId } })

      const result = await getPreferences()

      expect(result.success).toBe(false)
      expect(result.message).toBe('Preferences not found')
    })
  })

  describe('updatePreferences', () => {
    it('should successfully update character display', async () => {
      const result = await updatePreferences({
        characterDisplay: 'TRADITIONAL',
      })

      expect(result.success).toBe(true)
      expect(result.message).toBe('Preferences updated successfully')

      // Verify preference was updated
      const preference = await prisma.userPreference.findUnique({
        where: { userId: testUserId },
      })
      expect(preference?.characterDisplay).toBe('TRADITIONAL')
    })

    it('should successfully update cards per session', async () => {
      const result = await updatePreferences({
        cardsPerSession: 30,
      })

      expect(result.success).toBe(true)

      // Verify preference was updated
      const preference = await prisma.userPreference.findUnique({
        where: { userId: testUserId },
      })
      expect(preference?.cardsPerSession).toBe(30)
    })

    it('should successfully update daily goal', async () => {
      const result = await updatePreferences({
        dailyGoal: 100,
      })

      expect(result.success).toBe(true)

      // Verify preference was updated
      const preference = await prisma.userPreference.findUnique({
        where: { userId: testUserId },
      })
      expect(preference?.dailyGoal).toBe(100)
    })

    it('should successfully update multiple preferences', async () => {
      const result = await updatePreferences({
        characterDisplay: 'BOTH',
        cardsPerSession: 25,
        dailyGoal: 75,
        transcriptionType: 'zhuyin',
        removalThreshold: 15,
        allowManualDifficulty: false,
      })

      expect(result.success).toBe(true)

      // Verify all preferences were updated
      const preference = await prisma.userPreference.findUnique({
        where: { userId: testUserId },
      })
      expect(preference?.characterDisplay).toBe('BOTH')
      expect(preference?.cardsPerSession).toBe(25)
      expect(preference?.dailyGoal).toBe(75)
      expect(preference?.transcriptionType).toBe('zhuyin')
      expect(preference?.removalThreshold).toBe(15)
      expect(preference?.allowManualDifficulty).toBe(false)
    })

    it('should successfully update preferred language', async () => {
      // Create another language
      const spanish = await prisma.language.create({
        data: {
          code: 'es',
          name: 'Spanish',
          nativeName: 'Español',
          isActive: true,
        },
      })

      const result = await updatePreferences({
        preferredLanguageId: spanish.id,
      })

      expect(result.success).toBe(true)

      // Verify preference was updated
      const preference = await prisma.userPreference.findUnique({
        where: { userId: testUserId },
      })
      expect(preference?.preferredLanguageId).toBe(spanish.id)
    })

    it('should reject when not logged in', async () => {
      const { auth } = await import('@/lib/auth')
      vi.mocked(auth).mockResolvedValue(null)

      const result = await updatePreferences({
        cardsPerSession: 30,
      })

      expect(result.success).toBe(false)
      expect(result.message).toBe('You must be logged in to update preferences')
    })

    it('should validate cards per session range', async () => {
      const result = await updatePreferences({
        cardsPerSession: 0, // Below minimum (1)
      })

      expect(result.success).toBe(false)
      expect(result.message).toBe('Validation failed')
    })

    it('should validate daily goal range', async () => {
      const result = await updatePreferences({
        dailyGoal: 0, // Below minimum (1)
      })

      expect(result.success).toBe(false)
      expect(result.message).toBe('Validation failed')
    })

    it('should validate character display enum', async () => {
      const result = await updatePreferences({
        characterDisplay: 'INVALID' as any,
      })

      expect(result.success).toBe(false)
      expect(result.message).toBe('Validation failed')
    })
  })

  describe('getAvailableLanguages', () => {
    it('should return all active languages', async () => {
      // Create additional languages
      await prisma.language.createMany({
        data: [
          {
            code: 'es',
            name: 'Spanish',
            nativeName: 'Español',
            isActive: true,
          },
          {
            code: 'fr',
            name: 'French',
            nativeName: 'Français',
            isActive: true,
          },
          {
            code: 'de',
            name: 'German',
            nativeName: 'Deutsch',
            isActive: false, // Inactive
          },
        ],
      })

      const result = await getAvailableLanguages()

      expect(result.success).toBe(true)
      expect(result.data).toBeDefined()
      expect(result.data?.length).toBe(3) // English, Spanish, French (not German)

      // Should be sorted by name
      const names = result.data!.map((lang) => lang.name)
      expect(names).toEqual(['English', 'French', 'Spanish'])
    })

    it('should return only active languages', async () => {
      await prisma.language.create({
        data: {
          code: 'de',
          name: 'German',
          nativeName: 'Deutsch',
          isActive: false,
        },
      })

      const result = await getAvailableLanguages()

      expect(result.success).toBe(true)
      const codes = result.data!.map((lang) => lang.code)
      expect(codes).not.toContain('de')
    })

    it('should return language with all required fields', async () => {
      const result = await getAvailableLanguages()

      expect(result.success).toBe(true)
      expect(result.data).toBeDefined()

      const language = result.data![0]
      expect(language).toHaveProperty('id')
      expect(language).toHaveProperty('code')
      expect(language).toHaveProperty('name')
      expect(language).toHaveProperty('nativeName')
    })

    it('should handle empty language list', async () => {
      // Delete user preferences first (they reference languages)
      await prisma.userPreference.deleteMany()
      // Then delete all languages
      await prisma.language.deleteMany()

      const result = await getAvailableLanguages()

      expect(result.success).toBe(true)
      expect(result.data).toEqual([])
    })
  })
})
