import prisma from "../config/prisma.js";
import { Prisma } from "../generated/prisma/client.js";

export class PurchaseReturnService {
  async createPurchaseReturn(data: {
    purchaseId: bigint;
    returnDate: string | Date;
    quantity: number | Prisma.Decimal;
    bags?: number | Prisma.Decimal | null;
    returnAmount: number | Prisma.Decimal;
    reason?: string;
    remarks?: string;
    createdBy?: bigint | null;
  }) {
    return await prisma.$transaction(async (tx) => {
      const purchaseId = BigInt(data.purchaseId);
      const purchase = await tx.purchase.findFirst({
        where: { id: purchaseId, deletedAt: null },
        include: { supplier: true },
      });

      if (!purchase) {
        throw new Error("Purchase not found or has been deleted.");
      }

      const returnQuantity = new Prisma.Decimal(data.quantity);
      const returnBags = data.bags ? new Prisma.Decimal(data.bags) : new Prisma.Decimal(0);
      
      const isBagOnly = purchase.unit !== 'KG' && purchase.quantity.lte(0);
      
      if (isBagOnly && !returnQuantity.isInteger()) {
        throw new Error("Return quantity for bags/boxes must be a whole number.");
      }
      if (returnBags.gt(0) && !returnBags.isInteger()) {
        throw new Error("Return bags must be a whole number.");
      }
      
      const effectiveRate = purchase.costPerUnit || purchase.ratePerUnit;
      
      const returnAmount = isBagOnly 
        ? returnBags.mul(effectiveRate).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP)
        : returnQuantity.mul(effectiveRate).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);

      if (purchase.remainingQuantity.lt(returnQuantity)) {
        throw new Error(
          `Return quantity (${returnQuantity.toFixed(3)}) exceeds remaining purchase quantity (${purchase.remainingQuantity.toFixed(3)}).`
        );
      }

      if (purchase.remainingBags.lt(returnBags)) {
        throw new Error(
          `Return bags (${returnBags.toFixed(3)}) exceeds remaining purchase bags (${purchase.remainingBags.toFixed(3)}).`
        );
      }

      // 1. Create Purchase Return
      const purchaseReturn = await tx.purchaseReturn.create({
        data: {
          purchaseId: purchase.id,
          returnDate: new Date(data.returnDate),
          quantity: returnQuantity,
          bags: returnBags.isZero() ? null : returnBags,
          returnAmount: returnAmount,
          reason: data.reason || null,
          remarks: data.remarks || null,
          createdBy: data.createdBy ?? null,
        },
      });

      // 2. Decrement remaining quantity/bags in Purchase
      const result = await tx.purchase.updateMany({
        where: { 
          id: purchase.id,
          remainingQuantity: { gte: returnQuantity },
          ...(returnBags.gt(0) ? { remainingBags: { gte: returnBags } } : {})
        },
        data: {
          remainingQuantity: { decrement: returnQuantity },
          ...(returnBags.gt(0) ? { remainingBags: { decrement: returnBags } } : {}),
        },
      });

      if (result.count === 0) {
        throw new Error('Stock changed concurrently or insufficient stock. Please retry.');
      }

      // Auto update status
      const updatedPurchase = await tx.purchase.findUnique({
        where: { id: purchase.id },
        select: { remainingQuantity: true, status: true },
      });
      if (updatedPurchase && updatedPurchase.status !== "CLOSED") {
        const newStatus = updatedPurchase.remainingQuantity.gt(0) ? "ACTIVE" : "DEPLETED";
        if (updatedPurchase.status !== newStatus) {
          await tx.purchase.update({
            where: { id: purchase.id },
            data: { status: newStatus },
          });
        }
      }

      // 3. Decrement outstanding balance in Supplier
      if (purchase.supplierId) {
        await tx.supplier.update({
          where: { id: purchase.supplierId },
          data: {
            outstandingBalance: { decrement: returnAmount },
          },
        });
      }

      return purchaseReturn;
    });
  }

  async getPurchaseReturns(
    filters: {
      supplierId?: string | bigint;
      startDate?: string;
      endDate?: string;
      search?: string;
    },
    page: number = 1,
    limit: number = 20
  ) {
    const where: Prisma.PurchaseReturnWhereInput = {};

    if (filters.supplierId) {
      where.purchase = { supplierId: BigInt(filters.supplierId) };
    }

    if (filters.search) {
      const search = filters.search;
      where.OR = [
        { purchase: { lotNo: { contains: search } } },
        { purchase: { billNo: { contains: search } } },
        { purchase: { supplier: { name: { contains: search } } } },
        { purchase: { vegetable: { name: { contains: search } } } },
        { reason: { contains: search } },
      ];
    }

    if (filters.startDate || filters.endDate) {
      where.returnDate = {};
      if (filters.startDate) {
        where.returnDate.gte = new Date(filters.startDate);
      }
      if (filters.endDate) {
        where.returnDate.lte = new Date(filters.endDate);
      }
    }

    const skip = (page - 1) * limit;

    const [items, total] = await Promise.all([
      prisma.purchaseReturn.findMany({
        where,
        skip,
        take: limit,
        orderBy: { returnDate: "desc" },
        include: {
          purchase: {
            include: {
              supplier: { select: { id: true, name: true, tamilName: true } },
              vegetable: { select: { id: true, name: true, tamilName: true } },
            },
          },
        },
      }),
      prisma.purchaseReturn.count({ where }),
    ]);

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

  async getPurchaseReturnById(id: bigint) {
    return await prisma.purchaseReturn.findUnique({
      where: { id },
      include: {
        purchase: {
          include: {
            supplier: true,
            vegetable: true,
          },
        },
      },
    });
  }

  async deletePurchaseReturn(id: bigint) {
    return await prisma.$transaction(async (tx) => {
      const purchaseReturn = await tx.purchaseReturn.findUnique({
        where: { id },
        include: { purchase: true },
      });

      if (!purchaseReturn) {
        throw new Error("Purchase return not found");
      }

      // Revert stock
      await tx.purchase.update({
        where: { id: purchaseReturn.purchaseId },
        data: {
          remainingQuantity: { increment: purchaseReturn.quantity },
          ...(purchaseReturn.bags ? { remainingBags: { increment: purchaseReturn.bags } } : {}),
        },
      });

      const updatedPurchase = await tx.purchase.findUnique({
        where: { id: purchaseReturn.purchaseId },
        select: { remainingQuantity: true, status: true },
      });
      if (updatedPurchase && updatedPurchase.status !== "CLOSED") {
        const newStatus = updatedPurchase.remainingQuantity.gt(0) ? "ACTIVE" : "DEPLETED";
        if (updatedPurchase.status !== newStatus) {
          await tx.purchase.update({
            where: { id: purchaseReturn.purchaseId },
            data: { status: newStatus },
          });
        }
      }

      // Revert Supplier Balance
      if (purchaseReturn.purchase.supplierId) {
        await tx.supplier.update({
          where: { id: purchaseReturn.purchase.supplierId },
          data: {
            outstandingBalance: { increment: purchaseReturn.returnAmount },
          },
        });
      }

      return await tx.purchaseReturn.delete({
        where: { id },
      });
    });
  }

  async getAllForExport(filters: {
    supplierId?: string | bigint;
    startDate?: string;
    endDate?: string;
    search?: string;
  }) {
    const where: Prisma.PurchaseReturnWhereInput = {};

    if (filters.supplierId) {
      where.purchase = { supplierId: BigInt(filters.supplierId) };
    }

    if (filters.search) {
      const search = filters.search;
      where.OR = [
        { purchase: { lotNo: { contains: search } } },
        { purchase: { billNo: { contains: search } } },
        { purchase: { supplier: { name: { contains: search } } } },
        { purchase: { vegetable: { name: { contains: search } } } },
        { reason: { contains: search } },
      ];
    }

    if (filters.startDate || filters.endDate) {
      where.returnDate = {};
      if (filters.startDate) {
        where.returnDate.gte = new Date(filters.startDate);
      }
      if (filters.endDate) {
        where.returnDate.lte = new Date(filters.endDate);
      }
    }

    return prisma.purchaseReturn.findMany({
      where,
      orderBy: { returnDate: "desc" },
      include: {
        purchase: {
          include: {
            supplier: { select: { id: true, name: true, tamilName: true } },
            vegetable: { select: { id: true, name: true, tamilName: true } },
          },
        },
      },
    });
  }
}
