/**
 * User Entity
 *
 * Represents a user account in the Xpeditis platform.
 *
 * Business Rules:
 * - Email must be valid and unique
 * - Password must meet complexity requirements (enforced at application layer)
 * - Users belong to an organization
 * - Role-based access control (Admin, Manager, User, Viewer)
 */

import { DEFAULT_LOCALE, Locale } from '../value-objects/locale.vo';

export enum UserRole {
  ADMIN = 'ADMIN', // Full system access
  MANAGER = 'MANAGER', // Manage bookings and users within organization
  USER = 'USER', // Create and view bookings
  VIEWER = 'VIEWER', // Read-only access
}

export interface UserProps {
  id: string;
  organizationId: string;
  email: string;
  passwordHash: string;
  role: UserRole;
  firstName: string;
  lastName: string;
  phoneNumber?: string;
  totpSecret?: string; // For 2FA
  isEmailVerified: boolean;
  isActive: boolean;
  lastLoginAt?: Date;
  preferredLanguage: Locale;
  createdAt: Date;
  updatedAt: Date;
}

export class User {
  private readonly props: UserProps;

  private constructor(props: UserProps) {
    this.props = props;
  }

  /**
   * Factory method to create a new User
   */
  static create(
    props: Omit<
      UserProps,
      | 'createdAt'
      | 'updatedAt'
      | 'isEmailVerified'
      | 'isActive'
      | 'lastLoginAt'
      | 'preferredLanguage'
    > & { preferredLanguage?: Locale }
  ): User {
    const now = new Date();

    // Validate email format (basic validation)
    if (!User.isValidEmail(props.email)) {
      throw new Error('Invalid email format.');
    }

    return new User({
      ...props,
      preferredLanguage: props.preferredLanguage ?? DEFAULT_LOCALE,
      isEmailVerified: false,
      isActive: true,
      createdAt: now,
      updatedAt: now,
    });
  }

  /**
   * Factory method to reconstitute from persistence
   */
  static fromPersistence(props: UserProps): User {
    return new User(props);
  }

  /**
   * Validate email format
   */
  private static isValidEmail(email: string): boolean {
    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailPattern.test(email);
  }

  // Getters
  get id(): string {
    return this.props.id;
  }

  get organizationId(): string {
    return this.props.organizationId;
  }

  get email(): string {
    return this.props.email;
  }

  get passwordHash(): string {
    return this.props.passwordHash;
  }

  get role(): UserRole {
    return this.props.role;
  }

  get firstName(): string {
    return this.props.firstName;
  }

  get lastName(): string {
    return this.props.lastName;
  }

  get fullName(): string {
    return `${this.props.firstName} ${this.props.lastName}`;
  }

  get phoneNumber(): string | undefined {
    return this.props.phoneNumber;
  }

  get totpSecret(): string | undefined {
    return this.props.totpSecret;
  }

  get isEmailVerified(): boolean {
    return this.props.isEmailVerified;
  }

  get isActive(): boolean {
    return this.props.isActive;
  }

  get lastLoginAt(): Date | undefined {
    return this.props.lastLoginAt;
  }

  get createdAt(): Date {
    return this.props.createdAt;
  }

  get updatedAt(): Date {
    return this.props.updatedAt;
  }

  get preferredLanguage(): Locale {
    return this.props.preferredLanguage;
  }

  updatePreferredLanguage(locale: Locale): void {
    this.props.preferredLanguage = locale;
    this.props.updatedAt = new Date();
  }

  // Business methods
  has2FAEnabled(): boolean {
    return !!this.props.totpSecret;
  }

  isAdmin(): boolean {
    return this.props.role === UserRole.ADMIN;
  }

  isManager(): boolean {
    return this.props.role === UserRole.MANAGER;
  }

  isRegularUser(): boolean {
    return this.props.role === UserRole.USER;
  }

  isViewer(): boolean {
    return this.props.role === UserRole.VIEWER;
  }

  canManageUsers(): boolean {
    return this.props.role === UserRole.ADMIN || this.props.role === UserRole.MANAGER;
  }

  canCreateBookings(): boolean {
    return (
      this.props.role === UserRole.ADMIN ||
      this.props.role === UserRole.MANAGER ||
      this.props.role === UserRole.USER
    );
  }

  updatePassword(newPasswordHash: string): void {
    this.props.passwordHash = newPasswordHash;
    this.props.updatedAt = new Date();
  }

  updateRole(newRole: UserRole): void {
    this.props.role = newRole;
    this.props.updatedAt = new Date();
  }

  updateFirstName(firstName: string): void {
    if (!firstName || firstName.trim().length === 0) {
      throw new Error('First name cannot be empty.');
    }
    this.props.firstName = firstName.trim();
    this.props.updatedAt = new Date();
  }

  updateLastName(lastName: string): void {
    if (!lastName || lastName.trim().length === 0) {
      throw new Error('Last name cannot be empty.');
    }
    this.props.lastName = lastName.trim();
    this.props.updatedAt = new Date();
  }

  updateProfile(firstName: string, lastName: string, phoneNumber?: string): void {
    if (!firstName || firstName.trim().length === 0) {
      throw new Error('First name cannot be empty.');
    }
    if (!lastName || lastName.trim().length === 0) {
      throw new Error('Last name cannot be empty.');
    }

    this.props.firstName = firstName.trim();
    this.props.lastName = lastName.trim();
    this.props.phoneNumber = phoneNumber;
    this.props.updatedAt = new Date();
  }

  verifyEmail(): void {
    this.props.isEmailVerified = true;
    this.props.updatedAt = new Date();
  }

  enable2FA(totpSecret: string): void {
    this.props.totpSecret = totpSecret;
    this.props.updatedAt = new Date();
  }

  disable2FA(): void {
    this.props.totpSecret = undefined;
    this.props.updatedAt = new Date();
  }

  recordLogin(): void {
    this.props.lastLoginAt = new Date();
  }

  deactivate(): void {
    this.props.isActive = false;
    this.props.updatedAt = new Date();
  }

  activate(): void {
    this.props.isActive = true;
    this.props.updatedAt = new Date();
  }

  /**
   * Convert to plain object for persistence
   */
  toObject(): UserProps {
    return { ...this.props };
  }
}
