import type { EmailValidationResult } from '../types/EmailAddress';
import { validateEmail } from '../types/EmailAddress';
import { UserId } from '../value-objects/UserId';
import { PasswordHash } from '../value-objects/PasswordHash';
import { StoredUser } from '../repositories/IUserRepository';

export interface UserProps {
  id: UserId;
  displayName: string;
  email?: string;
  passwordHash?: PasswordHash;
  iracingCustomerId?: string;
  primaryDriverId?: string;
  avatarUrl?: string;
}

export class User {
  private readonly id: UserId;
  private displayName: string;
  private email: string | undefined;
  private passwordHash: PasswordHash | undefined;
  private iracingCustomerId: string | undefined;
  private primaryDriverId: string | undefined;
  private avatarUrl: string | undefined;

  private constructor(props: UserProps) {
    this.validateDisplayName(props.displayName);

    this.id = props.id;
    this.displayName = this.formatDisplayName(props.displayName);
    this.email = props.email;
    this.passwordHash = props.passwordHash;
    this.iracingCustomerId = props.iracingCustomerId;
    this.primaryDriverId = props.primaryDriverId;
    this.avatarUrl = props.avatarUrl;
  }

  private validateDisplayName(displayName: string): void {
    const trimmed = displayName?.trim();
    
    if (!trimmed) {
      throw new Error('Display name cannot be empty');
    }

    if (trimmed.length < 2) {
      throw new Error('Name must be at least 2 characters long');
    }

    if (trimmed.length > 50) {
      throw new Error('Name must be no more than 50 characters');
    }

    // Only allow letters, spaces, hyphens, and apostrophes
    if (!/^[A-Za-z\s\-']+$/.test(trimmed)) {
      throw new Error('Name can only contain letters, spaces, hyphens, and apostrophes');
    }

    // Block common nickname patterns
    const blockedPatterns = [
      /^user/i,
      /^test/i,
      /^demo/i,
      /^[a-z0-9_]+$/i, // No alphanumeric-only (likely username/nickname)
      /^guest/i,
      /^player/i
    ];

    if (blockedPatterns.some(pattern => pattern.test(trimmed))) {
      throw new Error('Please use your real name (first and last name), not a nickname or username');
    }

    // Check for excessive spaces or repeated characters
    if (/\s{2,}/.test(trimmed)) {
      throw new Error('Name cannot contain multiple consecutive spaces');
    }

    if (/(.)\1{2,}/.test(trimmed)) {
      throw new Error('Name cannot contain excessive repeated characters');
    }
  }

  private formatDisplayName(displayName: string): string {
    const trimmed = displayName.trim();
    // Capitalize first letter of each word
    return trimmed.replace(/\b\w/g, char => char.toUpperCase());
  }

  public static create(props: UserProps): User {
    if (props.email) {
      const result: EmailValidationResult = validateEmail(props.email);
      if (!result.success) {
        throw new Error(result.error);
      }
      return new User({
        ...props,
        email: result.email,
      });
    }

    return new User(props);
  }

  public static rehydrate(props: {
    id: string;
    displayName: string;
    email?: string;
    passwordHash?: PasswordHash;
    iracingCustomerId?: string;
    primaryDriverId?: string;
    avatarUrl?: string;
  }): User {
    const email =
      props.email !== undefined
        ? (() => {
            const result: EmailValidationResult = validateEmail(props.email);
            if (!result.success) {
              throw new Error(result.error);
            }
            return result.email;
          })()
        : undefined;

    return new User({
      id: UserId.fromString(props.id),
      displayName: props.displayName,
      ...(email !== undefined ? { email } : {}),
      ...(props.passwordHash !== undefined ? { passwordHash: props.passwordHash } : {}),
      ...(props.iracingCustomerId !== undefined ? { iracingCustomerId: props.iracingCustomerId } : {}),
      ...(props.primaryDriverId !== undefined ? { primaryDriverId: props.primaryDriverId } : {}),
      ...(props.avatarUrl !== undefined ? { avatarUrl: props.avatarUrl } : {}),
    });
  }

  public static fromStored(stored: StoredUser): User {
    const passwordHash = stored.passwordHash
      ? PasswordHash.fromHash(stored.passwordHash)
      : undefined;

    const userProps: UserProps = {
      id: UserId.fromString(stored.id),
      displayName: stored.displayName,
      ...(stored.email !== undefined ? { email: stored.email } : {}),
      ...(passwordHash !== undefined ? { passwordHash } : {}),
      ...(stored.primaryDriverId !== undefined
        ? { primaryDriverId: stored.primaryDriverId }
        : {}),
    };

    return new User(userProps);
  }

  public getId(): UserId {
    return this.id;
  }

  public getDisplayName(): string {
    return this.displayName;
  }

  public getEmail(): string | undefined {
    return this.email;
  }

  public getPasswordHash(): PasswordHash | undefined {
    return this.passwordHash;
  }

  public getIracingCustomerId(): string | undefined {
    return this.iracingCustomerId;
  }

  public getPrimaryDriverId(): string | undefined {
    return this.primaryDriverId;
  }

  public getAvatarUrl(): string | undefined {
    return this.avatarUrl;
  }

  /**
   * Update display name - NOT ALLOWED after initial creation
   * This method will always throw an error to enforce immutability
   */
  public updateDisplayName(): void {
    throw new Error('Display name cannot be changed after account creation. Please contact support if you need to update your name.');
  }

  /**
   * Check if this user was created with a valid real name
   * Used to verify immutability for existing users
   */
  public hasImmutableName(): boolean {
    // All users created through proper channels have immutable names
    return true;
  }
}