import { ConflictException, Injectable } from '@nestjs/common';
import { Prisma, UserRole } from '@prisma/client';
import { AuditService } from '../audit/audit.service';
import { generateCode } from '../common/code-generator';
import { resolveEstateScope } from '../common/estate-scope';
import { getNumericPlanLimit } from '../common/plan-limits';
import { calculateProfileCompletion } from '../common/profile-completion';
import { RequestUser } from '../common/request-user';
import { PrismaService } from '../database/prisma.service';
import { CreateResidentDto } from './dto/create-resident.dto';

@Injectable()
export class ResidentsService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly audit: AuditService
  ) {}

  findAll(user: RequestUser, estateId?: string, q?: string) {
    const scopedEstateId = resolveEstateScope(user, estateId);
    return this.prisma.resident.findMany({
      where: {
        estateId: scopedEstateId,
        OR: q
          ? [
              { name: { contains: q, mode: 'insensitive' } },
              { unit: { contains: q, mode: 'insensitive' } },
              { phone: { contains: q, mode: 'insensitive' } }
            ]
          : undefined
      },
      orderBy: { createdAt: 'desc' },
      take: 50
    });
  }

  async create(dto: CreateResidentDto, user: RequestUser) {
    const estateId = resolveEstateScope(user, dto.estateId);
    await this.assertPlanResidentLimit(estateId);
    const name = this.displayName(dto);
    const unit = dto.flatNumber ?? dto.unit ?? '';
    const completion = calculateProfileCompletion(UserRole.RESIDENT, {
      estateId,
      firstName: dto.firstName,
      lastName: dto.lastName,
      phone: dto.phone,
      blockNumber: dto.blockNumber,
      flatNumber: unit,
      unit,
      address: dto.address,
      profileConfirmed: false
    });
    try {
      const resident = await this.prisma.$transaction(async (tx) => {
        const account = await tx.user.create({
          data: {
            estateId,
            name,
            phone: dto.phone,
            firstName: dto.firstName,
            lastName: dto.lastName,
            email: dto.email,
            address: dto.address,
            profileCompletion: completion.completion,
            role: UserRole.RESIDENT
          }
        });
        return tx.resident.create({
          data: {
            estateId,
            userId: account.id,
            residentCode: `RES-${generateCode()}`,
            name,
            firstName: dto.firstName,
            lastName: dto.lastName,
            phone: dto.phone,
            unit,
            blockNumber: dto.blockNumber,
            flatNumber: unit || undefined,
            address: dto.address,
            email: dto.email,
            profileCompletion: completion.completion
          }
        });
      });

      await this.audit.record({
        estateId,
        actorType: 'USER',
        actorId: user.id,
        action: 'RESIDENT_CREATED',
        metadata: { residentId: resident.id, unit: resident.unit }
      });
      return resident;
    } catch (error) {
      if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {
        throw new ConflictException('A resident with this phone number already exists');
      }
      throw error;
    }
  }

  private displayName(dto: CreateResidentDto) {
    const explicit = dto.name?.trim();
    if (explicit) {
      return explicit;
    }
    const composed = [dto.firstName, dto.lastName].map((value) => value?.trim()).filter(Boolean).join(' ');
    return composed || `Resident ${dto.phone}`;
  }

  private async assertPlanResidentLimit(estateId: string) {
    const estate = await this.prisma.estate.findUnique({
      where: { id: estateId },
      select: {
        subscriptionPlan: { select: { limits: true } },
        _count: { select: { residents: true } }
      }
    });
    const limit = getNumericPlanLimit(estate?.subscriptionPlan?.limits, 'residents');
    if (limit !== null && estate && estate._count.residents >= limit) {
      throw new ConflictException('Resident limit for this estate plan has been reached');
    }
  }
}
