import { AdminUser } from '../../domain/entities/AdminUser';
import { AdminUserRepository, StoredAdminUser, UserFilter, UserListQuery, UserListResult } from '../../domain/repositories/AdminUserRepository';
import { Email } from '../../domain/value-objects/Email';
import { UserId } from '../../domain/value-objects/UserId';

/**
 * In-memory implementation of AdminUserRepository for testing and development
 * Follows TDD - created with tests first
 */
export class InMemoryAdminUserRepository implements AdminUserRepository {
  private storage: Map<string, StoredAdminUser> = new Map();

  async findById(id: UserId): Promise<AdminUser | null> {
    const stored = this.storage.get(id.value);
    return stored ? this.fromStored(stored) : null;
  }

  async findByEmail(email: Email): Promise<AdminUser | null> {
    for (const stored of this.storage.values()) {
      if (stored.email === email.value) {
        return this.fromStored(stored);
      }
    }
    return null;
  }

  async emailExists(email: Email): Promise<boolean> {
    for (const stored of this.storage.values()) {
      if (stored.email === email.value) {
        return true;
      }
    }
    return false;
  }

  async existsById(id: UserId): Promise<boolean> {
    return this.storage.has(id.value);
  }

  async existsByEmail(email: Email): Promise<boolean> {
    return this.emailExists(email);
  }

  async list(query?: UserListQuery): Promise<UserListResult> {
    let users: AdminUser[] = [];

    // Get all users
    for (const stored of this.storage.values()) {
      users.push(this.fromStored(stored));
    }

    // Apply filters
    if (query?.filter) {
      users = users.filter(user => {
        if (query.filter?.role && !user.roles.some(r => r.equals(query.filter!.role!))) {
          return false;
        }
        if (query.filter?.status && !user.status.equals(query.filter.status)) {
          return false;
        }
        if (query.filter?.email && !user.email.equals(query.filter.email)) {
          return false;
        }
        if (query.filter?.search) {
          const search = query.filter.search.toLowerCase();
          const matchesEmail = user.email.value.toLowerCase().includes(search);
          const matchesDisplayName = user.displayName.toLowerCase().includes(search);
          if (!matchesEmail && !matchesDisplayName) {
            return false;
          }
        }
        return true;
      });
    }

    const total = users.length;

    // Apply sorting
    if (query?.sort) {
      const { field, direction } = query.sort;
      users.sort((a, b) => {
        let aVal: string | number | Date;
        let bVal: string | number | Date;
        
        switch (field) {
          case 'email':
            aVal = a.email.value;
            bVal = b.email.value;
            break;
          case 'displayName':
            aVal = a.displayName;
            bVal = b.displayName;
            break;
          case 'createdAt':
            aVal = a.createdAt;
            bVal = b.createdAt;
            break;
          case 'lastLoginAt':
            aVal = a.lastLoginAt || 0;
            bVal = b.lastLoginAt || 0;
            break;
          case 'status':
            aVal = a.status.value;
            bVal = b.status.value;
            break;
          default:
            return 0;
        }

        if (aVal < bVal) return direction === 'asc' ? -1 : 1;
        if (aVal > bVal) return direction === 'asc' ? 1 : -1;
        return 0;
      });
    }

    // Apply pagination
    const page = query?.pagination?.page || 1;
    const limit = query?.pagination?.limit || 10;
    const totalPages = Math.ceil(total / limit);
    const start = (page - 1) * limit;
    const end = start + limit;
    const paginatedUsers = users.slice(start, end);

    return {
      users: paginatedUsers,
      total,
      page,
      limit,
      totalPages,
    };
  }

  async count(filter?: UserFilter): Promise<number> {
    let count = 0;

    for (const stored of this.storage.values()) {
      const user = this.fromStored(stored);
      
      if (filter?.role && !user.roles.some(r => r.equals(filter.role!))) {
        continue;
      }
      if (filter?.status && !user.status.equals(filter.status)) {
        continue;
      }
      if (filter?.email && !user.email.equals(filter.email)) {
        continue;
      }
      if (filter?.search) {
        const search = filter.search.toLowerCase();
        const matchesEmail = user.email.value.toLowerCase().includes(search);
        const matchesDisplayName = user.displayName.toLowerCase().includes(search);
        if (!matchesEmail && !matchesDisplayName) {
          continue;
        }
      }

      count++;
    }

    return count;
  }

  async create(user: AdminUser): Promise<AdminUser> {
    // Check for duplicate email
    if (await this.emailExists(user.email)) {
      throw new Error('Email already exists');
    }
    
    // Check for duplicate ID
    if (this.storage.has(user.id.value)) {
      throw new Error('User ID already exists');
    }
    
    const stored = this.toStored(user);
    this.storage.set(user.id.value, stored);
    return user;
  }

  async update(user: AdminUser): Promise<AdminUser> {
    // Check if user exists
    if (!this.storage.has(user.id.value)) {
      throw new Error('User not found');
    }
    
    // Check for duplicate email (excluding current user)
    for (const [id, stored] of this.storage.entries()) {
      if (id !== user.id.value && stored.email === user.email.value) {
        throw new Error('Email already exists');
      }
    }
    
    const stored = this.toStored(user);
    this.storage.set(user.id.value, stored);
    return user;
  }

  async delete(id: UserId): Promise<void> {
    if (!this.storage.has(id.value)) {
      throw new Error('User not found');
    }
    this.storage.delete(id.value);
  }

  toStored(user: AdminUser): StoredAdminUser {
    const stored: StoredAdminUser = {
      id: user.id.value,
      email: user.email.value,
      roles: user.roles.map(r => r.value),
      status: user.status.value,
      displayName: user.displayName,
      createdAt: user.createdAt,
      updatedAt: user.updatedAt,
    };

    if (user.lastLoginAt !== undefined) {
      stored.lastLoginAt = user.lastLoginAt;
    }

    if (user.primaryDriverId !== undefined) {
      stored.primaryDriverId = user.primaryDriverId;
    }

    return stored;
  }

  fromStored(stored: StoredAdminUser): AdminUser {
    const props: {
      id: string;
      email: string;
      roles: string[];
      status: string;
      displayName: string;
      createdAt: Date;
      updatedAt: Date;
      lastLoginAt?: Date;
      primaryDriverId?: string;
    } = {
      id: stored.id,
      email: stored.email,
      roles: stored.roles,
      status: stored.status,
      displayName: stored.displayName,
      createdAt: stored.createdAt,
      updatedAt: stored.updatedAt,
    };

    if (stored.lastLoginAt !== undefined) {
      props.lastLoginAt = stored.lastLoginAt;
    }

    if (stored.primaryDriverId !== undefined) {
      props.primaryDriverId = stored.primaryDriverId;
    }

    return AdminUser.rehydrate(props);
  }
}