import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "../../infra/prisma/prisma.service";
import { ContractQueryDto } from "./dto/contract-query.dto";
import { ContractStatusEnum, CreateContractDto } from "./dto/create-contract.dto";
import { UpdateContractDto } from "./dto/update-contract.dto";

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

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

    const where: any = {
      companyId,
      status: { in: ["RESERVED", "IN_PROGRESS", "FINISHED"] },
      ...(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: true } },
          receivables: { orderBy: { dueDate: "asc" } },
        },
      }),
      this.prisma.rental.count({ where }),
    ]);

    const items = rentals.map((r) => {
      const isSigned = r.status === "IN_PROGRESS" || r.status === "FINISHED";
      const statusValue = isSigned
        ? ContractStatusEnum.SIGNED
        : r.status === "RESERVED"
        ? ContractStatusEnum.PENDING_SIGNATURE
        : ContractStatusEnum.TERMINATED;

      const depositEntry = r.receivables.find((rec) => rec.description.includes("Sinal"));
      const depositAmount = Number(r.depositAmount) || (depositEntry ? Number(depositEntry.amount) : Number(r.freight) || 0);
      const depositPaidAt = depositEntry?.paidAt
        ? depositEntry.paidAt.toISOString()
        : (r.status !== "QUOTE" && depositAmount > 0 ? r.createdAt.toISOString() : null);

      let recs = r.receivables;
      if (recs.length === 0 && (depositAmount > 0 || (Number(r.installmentsCount) || 1) > 1)) {
        const count = Math.max(Number(r.installmentsCount) || 1, 1);
        const remTotal = Math.max(Number(r.total) - depositAmount, 0);
        const valPerInst = Number((remTotal / count).toFixed(2));
        const proj: any[] = [];
        if (depositAmount > 0) {
          proj.push({
            id: `proj-dep-${r.id}`,
            description: `Sinal de Depósito - ${r.code}`,
            amount: depositAmount,
            dueDate: r.startDate,
            paidAt: r.createdAt,
            status: "PAID",
          });
        }
        if (remTotal > 0) {
          for (let i = 0; i < count; i++) {
            const d = new Date(r.startDate);
            d.setDate(d.getDate() + i * 30);
            proj.push({
              id: `proj-inst-${r.id}-${i}`,
              description: `Parcela ${i + 1}/${count} - Contrato ${r.code}`,
              amount: i === count - 1 ? remTotal - valPerInst * (count - 1) : valPerInst,
              dueDate: d,
              paidAt: null,
              status: "OPEN",
            });
          }
        }
        recs = proj;
      }

      return {
        id: r.id,
        companyId: r.companyId,
        code: r.code.replace("LOC-", "CTR-").replace("ORC-", "CTR-"),
        customerId: r.customerId,
        rentalId: r.id,
        title: `Contrato de Locação Industrial - ${r.customer?.name || "Cliente"}`,
        startDate: r.startDate.toISOString(),
        endDate: r.endDate.toISOString(),
        value: Number(r.total),
        depositAmount,
        depositPaidAt,
        clauses:
          "CLÁUSULA 1ª: O LOCATÁRIO se responsabiliza pela conservação e uso adequado do equipamento. CLÁUSULA 2ª: Manutenções preventivas serão de responsabilidade da LOCADORA. CLÁUSULA 3ª: O atraso na devolução sujeita o locatário a cobrança de diárias adicionais.",
        notes: r.notes,
        status: statusValue,
        signedAt: isSigned ? r.createdAt.toISOString() : null,
        createdAt: r.createdAt.toISOString(),
        updatedAt: r.updatedAt.toISOString(),
        customer: r.customer,
        equipmentCount: r.items.length,
        receivables: recs.map((rec) => ({
          id: rec.id,
          description: rec.description,
          amount: Number(rec.amount),
          dueDate: new Date(rec.dueDate).toISOString(),
          paidAt: rec.paidAt ? new Date(rec.paidAt).toISOString() : null,
          status: rec.status,
        })),
      };
    });

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

  async getStats(companyId: string) {
    const rentals = await this.prisma.rental.findMany({
      where: {
        companyId,
        status: { in: ["RESERVED", "IN_PROGRESS", "FINISHED"] },
      },
    });

    const total = rentals.length;
    const signed = rentals.filter((r) => r.status === "IN_PROGRESS" || r.status === "FINISHED").length;
    const pendingSignature = rentals.filter((r) => r.status === "RESERVED").length;
    const draft = 0;
    const totalContractedValue = rentals.reduce((acc, r) => acc + (Number(r.total) || 0), 0);

    return {
      total,
      signed,
      pendingSignature,
      draft,
      totalContractedValue,
    };
  }

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

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

    const isSigned = rental.status === "IN_PROGRESS" || rental.status === "FINISHED";
    const statusValue = isSigned
      ? ContractStatusEnum.SIGNED
      : rental.status === "RESERVED"
      ? ContractStatusEnum.PENDING_SIGNATURE
      : ContractStatusEnum.TERMINATED;

    const depositEntry = rental.receivables.find((rec) => rec.description.includes("Sinal"));
    const depositAmount = Number(rental.depositAmount) || (depositEntry ? Number(depositEntry.amount) : Number(rental.freight) || 0);
    const depositPaidAt = depositEntry?.paidAt
      ? depositEntry.paidAt.toISOString()
      : (rental.status !== "QUOTE" && depositAmount > 0 ? rental.createdAt.toISOString() : null);

    let recs = rental.receivables;
    if (recs.length === 0 && (depositAmount > 0 || (Number(rental.installmentsCount) || 1) > 1)) {
      const count = Math.max(Number(rental.installmentsCount) || 1, 1);
      const remTotal = Math.max(Number(rental.total) - depositAmount, 0);
      const valPerInst = Number((remTotal / count).toFixed(2));
      const proj: any[] = [];
      if (depositAmount > 0) {
        proj.push({
          id: `proj-dep-${rental.id}`,
          description: `Sinal de Depósito - ${rental.code}`,
          amount: depositAmount,
          dueDate: rental.startDate,
          paidAt: rental.createdAt,
          status: "PAID",
        });
      }
      if (remTotal > 0) {
        for (let i = 0; i < count; i++) {
          const d = new Date(rental.startDate);
          d.setDate(d.getDate() + i * 30);
          proj.push({
            id: `proj-inst-${rental.id}-${i}`,
            description: `Parcela ${i + 1}/${count} - Contrato ${rental.code}`,
            amount: i === count - 1 ? remTotal - valPerInst * (count - 1) : valPerInst,
            dueDate: d,
            paidAt: null,
            status: "OPEN",
          });
        }
      }
      recs = proj;
    }

    return {
      id: rental.id,
      companyId: rental.companyId,
      code: rental.code.replace("LOC-", "CTR-").replace("ORC-", "CTR-"),
      customerId: rental.customerId,
      rentalId: rental.id,
      title: `Contrato de Locação Industrial - ${rental.customer?.name || "Cliente"}`,
      startDate: rental.startDate.toISOString(),
      endDate: rental.endDate.toISOString(),
      value: Number(rental.total),
      depositAmount,
      depositPaidAt,
      clauses:
        "CLÁUSULA 1ª: O LOCATÁRIO se responsabiliza pela conservação e uso adequado do equipamento. CLÁUSULA 2ª: Manutenções preventivas serão de responsabilidade da LOCADORA. CLÁUSULA 3ª: O atraso na devolução sujeita o locatário a cobrança de diárias adicionais.",
      notes: rental.notes,
      status: statusValue,
      signedAt: isSigned ? rental.createdAt.toISOString() : null,
      createdAt: rental.createdAt.toISOString(),
      updatedAt: rental.updatedAt.toISOString(),
      customer: rental.customer,
      equipmentCount: rental.items.length,
      receivables: recs.map((rec) => ({
        id: rec.id,
        description: rec.description,
        amount: Number(rec.amount),
        dueDate: new Date(rec.dueDate).toISOString(),
        paidAt: rec.paidAt ? new Date(rec.paidAt).toISOString() : null,
        status: rec.status,
      })),
    };
  }

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

    const rental = await this.prisma.rental.create({
      data: {
        companyId,
        customerId: dto.customerId,
        code,
        startDate: new Date(dto.startDate),
        endDate: new Date(dto.endDate),
        subtotal: dto.value,
        total: dto.value,
        notes: dto.notes,
        status: "RESERVED",
      },
      include: { customer: true, items: true, receivables: true },
    });

    return this.findOne(companyId, rental.id);
  }

  async update(companyId: string, id: string, dto: UpdateContractDto) {
    const rental = await this.prisma.rental.update({
      where: { id },
      data: {
        ...(dto.startDate ? { startDate: new Date(dto.startDate) } : {}),
        ...(dto.endDate ? { endDate: new Date(dto.endDate) } : {}),
        ...(dto.value !== undefined ? { total: dto.value, subtotal: dto.value } : {}),
        ...(dto.notes !== undefined ? { notes: dto.notes } : {}),
        ...(dto.status === ContractStatusEnum.SIGNED ? { status: "IN_PROGRESS" } : {}),
      },
      include: { customer: true, items: true, receivables: true },
    });

    return this.findOne(companyId, rental.id);
  }

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