import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User, UserRole, UserStatus } from '../../domain/entities/user.entity';
import { IUserRepository, UserListFilters } from '../../domain/repositories/user.repository.interface';

@Injectable()
export class UserRepository implements IUserRepository {
  constructor(@InjectRepository(User) private readonly repo: Repository<User>) {}

  async findById(id: string): Promise<User | null> {
    return this.repo.findOne({ where: { id } });
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.repo.findOne({ where: { email } });
  }

  async findAll(page: number, limit: number): Promise<[User[], number]> {
    return this.repo.findAndCount({
      skip: (page - 1) * limit,
      take: limit,
      order: { createdAt: 'DESC' },
    });
  }

  async updateProfile(id: string, data: Partial<User>): Promise<User> {
    await this.repo.update(id, data);
    return this.repo.findOneOrFail({ where: { id } });
  }

  async updateKycLevel(id: string, level: number): Promise<void> {
    await this.repo.update(id, { kycLevel: level });
  }

  async updateStatus(id: string, status: UserStatus | string): Promise<void> {
    await this.repo.update(id, { status: status as UserStatus });
  }

  async save(user: User): Promise<User> {
    return this.repo.save(user);
  }

  async create(data: Partial<User>): Promise<User> {
    const user = this.repo.create(data);
    return this.repo.save(user);
  }

  // ── Admin Queries ─────────────────────────────────────────────────────────

  async findByFilters(filters: UserListFilters): Promise<[User[], number]> {
    const { page, limit, search, kycLevel, status } = filters;
    const qb = this.repo.createQueryBuilder('u');

    if (search) {
      qb.andWhere(
        '(u.phone ILIKE :search OR u.email ILIKE :search OR u.nickname ILIKE :search)',
        { search: `%${search}%` },
      );
    }
    if (kycLevel !== undefined) {
      qb.andWhere('u.kyc_level = :kycLevel', { kycLevel });
    }
    if (status) {
      qb.andWhere('u.status = :status', { status });
    }

    qb.orderBy('u.created_at', 'DESC')
      .skip((page - 1) * limit)
      .take(limit);

    return qb.getManyAndCount();
  }

  async countByStatus(status: string): Promise<number> {
    return this.repo.count({ where: { status: status as UserStatus } });
  }

  async countByRole(role: UserRole): Promise<number> {
    return this.repo.count({ where: { role } });
  }

  async count(): Promise<number> {
    return this.repo.count();
  }

  async findByRole(role: UserRole): Promise<User[]> {
    return this.repo.find({ where: { role } });
  }

  async findByIdAndRole(id: string, role: UserRole): Promise<User | null> {
    return this.repo.findOne({ where: { id, role } });
  }

  async countCreatedSince(since: Date): Promise<number> {
    return this.repo
      .createQueryBuilder('u')
      .where('u.created_at >= :since', { since })
      .getCount();
  }

  async getRegistrationTrend(days: number): Promise<Array<{ date: string; count: number }>> {
    const result = await this.repo
      .createQueryBuilder('u')
      .select("DATE(u.created_at)", 'date')
      .addSelect('COUNT(*)', 'count')
      .where(`u.created_at >= NOW() - INTERVAL '${days} days'`)
      .groupBy("DATE(u.created_at)")
      .orderBy('date', 'ASC')
      .getRawMany();

    return result.map((r) => ({ date: r.date, count: parseInt(r.count, 10) }));
  }

  async getKycLevelDistribution(): Promise<Array<{ level: number; count: number }>> {
    const result = await this.repo
      .createQueryBuilder('u')
      .select('u.kyc_level', 'level')
      .addSelect('COUNT(*)', 'count')
      .groupBy('u.kyc_level')
      .getRawMany();

    return result.map((r) => ({ level: parseInt(r.level, 10), count: parseInt(r.count, 10) }));
  }

  async getGeoDistribution(): Promise<Array<{ region: string; count: number }>> {
    const result = await this.repo
      .createQueryBuilder('u')
      .select('u.residence_state', 'region')
      .addSelect('COUNT(*)', 'count')
      .where('u.residence_state IS NOT NULL')
      .groupBy('u.residence_state')
      .orderBy('count', 'DESC')
      .getRawMany();

    return result.map((r) => ({ region: r.region, count: parseInt(r.count, 10) }));
  }
}
