/**
 * In-Memory User Repository
 * 
 * Stores users in memory for demo/development purposes.
 */

import type { StoredUser, UserRepository } from '@core/identity/domain/repositories/UserRepository';
import type { Logger } from '@core/shared/domain/Logger';

export class InMemoryUserRepository implements UserRepository {
  private users: Map<string, StoredUser> = new Map();
  private emailIndex: Map<string, string> = new Map(); // email -> userId
  private readonly logger: Logger;

  constructor(logger: Logger, initialUsers: StoredUser[] = []) {
    this.logger = logger;
    this.logger.info('InMemoryUserRepository initialized.');
    for (const user of initialUsers) {
      this.users.set(user.id, user);
      this.emailIndex.set(user.email.toLowerCase(), user.id);
      this.logger.debug(`Seeded user: ${user.id} (${user.email}).`);
    }
  }

  async findByEmail(email: string): Promise<StoredUser | null> {
    this.logger.debug(`Finding user by email: ${email}`);
    try {
      const userId = this.emailIndex.get(email.toLowerCase());
      if (!userId) {
        this.logger.warn(`User with email ${email} not found.`);
        return null;
      }
      const user = this.users.get(userId) ?? null;
      if (user) {
        this.logger.info(`Found user by email: ${email}.`);
      } else {
        this.logger.warn(`User with ID ${userId} (from email index) not found.`);
      }
      return user;
    } catch (error) {
      this.logger.error(`Error finding user by email ${email}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async findById(id: string): Promise<StoredUser | null> {
    this.logger.debug(`Finding user by id: ${id}`);
    try {
      const user = this.users.get(id) ?? null;
      if (user) {
        this.logger.info(`Found user: ${id}.`);
      } else {
        this.logger.warn(`User with id ${id} not found.`);
      }
      return user;
    } catch (error) {
      this.logger.error(`Error finding user by id ${id}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async create(user: StoredUser): Promise<StoredUser> {
    this.logger.debug(`Creating user: ${user.id} with email: ${user.email}`);
    try {
      if (this.emailIndex.has(user.email.toLowerCase())) {
        this.logger.warn(`Email ${user.email} already exists.`);
        throw new Error('Email already exists');
      }
      this.users.set(user.id, user);
      this.emailIndex.set(user.email.toLowerCase(), user.id);
      this.logger.info(`User ${user.id} (${user.email}) created successfully.`);
      return user;
    } catch (error) {
      this.logger.error(`Error creating user ${user.id} (${user.email}):`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async update(user: StoredUser): Promise<StoredUser> {
    this.logger.debug(`Updating user: ${user.id} with email: ${user.email}`);
    try {
      const existing = this.users.get(user.id);
      if (!existing) {
        this.logger.warn(`User with ID ${user.id} not found for update.`);
        throw new Error('User not found');
      }
      // If email changed, update index
      if (existing.email.toLowerCase() !== user.email.toLowerCase()) {
        if (this.emailIndex.has(user.email.toLowerCase()) && this.emailIndex.get(user.email.toLowerCase()) !== user.id) {
          this.logger.warn(`Cannot update user ${user.id} to email ${user.email} as it's already taken.`);
          throw new Error('Email already exists for another user');
        }
        this.logger.debug(`Updating email index from ${existing.email} to ${user.email}.`);
        this.emailIndex.delete(existing.email.toLowerCase());
        this.emailIndex.set(user.email.toLowerCase(), user.id);
      }
      this.users.set(user.id, user);
      this.logger.info(`User ${user.id} (${user.email}) updated successfully.`);
      return user;
    } catch (error) {
      this.logger.error(`Error updating user ${user.id} (${user.email}):`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }

  async emailExists(email: string): Promise<boolean> {
    this.logger.debug(`Checking existence of email: ${email}`);
    try {
      const exists = this.emailIndex.has(email.toLowerCase());
      this.logger.debug(`Email ${email} exists: ${exists}.`);
      return exists;
    } catch (error) {
      this.logger.error(`Error checking existence of email ${email}:`, error instanceof Error ? error : new Error(String(error)));
      throw error;
    }
  }
}