import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like, FindOptionsWhere } from 'typeorm';
import { BaseTenantRepository, TenantContextService } from '@iconsulting/shared';
import {
  IUserRepository,
  UserQueryOptions,
  PaginatedUsers,
} from '../../../domain/repositories/user.repository.interface';
import { UserEntity, UserType } from '../../../domain/entities/user.entity';
import { UserORM } from '../../../infrastructure/database/postgres/entities/user.orm';

@Injectable()
export class UserPostgresRepository
  extends BaseTenantRepository<UserEntity, UserORM>
  implements IUserRepository
{
  constructor(
    @InjectRepository(UserORM) repo: Repository<UserORM>,
    tenantContext: TenantContextService,
  ) {
    super(repo, tenantContext);
  }

  async save(user: UserEntity): Promise<UserEntity> {
    const orm = this.toORM(user);
    orm.tenantId = this.getTenantId();
    const saved = await this.repo.save(orm);
    return this.toEntity(saved);
  }

  async findById(id: string): Promise<UserEntity | null> {
    const orm = await this.findOneWithTenant({ id } as any);
    return orm ? this.toEntity(orm) : null;
  }

  async findByPhone(phone: string): Promise<UserEntity | null> {
    const orm = await this.findOneWithTenant({ phone } as any);
    return orm ? this.toEntity(orm) : null;
  }

  async findByFingerprint(fingerprint: string): Promise<UserEntity | null> {
    const orm = await this.findOneWithTenant({ fingerprint } as any);
    return orm ? this.toEntity(orm) : null;
  }

  async updateLastActive(userId: string): Promise<void> {
    await this.updateWithTenant(userId, { lastActiveAt: new Date() } as any);
  }

  async findAll(options: UserQueryOptions): Promise<PaginatedUsers> {
    const page = options.page || 1;
    const pageSize = options.pageSize || 20;
    const skip = (page - 1) * pageSize;

    const queryBuilder = this.createTenantQueryBuilder('user');

    if (options.type) {
      queryBuilder.andWhere('user.type = :type', { type: options.type });
    }
    if (options.phone) {
      queryBuilder.andWhere('user.phone LIKE :phone', { phone: `%${options.phone}%` });
    }
    if (options.nickname) {
      queryBuilder.andWhere('user.nickname LIKE :nickname', { nickname: `%${options.nickname}%` });
    }

    queryBuilder.orderBy(`user.${options.sortBy || 'createdAt'}`, options.sortOrder || 'DESC');
    queryBuilder.skip(skip).take(pageSize);

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

    return {
      items: items.map((orm) => this.toEntity(orm)),
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

  async countByType(): Promise<Record<UserType, number>> {
    const result = await this.createTenantQueryBuilder('user')
      .select('user.type', 'type')
      .addSelect('COUNT(*)', 'count')
      .groupBy('user.type')
      .getRawMany();

    const counts: Record<UserType, number> = {
      [UserType.ANONYMOUS]: 0,
      [UserType.REGISTERED]: 0,
    };

    for (const row of result) {
      counts[row.type as UserType] = parseInt(row.count, 10);
    }

    return counts;
  }

  async search(keyword: string, limit = 10): Promise<UserEntity[]> {
    const items = await this.createTenantQueryBuilder('user')
      .andWhere('(user.phone LIKE :keyword OR user.nickname LIKE :keyword)', {
        keyword: `%${keyword}%`,
      })
      .orderBy('user.lastActiveAt', 'DESC')
      .take(limit)
      .getMany();

    return items.map((orm) => this.toEntity(orm));
  }

  private toORM(entity: UserEntity): UserORM {
    const orm = new UserORM();
    orm.id = entity.id;
    orm.tenantId = this.getTenantId();
    orm.type = entity.type;
    orm.fingerprint = entity.fingerprint;
    orm.phone = entity.phone;
    orm.nickname = entity.nickname;
    orm.avatar = entity.avatar;
    orm.createdAt = entity.createdAt;
    orm.updatedAt = entity.updatedAt;
    orm.lastActiveAt = entity.lastActiveAt;
    return orm;
  }

  private toEntity(orm: UserORM): UserEntity {
    return UserEntity.fromPersistence({
      id: orm.id,
      type: orm.type,
      fingerprint: orm.fingerprint,
      phone: orm.phone,
      nickname: orm.nickname,
      avatar: orm.avatar,
      createdAt: orm.createdAt,
      updatedAt: orm.updatedAt,
      lastActiveAt: orm.lastActiveAt,
    });
  }
}
