import {
  Injectable,
  NotFoundException,
  ConflictException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { User } from '../../../database/entities/user.entity';
import {
  CreateUserDto,
  UpdateUserDto,
  ListUsersQueryDto,
  UserResponseDto,
  PaginatedUsersResponseDto,
} from './user-management.dto';
import * as bcrypt from 'bcrypt';

@Injectable()
export class UserManagementService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {}

  async listUsers(
    query: ListUsersQueryDto,
  ): Promise<PaginatedUsersResponseDto> {
    const { page = 1, limit = 20, search, role, isAdmin } = query;
    const skip = (page - 1) * limit;

    const queryBuilder = this.userRepository.createQueryBuilder('user');

    // Apply filters
    if (search) {
      queryBuilder.andWhere(
        '(user.email ILIKE :search OR user.name ILIKE :search)',
        { search: `%${search}%` },
      );
    }

    if (role) {
      queryBuilder.andWhere('user.global_role = :role', { role });
    }

    if (typeof isAdmin === 'boolean') {
      queryBuilder.andWhere('user.is_admin = :isAdmin', { isAdmin });
    }

    // Execute query with pagination
    const [users, total] = await queryBuilder
      .orderBy('user.created_at', 'DESC')
      .skip(skip)
      .take(limit)
      .getManyAndCount();

    // Fetch counts for all users in a single query
    const userIds = users.map((u) => u.id);
    const counts = await this.getUserCounts(userIds);

    return {
      users: users.map((user) => this.toResponseDto(user, counts[user.id])),
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  private async getUserCounts(userIds: string[]): Promise<
    Record<
      string,
      { familyCount: number; childrenCount: number; deviceCount: number }
    >
  > {
    if (userIds.length === 0) return {};

    // Query family memberships
    const familyCountsRaw = await this.userRepository.query(
      `SELECT user_id, COUNT(DISTINCT family_id) as count
       FROM family_members
       WHERE user_id = ANY($1)
       GROUP BY user_id`,
      [userIds],
    );

    // Query children (through families they belong to)
    const childrenCountsRaw = await this.userRepository.query(
      `SELECT fm.user_id, COUNT(DISTINCT c.id) as count
       FROM family_members fm
       JOIN children c ON c.family_id = fm.family_id
       WHERE fm.user_id = ANY($1)
       GROUP BY fm.user_id`,
      [userIds],
    );

    // Query devices
    const deviceCountsRaw = await this.userRepository.query(
      `SELECT user_id, COUNT(*) as count
       FROM device_registry
       WHERE user_id = ANY($1)
       GROUP BY user_id`,
      [userIds],
    );

    // Build counts map
    const counts: Record<
      string,
      { familyCount: number; childrenCount: number; deviceCount: number }
    > = {};

    userIds.forEach((userId) => {
      counts[userId] = { familyCount: 0, childrenCount: 0, deviceCount: 0 };
    });

    familyCountsRaw.forEach((row: any) => {
      counts[row.user_id].familyCount = parseInt(row.count, 10);
    });

    childrenCountsRaw.forEach((row: any) => {
      counts[row.user_id].childrenCount = parseInt(row.count, 10);
    });

    deviceCountsRaw.forEach((row: any) => {
      counts[row.user_id].deviceCount = parseInt(row.count, 10);
    });

    return counts;
  }

  async getUserById(userId: string): Promise<UserResponseDto> {
    const user = await this.userRepository.findOne({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException(`User with ID ${userId} not found`);
    }

    const counts = await this.getUserCounts([userId]);
    return this.toResponseDto(user, counts[userId]);
  }

  async createUser(dto: CreateUserDto): Promise<UserResponseDto> {
    // Check if user already exists
    const existingUser = await this.userRepository.findOne({
      where: { email: dto.email },
    });

    if (existingUser) {
      throw new ConflictException(
        `User with email ${dto.email} already exists`,
      );
    }

    // Hash password
    const hashedPassword = await bcrypt.hash(dto.password, 10);

    // Create user
    const user = this.userRepository.create({
      email: dto.email,
      passwordHash: hashedPassword,
      name: dto.name,
      phone: dto.phone,
      globalRole: dto.globalRole || 'parent',
      isAdmin: dto.isAdmin || false,
      adminPermissions: dto.adminPermissions || [],
      emailVerified: true, // Admin-created users are pre-verified
    });

    const savedUser = await this.userRepository.save(user);

    return this.toResponseDto(savedUser);
  }

  async updateUser(
    userId: string,
    dto: UpdateUserDto,
  ): Promise<UserResponseDto> {
    const user = await this.userRepository.findOne({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException(`User with ID ${userId} not found`);
    }

    // Validate role changes
    if (dto.globalRole === 'admin' && !dto.isAdmin) {
      throw new BadRequestException(
        'Users with globalRole "admin" must have isAdmin = true',
      );
    }

    if (dto.isAdmin && dto.globalRole && dto.globalRole !== 'admin') {
      throw new BadRequestException(
        'Admin users must have globalRole = "admin"',
      );
    }

    // Update fields
    Object.assign(user, dto);

    const updatedUser = await this.userRepository.save(user);

    return this.toResponseDto(updatedUser);
  }

  async deleteUser(userId: string): Promise<{ message: string }> {
    const user = await this.userRepository.findOne({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException(`User with ID ${userId} not found`);
    }

    // For GDPR compliance, we can either hard delete or anonymize
    // Here we'll do a hard delete (cascade will handle related data)
    await this.userRepository.remove(user);

    return {
      message: `User ${userId} has been permanently deleted`,
    };
  }

  private toResponseDto(
    user: User,
    counts?: { familyCount: number; childrenCount: number; deviceCount: number },
  ): UserResponseDto {
    return {
      id: user.id,
      email: user.email,
      name: user.name,
      phone: user.phone,
      photoUrl: user.photoUrl || undefined,
      globalRole: user.globalRole,
      isAdmin: user.isAdmin,
      adminPermissions: user.adminPermissions,
      emailVerified: user.emailVerified,
      createdAt: user.createdAt,
      updatedAt: user.updatedAt,
      familyCount: counts?.familyCount,
      childrenCount: counts?.childrenCount,
      deviceCount: counts?.deviceCount,
    };
  }
}
