/**
 * Signup with Email Use Case
 * 
 * Creates a new user account with email and password.
 */

import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
import type { AuthenticatedUserDTO } from '../dto/AuthenticatedUserDTO';
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';

export interface SignupCommandDTO {
  email: string;
  password: string;
  displayName: string;
}

export interface SignupResultDTO {
  session: AuthSessionDTO;
  isNewUser: boolean;
}

export class SignupWithEmailUseCase {
  constructor(
    private readonly userRepository: IUserRepository,
    private readonly sessionPort: IdentitySessionPort,
  ) {}

  async execute(command: SignupCommandDTO): Promise<SignupResultDTO> {
    // Validate email format
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(command.email)) {
      throw new Error('Invalid email format');
    }

    // Validate password strength
    if (command.password.length < 8) {
      throw new Error('Password must be at least 8 characters');
    }

    // Validate display name
    if (!command.displayName || command.displayName.trim().length < 2) {
      throw new Error('Display name must be at least 2 characters');
    }

    // Check if email already exists
    const existingUser = await this.userRepository.findByEmail(command.email);
    if (existingUser) {
      throw new Error('An account with this email already exists');
    }

    // Hash password (simple hash for demo - in production use bcrypt)
    const salt = this.generateSalt();
    const passwordHash = await this.hashPassword(command.password, salt);

    // Create user
    const userId = this.generateUserId();
    const newUser: StoredUser = {
      id: userId,
      email: command.email.toLowerCase().trim(),
      displayName: command.displayName.trim(),
      passwordHash,
      salt,
      createdAt: new Date(),
    };

    await this.userRepository.create(newUser);

    // Create session
    const authenticatedUser: AuthenticatedUserDTO = {
      id: newUser.id,
      displayName: newUser.displayName,
      email: newUser.email,
    };

    const session = await this.sessionPort.createSession(authenticatedUser);

    return {
      session,
      isNewUser: true,
    };
  }

  private generateSalt(): string {
    const array = new Uint8Array(16);
    if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
      crypto.getRandomValues(array);
    } else {
      for (let i = 0; i < array.length; i++) {
        array[i] = Math.floor(Math.random() * 256);
      }
    }
    return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
  }

  private async hashPassword(password: string, salt: string): Promise<string> {
    // Simple hash for demo - in production, use bcrypt or argon2
    const data = password + salt;
    if (typeof crypto !== 'undefined' && crypto.subtle) {
      const encoder = new TextEncoder();
      const dataBuffer = encoder.encode(data);
      const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer);
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }
    // Fallback for environments without crypto.subtle
    let hash = 0;
    for (let i = 0; i < data.length; i++) {
      const char = data.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash;
    }
    return Math.abs(hash).toString(16).padStart(16, '0');
  }

  private generateUserId(): string {
    if (typeof crypto !== 'undefined' && crypto.randomUUID) {
      return crypto.randomUUID();
    }
    return 'user-' + Date.now().toString(36) + Math.random().toString(36).substr(2, 9);
  }
}