/**
 * Tests for UserGenerator
 */

import { describe, it, expect, beforeEach } from 'vitest'
import { UserGenerator } from '../../../core/generators/users'
import { DataRelationManager } from '../../../core/utils/data-relations'

describe('UserGenerator', () => {
  beforeEach(() => {
    UserGenerator.clearCache()
    DataRelationManager.clearAll()
  })

  describe('generate', () => {
    it('should generate a valid user with default options', () => {
      const user = UserGenerator.generate()
      
      expect(user).toBeDefined()
      expect(user.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)
      expect(user.username).toBeTruthy()
      expect(user.email).toMatch(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)
      expect(user.firstName).toBeTruthy()
      expect(user.lastName).toBeTruthy()
      expect(['admin', 'moderator', 'producer', 'artist', 'user']).toContain(user.role)
      expect(['active', 'inactive', 'suspended', 'pending']).toContain(user.status)
      expect(user.createdAt).toBeInstanceOf(Date)
      expect(user.updatedAt).toBeInstanceOf(Date)
    })

    it('should generate a user with specified role', () => {
      const user = UserGenerator.generate({ role: 'artist' })
      
      expect(user.role).toBe('artist')
      expect(user.isVerified).toBeDefined()
    })

    it('should generate a user with specified status', () => {
      const user = UserGenerator.generate({ status: 'inactive' })
      
      expect(user.status).toBe('inactive')
    })

    it('should generate users with unique usernames and emails', () => {
      const users = Array.from({ length: 10 }, () => UserGenerator.generate())
      
      const usernames = users.map(u => u.username)
      const emails = users.map(u => u.email)
      
      expect(new Set(usernames).size).toBe(usernames.length)
      expect(new Set(emails).size).toBe(emails.length)
    })
  })

  describe('generateBatch', () => {
    it('should generate multiple users', () => {
      const users = UserGenerator.generateBatch(5)
      
      expect(users).toHaveLength(5)
      users.forEach(user => {
        expect(user.id).toBeTruthy()
        expect(user.username).toBeTruthy()
        expect(user.email).toBeTruthy()
      })
    })

    it('should generate users with consistent options', () => {
      const users = UserGenerator.generateBatch(3, { role: 'producer', status: 'active' })
      
      users.forEach(user => {
        expect(user.role).toBe('producer')
        expect(user.status).toBe('active')
      })
    })
  })

  describe('generateWithDistribution', () => {
    it('should generate users with role distribution', () => {
      const users = UserGenerator.generateWithDistribution()
      
      expect(users.length).toBeGreaterThan(0)
      
      const roleCount = users.reduce((acc, user) => {
        acc[user.role] = (acc[user.role] || 0) + 1
        return acc
      }, {} as Record<string, number>)
      
      expect(roleCount.admin).toBeGreaterThan(0)
      expect(roleCount.user).toBeGreaterThan(0)
    })
  })

  describe('generateAdmin', () => {
    it('should generate an admin user', () => {
      const admin = UserGenerator.generateAdmin()
      
      expect(admin.role).toBe('admin')
      expect(admin.status).toBe('active')
      expect(admin.isVerified).toBe(true)
      expect(admin.emailVerified).toBe(true)
      expect(admin.avatar).toBeTruthy()
      expect(admin.bio).toBeTruthy()
    })
  })

  describe('generateArtist', () => {
    it('should generate an artist user', () => {
      const artist = UserGenerator.generateArtist()
      
      expect(artist.role).toBe('artist')
      expect(artist.avatar).toBeTruthy()
      expect(artist.bio).toBeTruthy()
    })
  })

  describe('generateProducer', () => {
    it('should generate a producer user', () => {
      const producer = UserGenerator.generateProducer()
      
      expect(producer.role).toBe('producer')
      expect(producer.avatar).toBeTruthy()
      expect(producer.bio).toBeTruthy()
    })
  })

  describe('generateTestUser', () => {
    it('should generate a basic test user', () => {
      const testUser = UserGenerator.generateTestUser()
      
      expect(testUser.role).toBe('user')
      expect(testUser.status).toBe('active')
      expect(testUser.isVerified).toBe(false)
      expect(testUser.emailVerified).toBe(true)
    })
  })

  describe('generateRelationships', () => {
    it('should generate relationships between users', () => {
      const users = UserGenerator.generateBatch(10)
      const relationships = UserGenerator.generateRelationships(users)
      
      expect(relationships.size).toBe(users.length)
      
      users.forEach(user => {
        const userRelations = relationships.get(user.id)
        expect(userRelations).toBeDefined()
        expect(Array.isArray(userRelations!.followers)).toBe(true)
        expect(Array.isArray(userRelations!.following)).toBe(true)
        expect(Array.isArray(userRelations!.blockedUsers)).toBe(true)
        expect(Array.isArray(userRelations!.mutedUsers)).toBe(true)
      })
    })
  })

  describe('cache management', () => {
    it('should cache generated users', () => {
      const user = UserGenerator.generate()
      const cachedUser = UserGenerator.getGeneratedUser(user.id)
      
      expect(cachedUser).toEqual(user)
    })

    it('should return all generated users', () => {
      const users = UserGenerator.generateBatch(3)
      const allUsers = UserGenerator.getAllGeneratedUsers()
      
      expect(allUsers).toHaveLength(3)
      expect(allUsers).toEqual(expect.arrayContaining(users))
    })

    it('should clear cache', () => {
      UserGenerator.generateBatch(3)
      expect(UserGenerator.getAllGeneratedUsers()).toHaveLength(3)
      
      UserGenerator.clearCache()
      expect(UserGenerator.getAllGeneratedUsers()).toHaveLength(0)
    })
  })

  describe('statistics', () => {
    it('should provide generation statistics', () => {
      UserGenerator.generate({ role: 'admin' })
      UserGenerator.generate({ role: 'artist' })
      UserGenerator.generate({ role: 'user' })
      
      const stats = UserGenerator.getStats()
      
      expect(stats.totalUsers).toBe(3)
      expect(stats.byRole.admin).toBe(1)
      expect(stats.byRole.artist).toBe(1)
      expect(stats.byRole.user).toBe(1)
    })
  })

  describe('data validation', () => {
    it('should generate users with valid preferences', () => {
      const user = UserGenerator.generate()
      
      expect(user.preferences).toBeDefined()
      expect(['fr', 'en']).toContain(user.preferences.language)
      expect(['light', 'dark', 'system']).toContain(user.preferences.theme)
      expect(typeof user.preferences.notifications.email).toBe('boolean')
      expect(typeof user.preferences.notifications.push).toBe('boolean')
      expect(typeof user.preferences.notifications.desktop).toBe('boolean')
      expect(['public', 'friends', 'private']).toContain(user.preferences.privacy.profileVisibility)
    })

    it('should generate users with valid stats', () => {
      const user = UserGenerator.generate({ withStats: true })
      
      expect(user.stats).toBeDefined()
      expect(typeof user.stats.tracksUploaded).toBe('number')
      expect(typeof user.stats.playlistsCreated).toBe('number')
      expect(typeof user.stats.followersCount).toBe('number')
      expect(typeof user.stats.followingCount).toBe('number')
      expect(typeof user.stats.totalPlays).toBe('number')
      
      expect(user.stats.tracksUploaded).toBeGreaterThanOrEqual(0)
      expect(user.stats.playlistsCreated).toBeGreaterThanOrEqual(0)
      expect(user.stats.followersCount).toBeGreaterThanOrEqual(0)
      expect(user.stats.followingCount).toBeGreaterThanOrEqual(0)
      expect(user.stats.totalPlays).toBeGreaterThanOrEqual(0)
    })

    it('should generate users with appropriate verification status based on role', () => {
      const admin = UserGenerator.generate({ role: 'admin' })
      const user = UserGenerator.generate({ role: 'user' })
      
      // Admins should typically be verified
      expect(admin.isVerified).toBe(true)
      
      // Regular users have lower verification probability
      // We can't assert exact value due to randomness, but we can check it's boolean
      expect(typeof user.isVerified).toBe('boolean')
    })
  })
})