import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "../../infra/prisma/prisma.service";
import { CreateReservationDto, ReservationStatusEnum } from "./dto/create-reservation.dto";
import { ReservationQueryDto } from "./dto/reservation-query.dto";
import { UpdateReservationDto } from "./dto/update-reservation.dto";

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

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

    const where: any = {
      companyId,
      ...(status === ReservationStatusEnum.CONVERTED
        ? { status: "IN_PROGRESS" }
        : status === ReservationStatusEnum.CONFIRMED
        ? { status: "RESERVED" }
        : status === ReservationStatusEnum.PENDING
        ? { status: "QUOTE" }
        : {}),
      ...(customerId ? { customerId } : {}),
      ...(search
        ? {
            OR: [
              { code: { contains: search, mode: "insensitive" } },
              { customer: { name: { contains: search, mode: "insensitive" } } },
            ],
          }
        : {}),
    };

    const [rentals, total] = await Promise.all([
      this.prisma.rental.findMany({
        where,
        orderBy: { createdAt: "desc" },
        skip,
        take: limit,
        include: {
          customer: { select: { id: true, name: true, document: true, phone: true } },
          items: {
            include: {
              equipment: { select: { id: true, code: true, assetTag: true, brand: true, model: true, dailyRate: true } },
            },
          },
        },
      }),
      this.prisma.rental.count({ where }),
    ]);

    const items = rentals.map((r) => {
      const mainEquip = r.items[0]?.equipment;
      // 💡 REGRA DE NEGÓCIO SECTORIAL:
      // O sinal/depósito é estritamente o valor informado pelo usuário (campo r.freight como reserva/sinal).
      // Se o usuário não digitou sinal, é 0 (R$ 0,00) - sem porcentagens mágicas/fictícias.
      const depositAmount = Number(r.depositAmount) || Number(r.freight) || 0;

      return {
        id: r.id,
        companyId: r.companyId,
        code: r.code.replace("LOC-", "RES-").replace("ORC-", "RES-"),
        customerId: r.customerId,
        equipmentId: mainEquip?.id || "",
        startDate: r.startDate.toISOString(),
        endDate: r.endDate.toISOString(),
        depositAmount,
        validUntil: new Date(r.startDate.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
        notes: r.notes,
        status:
          r.status === "IN_PROGRESS"
            ? ReservationStatusEnum.CONVERTED
            : r.status === "RESERVED"
            ? ReservationStatusEnum.CONFIRMED
            : r.status === "QUOTE"
            ? ReservationStatusEnum.PENDING
            : ReservationStatusEnum.CANCELED,
        createdAt: r.createdAt.toISOString(),
        updatedAt: r.updatedAt.toISOString(),
        customer: r.customer,
        equipment: mainEquip,
      };
    });

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

  async getStats(companyId: string) {
    const rentals = await this.prisma.rental.findMany({
      where: { companyId },
    });

    const total = rentals.length;
    const confirmed = rentals.filter((r) => r.status === "RESERVED").length;
    const pending = rentals.filter((r) => r.status === "QUOTE").length;
    const converted = rentals.filter((r) => r.status === "IN_PROGRESS").length;
    const totalDeposit = rentals
      .filter((r) => r.status === "RESERVED")
      .reduce((acc, r) => acc + (Number(r.depositAmount) || Number(r.freight) || 0), 0);

    const conversionRate = total > 0 ? Math.round((converted / total) * 100) : 0;

    return {
      total,
      confirmed,
      pending,
      converted,
      totalDeposit,
      conversionRate,
    };
  }

  async findOne(companyId: string, id: string) {
    const rental = await this.prisma.rental.findFirst({
      where: { id, companyId },
      include: {
        customer: true,
        items: { include: { equipment: true } },
      },
    });

    if (!rental) {
      throw new NotFoundException("Reserva não encontrada.");
    }

    const mainEquip = rental.items[0]?.equipment;
    const depositAmount = Number(rental.depositAmount) || Number(rental.freight) || 0;

    return {
      id: rental.id,
      companyId: rental.companyId,
      code: rental.code.replace("LOC-", "RES-").replace("ORC-", "RES-"),
      customerId: rental.customerId,
      equipmentId: mainEquip?.id || "",
      startDate: rental.startDate.toISOString(),
      endDate: rental.endDate.toISOString(),
      depositAmount,
      validUntil: new Date(rental.startDate.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
      notes: rental.notes,
      status:
        rental.status === "IN_PROGRESS"
          ? ReservationStatusEnum.CONVERTED
          : rental.status === "RESERVED"
          ? ReservationStatusEnum.CONFIRMED
          : rental.status === "QUOTE"
          ? ReservationStatusEnum.PENDING
          : ReservationStatusEnum.CANCELED,
      createdAt: rental.createdAt.toISOString(),
      updatedAt: rental.updatedAt.toISOString(),
      customer: rental.customer,
      equipment: mainEquip,
    };
  }

  async create(companyId: string, dto: CreateReservationDto) {
    const count = await this.prisma.rental.count({ where: { companyId } });
    const code = `RES-2026-${String(count + 1).padStart(4, "0")}`;

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

    const dailyRate = Number(equipment?.dailyRate || 500);
    const start = new Date(dto.startDate);
    const end = new Date(dto.endDate);
    const days = Math.max(Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)), 1);
    const subtotal = dailyRate * days;

    return this.prisma.rental.create({
      data: {
        companyId,
        customerId: dto.customerId,
        code,
        startDate: start,
        endDate: end,
        subtotal,
        freight: dto.depositAmount || 0,
        total: subtotal,
        notes: dto.notes,
        status: dto.status === ReservationStatusEnum.CONFIRMED ? "RESERVED" : "QUOTE",
        items: {
          create: {
            equipmentId: dto.equipmentId,
            quantity: 1,
            dailyRate,
            total: subtotal,
          },
        },
      },
      include: {
        customer: true,
        items: { include: { equipment: true } },
      },
    });
  }

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

    return this.prisma.rental.update({
      where: { id },
      data: {
        customerId: dto.customerId,
        startDate: new Date(dto.startDate),
        endDate: new Date(dto.endDate),
        freight: dto.depositAmount || 0,
        notes: dto.notes,
        status:
          dto.status === ReservationStatusEnum.CONVERTED
            ? "IN_PROGRESS"
            : dto.status === ReservationStatusEnum.CONFIRMED
            ? "RESERVED"
            : "QUOTE",
      },
      include: {
        customer: true,
        items: { include: { equipment: true } },
      },
    });
  }

  async convertToRental(companyId: string, id: string) {
    await this.findOne(companyId, id);

    return this.prisma.rental.update({
      where: { id },
      data: {
        status: "IN_PROGRESS",
      },
      include: {
        customer: true,
        items: { include: { equipment: true } },
      },
    });
  }

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