import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { EquipmentStatus, Prisma } from "@prisma/client";
import { PrismaService } from "../../infra/prisma/prisma.service";
import { CreateEquipmentDto } from "./dto/create-equipment.dto";
import { EquipmentQueryDto } from "./dto/equipment-query.dto";
import { UpdateEquipmentDto } from "./dto/update-equipment.dto";

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

  async findAll(companyId: string, query: EquipmentQueryDto) {
    const { search, status, categoryId, page = 1, limit = 20 } = query;
    const skip = (page - 1) * limit;

    const where: Prisma.EquipmentWhereInput = {
      companyId,
      ...(status ? { status } : {}),
      ...(categoryId ? { categoryId } : {}),
      ...(search
        ? {
            OR: [
              { code: { contains: search, mode: "insensitive" } },
              { assetTag: { contains: search, mode: "insensitive" } },
              { brand: { contains: search, mode: "insensitive" } },
              { model: { contains: search, mode: "insensitive" } },
              { serialNumber: { contains: search, mode: "insensitive" } },
            ],
          }
        : {}),
    };

    const [items, total] = await Promise.all([
      this.prisma.equipment.findMany({
        where,
        orderBy: { createdAt: "desc" },
        skip,
        take: limit,
        include: {
          category: true,
          photos: { where: { isPrimary: true }, take: 1 },
          _count: {
            select: {
              serviceOrders: true,
              rentalItems: true,
            },
          },
        },
      }),
      this.prisma.equipment.count({ where }),
    ]);

    return {
      items,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit) || 1,
    };
  }

  async getStats(companyId: string) {
    const [total, available, rented, maintenance, reserved, totalValue] = await Promise.all([
      this.prisma.equipment.count({ where: { companyId } }),
      this.prisma.equipment.count({ where: { companyId, status: EquipmentStatus.AVAILABLE } }),
      this.prisma.equipment.count({ where: { companyId, status: EquipmentStatus.RENTED } }),
      this.prisma.equipment.count({ where: { companyId, status: EquipmentStatus.MAINTENANCE } }),
      this.prisma.equipment.count({ where: { companyId, status: EquipmentStatus.RESERVED } }),
      this.prisma.equipment.aggregate({
        where: { companyId },
        _sum: { acquisitionValue: true },
      }),
    ]);

    return {
      total,
      available,
      rented,
      maintenance,
      reserved,
      totalAcquisitionValue: Number(totalValue._sum.acquisitionValue || 0),
    };
  }

  async getCategories(companyId: string) {
    return this.prisma.category.findMany({
      where: { companyId },
      orderBy: { name: "asc" },
    });
  }

  async findOne(companyId: string, id: string) {
    const equipment = await this.prisma.equipment.findFirst({
      where: { id, companyId },
      include: {
        category: true,
        photos: true,
        serviceOrders: {
          take: 5,
          orderBy: { openedAt: "desc" },
        },
        rentalItems: {
          take: 5,
          include: {
            rental: {
              select: {
                id: true,
                code: true,
                status: true,
                startDate: true,
                endDate: true,
                customer: { select: { id: true, name: true } },
              },
            },
          },
        },
        _count: {
          select: { serviceOrders: true, rentalItems: true },
        },
      },
    });

    if (!equipment) {
      throw new NotFoundException("Equipamento não encontrado.");
    }

    return equipment;
  }

  async create(companyId: string, dto: CreateEquipmentDto) {
    const existingCode = await this.prisma.equipment.findFirst({
      where: { companyId, code: dto.code },
    });

    if (existingCode) {
      throw new BadRequestException("Já existe um equipamento cadastrado com este código interno.");
    }

    const existingAsset = await this.prisma.equipment.findFirst({
      where: { companyId, assetTag: dto.assetTag },
    });

    if (existingAsset) {
      throw new BadRequestException("Já existe um equipamento cadastrado com esta placa de patrimônio.");
    }

    return this.prisma.equipment.create({
      data: {
        ...dto,
        companyId,
      },
      include: { category: true },
    });
  }

  async update(companyId: string, id: string, dto: UpdateEquipmentDto) {
    await this.findOne(companyId, id);

    if (dto.code) {
      const existingCode = await this.prisma.equipment.findFirst({
        where: { companyId, code: dto.code, NOT: { id } },
      });
      if (existingCode) {
        throw new BadRequestException("Outro equipamento já possui este código interno.");
      }
    }

    if (dto.assetTag) {
      const existingAsset = await this.prisma.equipment.findFirst({
        where: { companyId, assetTag: dto.assetTag, NOT: { id } },
      });
      if (existingAsset) {
        throw new BadRequestException("Outro equipamento já possui esta placa de patrimônio.");
      }
    }

    return this.prisma.equipment.update({
      where: { id },
      data: dto,
      include: { category: true },
    });
  }

  async remove(companyId: string, id: string) {
    await this.findOne(companyId, id);
    return this.prisma.equipment.delete({
      where: { id },
    });
  }
}
