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 { CreateGuardDto } from './dto/create-guard.dto';

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

  findAll(user: RequestUser, estateId?: string) {
    const scopedEstateId = resolveEstateScope(user, estateId);
    return this.prisma.guard.findMany({
      where: { estateId: scopedEstateId },
      orderBy: { createdAt: 'desc' }
    });
  }

  async create(dto: CreateGuardDto, user: RequestUser) {
    const estateId = resolveEstateScope(user, dto.estateId);
    await this.assertPlanGuardLimit(estateId);
    const name = this.displayName(dto);
    const generatedGuardCode = `GRD-${generateCode()}`;
    const completion = calculateProfileCompletion(UserRole.GUARD, {
      estateId,
      firstName: dto.firstName,
      lastName: dto.lastName,
      phone: dto.phone,
      guardCode: generatedGuardCode,
      assignedGateId: dto.assignedGateId,
      address: dto.address,
      profileConfirmed: false
    });
    try {
      const guard = await this.prisma.$transaction(async (tx) => {
        const account = await tx.user.create({
          data: {
            estateId,
            name,
            phone: dto.phone,
            firstName: dto.firstName,
            lastName: dto.lastName,
            address: dto.address,
            profileCompletion: completion.completion,
            role: UserRole.GUARD
          }
        });
        return tx.guard.create({
          data: {
            estateId,
            userId: account.id,
            guardCode: generatedGuardCode,
            name,
            firstName: dto.firstName,
            lastName: dto.lastName,
            phone: dto.phone,
            address: dto.address,
            assignedGateId: dto.assignedGateId,
            profileCompletion: completion.completion,
            deviceId: dto.deviceId?.trim() || undefined
          }
        });
      });

      await this.audit.record({
        estateId,
        actorType: 'USER',
        actorId: user.id,
        action: 'GUARD_CREATED',
        metadata: { guardId: guard.id, deviceId: guard.deviceId }
      });
      return guard;
    } catch (error) {
      if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {
        throw new ConflictException('A guard with this phone number or device ID already exists');
      }
      throw error;
    }
  }

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

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