import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
import { ViharStatus } from '@prisma/client';
import { PrismaService } from '../../common/prisma/prisma.service';
import type { CreateUserInput } from '@vihar/shared';

@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  async list(
    cityId: number,
    query: { q?: string; localityId?: number; role?: string; isActive?: boolean; page: number; pageSize: number },
  ) {
    const where: Record<string, unknown> = { cityId };
    if (query.q) {
      where.OR = [
        { fullName: { contains: query.q } },
        { username: { contains: query.q } },
        { phone: { contains: query.q } },
      ];
    }
    if (query.localityId) where.homeLocalityId = query.localityId;
    if (query.role === 'captain') where.isCaptain = true;
    if (query.role === 'volunteer') where.isVolunteer = true;
    if (query.isActive !== undefined) where.isActive = query.isActive;

    const [items, total] = await this.prisma.$transaction([
      this.prisma.user.findMany({
        where,
        skip: (query.page - 1) * query.pageSize,
        take: query.pageSize,
        orderBy: { fullName: 'asc' },
        select: {
          userId: true,
          username: true,
          fullName: true,
          phone: true,
          gender: true,
          homeLocalityId: true,
          isCaptain: true,
          isVolunteer: true,
          isActive: true,
          createdAt: true,
          lastLoginAt: true,
          homeLocality: { select: { localityId: true, name: true } },
          _count: {
            select: {
              allocations: {
                where: {
                  isActive: true,
                  vihar: { status: { in: [ViharStatus.completed, ViharStatus.auto_closed] } },
                },
              },
            },
          },
        },
      }),
      this.prisma.user.count({ where }),
    ]);

    // Compute this-month vihar counts in one bulk query
    const startOfMonth = new Date();
    startOfMonth.setDate(1);
    startOfMonth.setHours(0, 0, 0, 0);
    const userIds = items.map((u) => u.userId);
    const monthCounts = userIds.length
      ? await this.prisma.viharVolunteer.groupBy({
          by: ['userId'],
          where: {
            userId: { in: userIds },
            isActive: true,
            vihar: {
              status: { in: [ViharStatus.completed, ViharStatus.auto_closed] },
              viharDate: { gte: startOfMonth },
            },
          },
          _count: { userId: true },
        })
      : [];
    const monthMap = new Map(monthCounts.map((r) => [r.userId, r._count.userId]));

    const enriched = items.map((u) => ({
      ...u,
      lifetimeViharCount: u._count.allocations,
      thisMonthViharCount: monthMap.get(u.userId) ?? 0,
    }));

    return { items: enriched, total, page: query.page, pageSize: query.pageSize };
  }

  async getById(cityId: number, userId: number) {
    const user = await this.prisma.user.findFirst({
      where: { userId, cityId },
      select: {
        userId: true, username: true, fullName: true, phone: true, gender: true,
        homeLocalityId: true, isCaptain: true, isVolunteer: true, isActive: true,
        createdAt: true, lastLoginAt: true,
        homeLocality: { select: { localityId: true, name: true } },
      },
    });
    if (!user) throw new NotFoundException('User not found');
    return user;
  }

  async create(cityId: number, createdBy: number, input: CreateUserInput) {
    const passwordHash = await bcrypt.hash(input.initialPassword, 11);
    return this.prisma.user.create({
      data: {
        cityId,
        username: input.username,
        passwordHash,
        fullName: input.fullName,
        phone: input.phone,
        gender: input.gender,
        homeLocalityId: input.homeLocalityId ?? null,
        isCaptain: input.isCaptain,
        isVolunteer: input.isVolunteer,
        createdBy,
      },
      select: {
        userId: true, username: true, fullName: true, phone: true, gender: true,
        homeLocalityId: true, isCaptain: true, isVolunteer: true, isActive: true,
        createdAt: true,
      },
    });
  }

  async update(
    cityId: number,
    userId: number,
    actorId: number,
    input: Record<string, unknown>,
  ) {
    const user = await this.prisma.user.findUnique({ where: { userId } });
    if (!user || user.cityId !== cityId) throw new NotFoundException('User not found');
    if (user.userId === actorId && input.isActive === false) {
      throw new ForbiddenException('You cannot deactivate yourself');
    }
    return this.prisma.user.update({ where: { userId }, data: input });
  }

  async resetPassword(cityId: number, userId: number, newPassword: string) {
    const user = await this.prisma.user.findUnique({ where: { userId } });
    if (!user || user.cityId !== cityId) throw new NotFoundException('User not found');
    const passwordHash = await bcrypt.hash(newPassword, 11);
    await this.prisma.user.update({ where: { userId }, data: { passwordHash } });
  }
}
