import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "../../infra/prisma/prisma.service";
import { CreateSupplierDto } from "./dto/create-supplier.dto";
import { SupplierQueryDto } from "./dto/supplier-query.dto";
import { UpdateSupplierDto } from "./dto/update-supplier.dto";
import { FinancialStatus } from "@prisma/client";

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

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

    const where: any = {
      companyId,
      ...(search
        ? {
            OR: [
              { name: { contains: search, mode: "insensitive" } },
              { document: { contains: search, mode: "insensitive" } },
              { email: { contains: search, mode: "insensitive" } },
              { phone: { contains: search, mode: "insensitive" } },
              { contactName: { contains: search, mode: "insensitive" } },
            ],
          }
        : {}),
    };

    const [items, total] = await Promise.all([
      this.prisma.supplier.findMany({
        where,
        orderBy: { name: "asc" },
        skip,
        take: limit,
        include: {
          payables: {
            select: {
              id: true,
              amount: true,
              status: true,
              dueDate: true,
            },
          },
        },
      }),
      this.prisma.supplier.count({ where }),
    ]);

    const formattedItems = items.map((supplier) => {
      const openPayables = supplier.payables.filter(
        (p) => p.status === FinancialStatus.OPEN || p.status === FinancialStatus.OVERDUE
      );
      const totalOpenAmount = openPayables.reduce((acc, p) => acc + Number(p.amount), 0);

      return {
        ...supplier,
        createdAt: supplier.createdAt.toISOString(),
        updatedAt: supplier.updatedAt.toISOString(),
        openPayablesCount: openPayables.length,
        totalOpenAmount,
      };
    });

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

  async getStats(companyId: string) {
    const suppliers = await this.prisma.supplier.findMany({
      where: { companyId },
      include: {
        payables: true,
      },
    });

    const total = suppliers.length;

    let activeWithPayables = 0;
    let totalPayableAmount = 0;

    suppliers.forEach((s) => {
      const openPayables = s.payables.filter(
        (p) => p.status === FinancialStatus.OPEN || p.status === FinancialStatus.OVERDUE
      );
      if (openPayables.length > 0) {
        activeWithPayables += 1;
      }
      totalPayableAmount += openPayables.reduce((acc, p) => acc + Number(p.amount), 0);
    });

    return {
      total,
      activeWithPayables,
      totalPayableAmount,
    };
  }

  async findOne(companyId: string, id: string) {
    const supplier = await this.prisma.supplier.findFirst({
      where: { id, companyId },
      include: {
        payables: {
          include: {
            serviceOrder: true,
          },
          orderBy: { dueDate: "asc" },
        },
      },
    });

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

    const openPayables = supplier.payables.filter(
      (p) => p.status === FinancialStatus.OPEN || p.status === FinancialStatus.OVERDUE
    );
    const totalOpenAmount = openPayables.reduce((acc, p) => acc + Number(p.amount), 0);

    return {
      ...supplier,
      createdAt: supplier.createdAt.toISOString(),
      updatedAt: supplier.updatedAt.toISOString(),
      openPayablesCount: openPayables.length,
      totalOpenAmount,
      payables: supplier.payables.map((p) => ({
        ...p,
        amount: Number(p.amount),
        dueDate: p.dueDate.toISOString(),
        paidAt: p.paidAt ? p.paidAt.toISOString() : null,
      })),
    };
  }

  async create(companyId: string, dto: CreateSupplierDto) {
    const supplier = await this.prisma.supplier.create({
      data: {
        companyId,
        name: dto.name,
        document: dto.document,
        email: dto.email,
        phone: dto.phone,
        contactName: dto.contactName,
      },
    });

    return {
      ...supplier,
      createdAt: supplier.createdAt.toISOString(),
      updatedAt: supplier.updatedAt.toISOString(),
    };
  }

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

    const updated = await this.prisma.supplier.update({
      where: { id },
      data: {
        ...(dto.name ? { name: dto.name } : {}),
        ...(dto.document !== undefined ? { document: dto.document } : {}),
        ...(dto.email !== undefined ? { email: dto.email } : {}),
        ...(dto.phone !== undefined ? { phone: dto.phone } : {}),
        ...(dto.contactName !== undefined ? { contactName: dto.contactName } : {}),
      },
    });

    return {
      ...updated,
      createdAt: updated.createdAt.toISOString(),
      updatedAt: updated.updatedAt.toISOString(),
    };
  }

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

    return this.prisma.supplier.delete({
      where: { id },
    });
  }
}
