import { User } from '@core/identity/domain/entities/User';
import { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import { IUserRepository, StoredUser } from '@core/identity/domain/repositories/IUserRepository';
import { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';

import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress';
import { Logger } from '@core/shared/application';

export class InMemoryAuthRepository implements IAuthRepository {
  constructor(
    private readonly userRepository: IUserRepository,
    private readonly passwordHashingService: IPasswordHashingService,
    private readonly logger: Logger,
  ) {}

  async findByEmail(email: EmailAddress): Promise<User | null> {
    this.logger.debug(`[InMemoryAuthRepository] Finding user by email: ${email.value}`);
    const storedUser = await this.userRepository.findByEmail(email.value);
    if (!storedUser) {
      return null;
    }
    return User.fromStored(storedUser);
  }

  async save(user: User): Promise<void> {
    this.logger.debug(`[InMemoryAuthRepository] Saving user with email: ${user.getEmail()}`);

    const userId = user.getId().value;
    const userEmail = user.getEmail() ?? '';
    const userDisplayName = user.getDisplayName();
    const userPasswordHash = user.getPasswordHash()?.value ?? '';

    const storedUser: StoredUser = {
      id: userId,
      email: userEmail,
      passwordHash: userPasswordHash,
      displayName: userDisplayName,
      salt: '', // In-memory doesn't use actual salt, but Stub has to provide.
      primaryDriverId: user.getPrimaryDriverId() ?? undefined,
      createdAt: new Date(),
    };

    // Check if user exists to decide between create or update
    const existingStoredUser = await this.userRepository.findById(userId);
    if (existingStoredUser) {
      // For update, ensure createdAt is preserved from existing user
      const updatedStoredUser: StoredUser = {
        ...storedUser,
        createdAt: existingStoredUser.createdAt,
      };
      await this.userRepository.update(updatedStoredUser);
    } else {
      await this.userRepository.create(storedUser);
    }
  }

  async create(user: User, passwordPlain: string): Promise<User> {
    this.logger.debug(`[InMemoryAuthRepository] Creating user with email: ${user.getEmail()}`);
    const passwordHashValue = await this.passwordHashingService.hash(passwordPlain);
    
    // Ensure email is not undefined before using
    const userEmail = user.getEmail() ?? ''; 
    const userDisplayName = user.getDisplayName();
    const userId = user.getId().value;

    const storedUser: StoredUser = {
      id: userId,
      email: userEmail,
      passwordHash: passwordHashValue,
      displayName: userDisplayName,
      salt: '', // Salt is handled by PasswordHashingService, but StoredUser requires it.
      primaryDriverId: user.getPrimaryDriverId() ?? undefined,
      createdAt: new Date(),
    };
    const createdStoredUser = await this.userRepository.create(storedUser);
    return User.fromStored(createdStoredUser);
  }

  async update(user: User): Promise<User> {
    this.logger.debug(`[InMemoryAuthRepository] Updating user with email: ${user.getEmail()}`);
    // Ensure values are not undefined before using
    const userId = user.getId().value;
    const userEmail = user.getEmail() ?? '';
    const userPasswordHash = user.getPasswordHash()?.value ?? '';
    const userDisplayName = user.getDisplayName();

    const storedUser: StoredUser = {
      id: userId,
      email: userEmail,
      passwordHash: userPasswordHash,
      displayName: userDisplayName,
      salt: '', // Salt is handled by PasswordHashingService, but StoredUser requires it.
      primaryDriverId: user.getPrimaryDriverId() ?? undefined,
      createdAt: new Date(), // Assuming creation date is maintained or updated within UserRepository
    };
    const updatedStoredUser = await this.userRepository.update(storedUser);
    return User.fromStored(updatedStoredUser);
  }

  async delete(userId: string): Promise<boolean> {
    // Deletion needs to be implemented in InMemoryUserRepository and exposed via IUserRepository
    // For now, it's not supported.
    this.logger.warn(`[InMemoryAuthRepository] Delete operation not implemented for user: ${userId}`);
    return false;
  }

  async userExists(email: string): Promise<boolean> {
    this.logger.debug(`[InMemoryAuthRepository] Checking if user exists for email: ${email}`);
    return this.userRepository.emailExists(email);
  }

  async verifyPassword(email: string, passwordPlain: string): Promise<User | null> {
    this.logger.debug(`[InMemoryAuthRepository] Verifying password for user with email: ${email}`);
    const user = await this.findByEmail(EmailAddress.create(email)); // Use EmailAddress value object
    if (!user) {
      this.logger.warn(`[InMemoryAuthRepository] User not found for email: ${email}`);
      return null;
    }
    const isPasswordValid = await this.passwordHashingService.verify(
      passwordPlain,
      user.getPasswordHash()?.value ?? '', // Access value and provide fallback
    );
    if (!isPasswordValid) {
      this.logger.warn(`[InMemoryAuthRepository] Invalid password for user with email: ${email}`);
      return null;
    }
    return user;
  }
}
