import { Injectable } from "@nestjs/common";
import { PrismaService } from "../../infra/prisma/prisma.service";
import { ReportQueryDto } from "./dto/report-query.dto";
import { EquipmentStatus, MaintenanceStatus, RentalStatus } from "@prisma/client";

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

  async getSummary(companyId: string, query: ReportQueryDto) {
    const { startDate, endDate } = query;

    const dateFilter: any = {};
    if (startDate) dateFilter.gte = new Date(startDate);
    if (endDate) {
      const endD = new Date(endDate);
      endD.setHours(23, 59, 59, 999);
      dateFilter.lte = endD;
    }

    const hasDateFilter = startDate || endDate;

    const [rentals, serviceOrders, totalEquipmentCount, rentedEquipmentCount] = await Promise.all([
      this.prisma.rental.findMany({
        where: {
          companyId,
          ...(hasDateFilter ? { startDate: dateFilter } : {}),
        },
        select: {
          total: true,
          status: true,
        },
      }),
      this.prisma.serviceOrder.findMany({
        where: {
          companyId,
          ...(hasDateFilter ? { openedAt: dateFilter } : {}),
        },
        select: {
          cost: true,
          status: true,
        },
      }),
      this.prisma.equipment.count({
        where: { companyId },
      }),
      this.prisma.equipment.count({
        where: { companyId, status: EquipmentStatus.RENTED },
      }),
    ]);

    const totalRevenue = rentals.reduce((acc, r) => acc + Number(r.total || 0), 0);
    const totalMaintenanceCost = serviceOrders.reduce((acc, s) => acc + Number(s.cost || 0), 0);
    const netProfit = totalRevenue - totalMaintenanceCost;
    const activeRentalsCount = rentals.filter((r) => r.status === RentalStatus.IN_PROGRESS || r.status === RentalStatus.RESERVED).length;
    const fleetUtilizationRate =
      totalEquipmentCount > 0
        ? Math.round((rentedEquipmentCount / totalEquipmentCount) * 100)
        : 0;

    return {
      totalRevenue,
      totalMaintenanceCost,
      netProfit,
      activeRentalsCount,
      totalEquipmentCount,
      rentedEquipmentCount,
      fleetUtilizationRate,
    };
  }

  async getFinancialReport(companyId: string, query: ReportQueryDto) {
    const { startDate, endDate, search } = query;

    const dateFilter: any = {};
    if (startDate) dateFilter.gte = new Date(startDate);
    if (endDate) {
      const endD = new Date(endDate);
      endD.setHours(23, 59, 59, 999);
      dateFilter.lte = endD;
    }

    const hasDateFilter = startDate || endDate;

    const [receivables, payables] = await Promise.all([
      this.prisma.accountReceivable.findMany({
        where: {
          companyId,
          ...(hasDateFilter ? { dueDate: dateFilter } : {}),
          ...(search
            ? {
                OR: [
                  { description: { contains: search, mode: "insensitive" } },
                  { customer: { name: { contains: search, mode: "insensitive" } } },
                ],
              }
            : {}),
        },
        include: {
          customer: { select: { name: true } },
        },
        orderBy: { dueDate: "desc" },
      }),
      this.prisma.accountPayable.findMany({
        where: {
          companyId,
          ...(hasDateFilter ? { dueDate: dateFilter } : {}),
          ...(search
            ? {
                OR: [
                  { description: { contains: search, mode: "insensitive" } },
                  { supplier: { name: { contains: search, mode: "insensitive" } } },
                ],
              }
            : {}),
        },
        include: {
          supplier: { select: { name: true } },
        },
        orderBy: { dueDate: "desc" },
      }),
    ]);

    const formattedReceivables = receivables.map((r) => ({
      id: r.id,
      type: "RECEIVABLE" as const,
      description: r.description || "Receita de Locação",
      entityName: r.customer?.name || "Cliente",
      amount: Number(r.amount),
      status: r.status,
      dueDate: r.dueDate.toISOString(),
      paidAt: r.paidAt ? r.paidAt.toISOString() : null,
    }));

    const formattedPayables = payables.map((p) => ({
      id: p.id,
      type: "PAYABLE" as const,
      description: p.description || "Despesa / Manutenção",
      entityName: p.supplier?.name || "Fornecedor",
      amount: Number(p.amount),
      status: p.status,
      dueDate: p.dueDate.toISOString(),
      paidAt: p.paidAt ? p.paidAt.toISOString() : null,
    }));

    const totalReceivables = formattedReceivables.reduce((acc, r) => acc + r.amount, 0);
    const totalPayables = formattedPayables.reduce((acc, p) => acc + p.amount, 0);

    return {
      items: [...formattedReceivables, ...formattedPayables],
      summary: {
        totalReceivables,
        totalPayables,
        balance: totalReceivables - totalPayables,
      },
    };
  }

  async getFleetReport(companyId: string, query: ReportQueryDto) {
    const { categoryId, search } = query;

    const equipments = await this.prisma.equipment.findMany({
      where: {
        companyId,
        ...(categoryId ? { categoryId } : {}),
        ...(search
          ? {
              OR: [
                { code: { contains: search, mode: "insensitive" } },
                { assetTag: { contains: search, mode: "insensitive" } },
                { brand: { contains: search, mode: "insensitive" } },
                { model: { contains: search, mode: "insensitive" } },
              ],
            }
          : {}),
      },
      include: {
        category: true,
        rentalItems: true,
        serviceOrders: true,
      },
      orderBy: { code: "asc" },
    });

    const items = equipments.map((eq) => {
      const totalRentals = eq.rentalItems.length;
      const totalRevenue = eq.rentalItems.reduce(
        (acc, r) => acc + Number(r.total || 0),
        0
      );
      const totalMaintenanceCost = eq.serviceOrders.reduce(
        (acc, s) => acc + Number(s.cost || 0),
        0
      );

      return {
        id: eq.id,
        code: eq.code,
        name: `${eq.brand} ${eq.model}`.trim() || eq.code,
        categoryName: eq.category?.name || "Geral",
        brand: eq.brand || "—",
        model: eq.model || "—",
        status: eq.status,
        totalRentals,
        totalRevenue,
        totalMaintenanceCost,
        netBalance: totalRevenue - totalMaintenanceCost,
      };
    });

    return { items };
  }

  async getMaintenanceReport(companyId: string, query: ReportQueryDto) {
    const { startDate, endDate, status, search } = query;

    const dateFilter: any = {};
    if (startDate) dateFilter.gte = new Date(startDate);
    if (endDate) {
      const endD = new Date(endDate);
      endD.setHours(23, 59, 59, 999);
      dateFilter.lte = endD;
    }

    const hasDateFilter = startDate || endDate;

    const serviceOrders = await this.prisma.serviceOrder.findMany({
      where: {
        companyId,
        ...(hasDateFilter ? { openedAt: dateFilter } : {}),
        ...(status && status !== "ALL" ? { status: status as MaintenanceStatus } : {}),
        ...(search
          ? {
              OR: [
                { code: { contains: search, mode: "insensitive" } },
                { title: { contains: search, mode: "insensitive" } },
                { equipment: { code: { contains: search, mode: "insensitive" } } },
              ],
            }
          : {}),
      },
      include: {
        equipment: {
          select: { code: true, brand: true, model: true },
        },
        payables: {
          include: {
            supplier: { select: { name: true } },
          },
        },
      },
      orderBy: { openedAt: "desc" },
    });

    const items = serviceOrders.map((so) => {
      const supplierName = so.payables.find((p) => p.supplier?.name)?.supplier?.name || "Interno / Próprio";
      const equipmentName = `${so.equipment.code} - ${so.equipment.brand} ${so.equipment.model}`.trim();

      return {
        id: so.id,
        code: so.code,
        title: so.title,
        equipmentName,
        supplierName,
        cost: Number(so.cost || 0),
        status: so.status,
        openedAt: so.openedAt.toISOString(),
        closedAt: so.closedAt ? so.closedAt.toISOString() : null,
      };
    });

    const totalCost = items.reduce((acc, item) => acc + item.cost, 0);

    return {
      items,
      summary: {
        totalOrders: items.length,
        totalCost,
      },
    };
  }
}
