import { v4 as uuidv4 } from 'uuid'
import { vezaFaker } from '../utils/faker-config'
import { globalConfig } from '../config'
import type { 
  User, 
  UserRole, 
  UserStatus 
} from '../schemas/database'

/**
 * User generation options
 */
export interface UserGenerationOptions {
  role?: UserRole
  status?: UserStatus
  isVerified?: boolean
  emailVerified?: boolean
  withAvatar?: boolean
  withBio?: boolean
  withStats?: boolean
  createdAt?: Date
  lastSeenAt?: Date
}

/**
 * User relationship data
 */
export interface UserRelations {
  followers: string[]
  following: string[]
  blockedUsers: string[]
  mutedUsers: string[]
}

/**
 * Advanced user generator for Veza Platform
 */
export class UserGenerator {
  private static generatedUsers: Map<string, User> = new Map()
  private static usedUsernames: Set<string> = new Set()
  private static usedEmails: Set<string> = new Set()

  /**
   * Generate a single user with realistic data
   */
  static generate(options: UserGenerationOptions = {}): User {
    const createdAt = options.createdAt || vezaFaker.date.userCreation()
    const firstName = vezaFaker.person.firstName()
    const lastName = vezaFaker.person.lastName()
    
    // Ensure unique username and email
    let username = vezaFaker.user.username(firstName, lastName)
    let attempts = 0
    while (this.usedUsernames.has(username) && attempts < 10) {
      username = vezaFaker.user.username(firstName, lastName)
      attempts++
    }
    this.usedUsernames.add(username)

    let email = `${username}@${vezaFaker.internet.domainName()}`
    attempts = 0
    while (this.usedEmails.has(email) && attempts < 10) {
      email = `${username}${vezaFaker.number.int({ min: 1, max: 999 })}@${vezaFaker.internet.domainName()}`
      attempts++
    }
    this.usedEmails.add(email)

    const role = options.role || this.generateWeightedRole()
    const status = options.status || 'active'
    const isVerified = options.isVerified ?? this.shouldBeVerified(role)
    const stats = options.withStats !== false ? vezaFaker.stats.userStats(role, createdAt) : {
      tracksUploaded: 0,
      playlistsCreated: 0,
      followersCount: 0,
      followingCount: 0,
      totalPlays: 0,
    }

    const user: User = {
      id: uuidv4(),
      username,
      email,
      firstName,
      lastName,
      displayName: options.withBio !== false ? `${firstName} ${lastName}` : undefined,
      role,
      status,
      avatar: options.withAvatar !== false ? vezaFaker.image.avatar() : undefined,
      bio: options.withBio !== false ? vezaFaker.user.bio(role) : undefined,
      location: vezaFaker.user.location(),
      website: role === 'artist' || role === 'producer' ? vezaFaker.internet.url() : undefined,
      isVerified,
      emailVerified: options.emailVerified ?? vezaFaker.datatype.boolean({ probability: 0.8 }),
      preferences: {
        language: vezaFaker.helpers.arrayElement(['fr', 'en']),
        theme: vezaFaker.helpers.arrayElement(['light', 'dark', 'system']),
        notifications: {
          email: vezaFaker.datatype.boolean({ probability: 0.7 }),
          push: vezaFaker.datatype.boolean({ probability: 0.8 }),
          desktop: vezaFaker.datatype.boolean({ probability: 0.3 }),
        },
        privacy: {
          profileVisibility: vezaFaker.helpers.arrayElement(['public', 'friends', 'private']),
          showOnlineStatus: vezaFaker.datatype.boolean({ probability: 0.6 }),
          allowDirectMessages: vezaFaker.datatype.boolean({ probability: 0.9 }),
        },
      },
      stats,
      lastSeenAt: options.lastSeenAt || (status === 'active' ? vezaFaker.date.recentActivity() : undefined),
      createdAt,
      updatedAt: vezaFaker.date.between({ from: createdAt, to: new Date() }),
    }

    this.generatedUsers.set(user.id, user)
    return user
  }

  /**
   * Generate multiple users with distribution
   */
  static generateBatch(count: number, options: UserGenerationOptions = {}): User[] {
    const users: User[] = []
    
    for (let i = 0; i < count; i++) {
      users.push(this.generate(options))
    }
    
    return users
  }

  /**
   * Generate users with role distribution
   */
  static generateWithDistribution(): User[] {
    const config = globalConfig.generation.users
    const users: User[] = []

    // Generate admins
    for (let i = 0; i < config.adminCount; i++) {
      users.push(this.generate({
        role: 'admin',
        isVerified: true,
        emailVerified: true,
        withAvatar: true,
        withBio: true,
        status: 'active'
      }))
    }

    // Generate artists
    for (let i = 0; i < config.artistCount; i++) {
      users.push(this.generate({
        role: 'artist',
        isVerified: vezaFaker.datatype.boolean({ probability: 0.6 }),
        withAvatar: true,
        withBio: true,
      }))
    }

    // Generate producers
    for (let i = 0; i < config.producerCount; i++) {
      users.push(this.generate({
        role: 'producer',
        isVerified: vezaFaker.datatype.boolean({ probability: 0.7 }),
        withAvatar: true,
        withBio: true,
      }))
    }

    // Generate moderators (small number)
    const moderatorCount = Math.max(1, Math.floor(config.count * 0.02))
    for (let i = 0; i < moderatorCount; i++) {
      users.push(this.generate({
        role: 'moderator',
        isVerified: true,
        emailVerified: true,
        withAvatar: true,
        withBio: true,
        status: 'active'
      }))
    }

    // Generate regular users
    const regularUserCount = config.count - users.length
    for (let i = 0; i < regularUserCount; i++) {
      users.push(this.generate({
        role: 'user',
        withAvatar: vezaFaker.datatype.boolean({ probability: 0.4 }),
        withBio: vezaFaker.datatype.boolean({ probability: 0.3 }),
      }))
    }

    return users
  }

  /**
   * Generate specific user types
   */
  static generateAdmin(options: Partial<UserGenerationOptions> = {}): User {
    return this.generate({
      ...options,
      role: 'admin',
      status: 'active',
      isVerified: true,
      emailVerified: true,
      withAvatar: true,
      withBio: true,
    })
  }

  static generateArtist(options: Partial<UserGenerationOptions> = {}): User {
    return this.generate({
      ...options,
      role: 'artist',
      isVerified: vezaFaker.datatype.boolean({ probability: 0.6 }),
      withAvatar: true,
      withBio: true,
    })
  }

  static generateProducer(options: Partial<UserGenerationOptions> = {}): User {
    return this.generate({
      ...options,
      role: 'producer',
      isVerified: vezaFaker.datatype.boolean({ probability: 0.7 }),
      withAvatar: true,
      withBio: true,
    })
  }

  static generateTestUser(username?: string): User {
    return this.generate({
      role: 'user',
      status: 'active',
      isVerified: false,
      emailVerified: true,
      withAvatar: false,
      withBio: false,
    })
  }

  /**
   * Generate user relationships
   */
  static generateRelationships(users: User[]): Map<string, UserRelations> {
    const relationships = new Map<string, UserRelations>()
    
    users.forEach(user => {
      const maxFollowing = Math.min(user.stats.followingCount, users.length - 1)
      const maxFollowers = Math.min(user.stats.followersCount, users.length - 1)
      
      // Generate following relationships
      const following = vezaFaker.helpers.arrayElements(
        users.filter(u => u.id !== user.id).map(u => u.id),
        Math.min(maxFollowing, 50)
      )
      
      // Generate followers (some overlap with following)
      const potentialFollowers = users
        .filter(u => u.id !== user.id && !following.includes(u.id))
        .map(u => u.id)
      
      const additionalFollowers = vezaFaker.helpers.arrayElements(
        potentialFollowers,
        Math.min(maxFollowers - following.length, potentialFollowers.length)
      )
      
      const followers = [...following.slice(0, Math.floor(following.length * 0.3)), ...additionalFollowers]
      
      relationships.set(user.id, {
        followers,
        following,
        blockedUsers: [],
        mutedUsers: []
      })
    })
    
    return relationships
  }

  /**
   * Private helper methods
   */
  private static generateWeightedRole(): UserRole {
    return vezaFaker.utils.weightedChoice([
      { item: 'user', weight: 70 },
      { item: 'artist', weight: 20 },
      { item: 'producer', weight: 8 },
      { item: 'moderator', weight: 1.5 },
      { item: 'admin', weight: 0.5 }
    ])
  }

  private static shouldBeVerified(role: UserRole): boolean {
    const verificationProbability = {
      admin: 1.0,
      moderator: 1.0,
      producer: 0.7,
      artist: 0.6,
      user: 0.1
    }
    
    return vezaFaker.datatype.boolean({ 
      probability: verificationProbability[role] 
    })
  }

  /**
   * Utility methods
   */
  static getGeneratedUser(id: string): User | undefined {
    return this.generatedUsers.get(id)
  }

  static getAllGeneratedUsers(): User[] {
    return Array.from(this.generatedUsers.values())
  }

  static clearCache(): void {
    this.generatedUsers.clear()
    this.usedUsernames.clear()
    this.usedEmails.clear()
  }

  static getStats(): { totalUsers: number; byRole: Record<UserRole, number> } {
    const users = this.getAllGeneratedUsers()
    const byRole: Record<UserRole, number> = {
      admin: 0,
      moderator: 0,
      producer: 0,
      artist: 0,
      user: 0
    }

    users.forEach(user => {
      byRole[user.role]++
    })

    return {
      totalUsers: users.length,
      byRole
    }
  }
}