import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Brackets, Repository } from 'typeorm';
import { ClsService } from 'nestjs-cls';
import { TenantAwareRepository } from '../../../common/repositories/tenant-aware.repository';
import { UserEntity } from '../../../entities/user.entity';

@Injectable()
export class UserRepository extends TenantAwareRepository<UserEntity> {
  constructor(@InjectRepository(UserEntity) repo: Repository<UserEntity>, cls: ClsService) {
    super(repo, cls);
  }

  async findByEmail(email: string): Promise<UserEntity | null> {
    return this.findOne({
      where: { email },
      relations: {
        role: true,
      },
    });
  }

  async findAllWithRole(): Promise<UserEntity[]> {
    return this.find({
      relations: {
        role: true,
      },
      order: { created_at: 'DESC' },
    });
  }

  async findByIdWithRole(id: string): Promise<UserEntity | null> {
    return this.findOne({
      where: { id },
      relations: {
        role: {
          permissions: true,
        },
        parent_user: true,
      },
    });
  }

  /** All direct + indirect reports of managerUserId (same tenant). */
  async findDescendantUserIds(managerUserId: string): Promise<string[]> {
    const tenantId = this.getTenantId();
    const rows: { id: string }[] = await this.manager.query(
      `WITH RECURSIVE reports AS (
         SELECT id FROM users
         WHERE tenant_id = $1 AND is_deleted = false AND parent_user_id = $2
         UNION ALL
         SELECT u.id FROM users u
         INNER JOIN reports r ON u.parent_user_id = r.id
         WHERE u.tenant_id = $1 AND u.is_deleted = false
       )
       SELECT id FROM reports`,
      [tenantId, managerUserId],
    );
    return rows.map((r) => r.id);
  }

  async findPaginatedForAdmin(opts: {
    page?: number;
    limit?: number;
    search?: string;
    reportsTo?: string;
  }) {
    const page = opts.page || 1;
    const limit = opts.limit || 10;
    const skip = (page - 1) * limit;
    const tenantId = this.getTenantId();

    const qb = this.repo
      .createQueryBuilder('u')
      .leftJoinAndSelect('u.role', 'role')
      .leftJoinAndSelect('u.parent_user', 'parent_user')
      .where('u.tenant_id = :tenantId', { tenantId })
      .andWhere('u.is_deleted = false');

    if (opts.reportsTo) {
      qb.andWhere('u.parent_user_id = :reportsTo', { reportsTo: opts.reportsTo });
    }

    const search = opts.search?.trim();
    if (search) {
      qb.andWhere(
        new Brackets((sub) => {
          sub
            .where('u.name ILIKE :s', { s: `%${search}%` })
            .orWhere('u.email ILIKE :s', { s: `%${search}%` })
            .orWhere('u.phone ILIKE :s', { s: `%${search}%` })
            .orWhere('parent_user.name ILIKE :s', { s: `%${search}%` })
            .orWhere('parent_user.email ILIKE :s', { s: `%${search}%` });
        }),
      );
    }

    qb.orderBy('u.name', 'ASC').skip(skip).take(limit);
    const [items, total] = await qb.getManyAndCount();
    return {
      items,
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }
}
