import { AdminUser } from '@core/admin/domain/entities/AdminUser';
import type { AdminUserRepository, StoredAdminUser, UserFilter, UserListQuery, UserListResult } from '@core/admin/domain/repositories/AdminUserRepository';
import { Email } from '@core/admin/domain/value-objects/Email';
import { UserId } from '@core/admin/domain/value-objects/UserId';
import type { DataSource, Repository } from 'typeorm';
import { AdminUserOrmEntity } from '../entities/AdminUserOrmEntity';
import { AdminUserOrmMapper } from '../mappers/AdminUserOrmMapper';

export class TypeOrmAdminUserRepository implements AdminUserRepository {
  private readonly repository: Repository<AdminUserOrmEntity>;
  private readonly mapper: AdminUserOrmMapper;

  constructor(
    dataSource: DataSource,
    mapper?: AdminUserOrmMapper,
  ) {
    this.repository = dataSource.getRepository(AdminUserOrmEntity);
    this.mapper = mapper ?? new AdminUserOrmMapper();
  }

  async save(user: AdminUser): Promise<void> {
    const entity = this.mapper.toOrmEntity(user);
    await this.repository.save(entity);
  }

  async findById(id: UserId): Promise<AdminUser | null> {
    const entity = await this.repository.findOne({ where: { id: id.value } });
    return entity ? this.mapper.toDomain(entity) : null;
  }

  async findByEmail(email: Email): Promise<AdminUser | null> {
    const entity = await this.repository.findOne({ where: { email: email.value } });
    return entity ? this.mapper.toDomain(entity) : null;
  }

  async emailExists(email: Email): Promise<boolean> {
    const count = await this.repository.count({ where: { email: email.value } });
    return count > 0;
  }

  async existsById(id: UserId): Promise<boolean> {
    const count = await this.repository.count({ where: { id: id.value } });
    return count > 0;
  }

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

  async list(query?: UserListQuery): Promise<UserListResult> {
    const page = query?.pagination?.page ?? 1;
    const limit = query?.pagination?.limit ?? 10;
    const skip = (page - 1) * limit;
    const sortBy = query?.sort?.field ?? 'createdAt';
    const sortOrder = query?.sort?.direction ?? 'desc';

    const queryBuilder = this.repository.createQueryBuilder('adminUser');

    if (query?.filter?.role) {
      // SQLite doesn't support ANY, use LIKE for JSON array search
      queryBuilder.andWhere('adminUser.roles LIKE :rolePattern', {
        rolePattern: `%${query.filter.role.value}%`
      });
    }

    if (query?.filter?.status) {
      queryBuilder.andWhere('adminUser.status = :status', { status: query.filter.status.value });
    }

    if (query?.filter?.search) {
      const searchParam = `%${query.filter.search}%`;
      queryBuilder.andWhere(
        '(adminUser.email LIKE :search OR adminUser.displayName LIKE :search)',
        { search: searchParam }
      );
    }

    queryBuilder.skip(skip).take(limit);
    queryBuilder.orderBy(`adminUser.${sortBy}`, sortOrder.toUpperCase() as 'ASC' | 'DESC');

    const [entities, total] = await queryBuilder.getManyAndCount();

    const users = entities.map(entity => this.mapper.toDomain(entity));

    return {
      users,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async count(filter?: UserFilter): Promise<number> {
    const queryBuilder = this.repository.createQueryBuilder('adminUser');

    if (filter?.role) {
      // SQLite doesn't support ANY, use LIKE for JSON array search
      queryBuilder.andWhere('adminUser.roles LIKE :rolePattern', {
        rolePattern: `%${filter.role.value}%`
      });
    }

    if (filter?.status) {
      queryBuilder.andWhere('adminUser.status = :status', { status: filter.status.value });
    }

    if (filter?.search) {
      const searchParam = `%${filter.search}%`;
      queryBuilder.andWhere(
        '(adminUser.email LIKE :search OR adminUser.displayName LIKE :search)',
        { search: searchParam }
      );
    }

    return await queryBuilder.getCount();
  }

  async create(user: AdminUser): Promise<AdminUser> {
    const entity = this.mapper.toOrmEntity(user);
    const saved = await this.repository.save(entity);
    return this.mapper.toDomain(saved);
  }

  async update(user: AdminUser): Promise<AdminUser> {
    const entity = this.mapper.toOrmEntity(user);
    const updated = await this.repository.save(entity);
    return this.mapper.toDomain(updated);
  }

  async delete(id: UserId): Promise<void> {
    await this.repository.delete({ id: 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);
  }
}