import { describe, it, expect, beforeEach, vi } from 'vitest'
import { register, login, updatePassword, updateProfile } from './auth'
import { prisma } from '@/lib/prisma'
import bcrypt from 'bcrypt'

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

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

describe('Auth Server Actions - Integration Tests', () => {
  beforeEach(async () => {
    // Create English language for tests
    await prisma.language.create({
      data: {
        code: 'en',
        name: 'English',
        nativeName: 'English',
      },
    })
  })

  describe('register', () => {
    it('should successfully register a new user', async () => {
      const result = await register('test@example.com', 'password123', 'Test User')

      expect(result.success).toBe(true)
      expect(result.data?.userId).toBeDefined()
      expect(result.message).toBe('Account created successfully')

      // Verify user was created in database
      const user = await prisma.user.findUnique({
        where: { email: 'test@example.com' },
        include: { preference: true },
      })

      expect(user).toBeDefined()
      expect(user?.email).toBe('test@example.com')
      expect(user?.name).toBe('Test User')
      expect(user?.role).toBe('USER')
      expect(user?.isActive).toBe(true)

      // Verify password was hashed
      const isPasswordValid = await bcrypt.compare('password123', user!.password)
      expect(isPasswordValid).toBe(true)

      // Verify preferences were created
      expect(user?.preference).toBeDefined()
      expect(user?.preference?.characterDisplay).toBe('SIMPLIFIED')
      expect(user?.preference?.cardsPerSession).toBe(20)
    })

    it('should reject duplicate email', async () => {
      // Create first user
      await register('test@example.com', 'password123', 'Test User')

      // Try to create duplicate
      const result = await register('test@example.com', 'password456', 'Another User')

      expect(result.success).toBe(false)
      expect(result.message).toBe('A user with this email already exists')
    })

    it('should validate email format', async () => {
      const result = await register('invalid-email', 'password123', 'Test User')

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

    it('should validate password length', async () => {
      const result = await register('test@example.com', 'short', 'Test User')

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

    it('should handle missing default language', async () => {
      // Delete the English language
      await prisma.language.delete({ where: { code: 'en' } })

      const result = await register('test@example.com', 'password123', 'Test User')

      expect(result.success).toBe(false)
      expect(result.message).toBe('System configuration error: default language not found')
    })
  })

  describe('login', () => {
    beforeEach(async () => {
      // Create a test user
      await register('test@example.com', 'password123', 'Test User')
    })

    it('should successfully login with valid credentials', async () => {
      const { signIn } = await import('@/lib/auth')
      vi.mocked(signIn).mockResolvedValue(undefined)

      const result = await login('test@example.com', 'password123')

      expect(result.success).toBe(true)
      expect(result.message).toBe('Logged in successfully')
      expect(signIn).toHaveBeenCalledWith('credentials', {
        email: 'test@example.com',
        password: 'password123',
        redirect: false,
      })
    })

    it('should reject invalid credentials', async () => {
      const { signIn } = await import('@/lib/auth')
      vi.mocked(signIn).mockResolvedValue({ error: 'Invalid credentials' } as any)

      const result = await login('test@example.com', 'wrongpassword')

      expect(result.success).toBe(false)
      expect(result.message).toBe('Invalid email or password')
    })

    it('should validate email format', async () => {
      const result = await login('invalid-email', 'password123')

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

  describe('updatePassword', () => {
    let testUserId: string

    beforeEach(async () => {
      // Create a test user
      const result = await register('test@example.com', 'oldpassword123', '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)
    })

    it('should successfully update password with correct current password', async () => {
      const result = await updatePassword('oldpassword123', 'newpassword123')

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

      // Verify password was updated
      const user = await prisma.user.findUnique({ where: { id: testUserId } })
      const isNewPasswordValid = await bcrypt.compare('newpassword123', user!.password)
      expect(isNewPasswordValid).toBe(true)
    })

    it('should reject incorrect current password', async () => {
      const result = await updatePassword('wrongpassword', 'newpassword123')

      expect(result.success).toBe(false)
      expect(result.message).toBe('Current password is incorrect')
    })

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

      const result = await updatePassword('oldpassword123', 'newpassword123')

      expect(result.success).toBe(false)
      expect(result.message).toBe('You must be logged in to change your password')
    })

    it('should validate new password length', async () => {
      const result = await updatePassword('oldpassword123', 'short')

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

  describe('updateProfile', () => {
    let testUserId: string

    beforeEach(async () => {
      // 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)
    })

    it('should successfully update name', async () => {
      const result = await updateProfile('New Name', undefined)

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

      // Verify name was updated
      const user = await prisma.user.findUnique({ where: { id: testUserId } })
      expect(user?.name).toBe('New Name')
    })

    it('should successfully update email', async () => {
      const result = await updateProfile(undefined, 'newemail@example.com')

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

      // Verify email was updated
      const user = await prisma.user.findUnique({ where: { id: testUserId } })
      expect(user?.email).toBe('newemail@example.com')
    })

    it('should successfully update both name and email', async () => {
      const result = await updateProfile('New Name', 'newemail@example.com')

      expect(result.success).toBe(true)

      // Verify both were updated
      const user = await prisma.user.findUnique({ where: { id: testUserId } })
      expect(user?.name).toBe('New Name')
      expect(user?.email).toBe('newemail@example.com')
    })

    it('should reject duplicate email from another user', async () => {
      // Create another user
      await register('another@example.com', 'password123', 'Another User')

      // Try to update to existing email
      const result = await updateProfile(undefined, 'another@example.com')

      expect(result.success).toBe(false)
      expect(result.message).toBe('This email is already in use')
    })

    it('should allow updating to same email', async () => {
      const result = await updateProfile('New Name', 'test@example.com')

      expect(result.success).toBe(true)
    })

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

      const result = await updateProfile('New Name', undefined)

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

    it('should validate email format', async () => {
      const result = await updateProfile(undefined, 'invalid-email')

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