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

export interface PurchaseCalculationInput {
  unit: "KG" | "BOX" | "BAG" | "THAAR" | "PIECES";
  rateType?: "UNIT" | "KG";
  quantity?: Prisma.Decimal | number | string;
  bags?: Prisma.Decimal | number | string;
  ratePerUnit: Prisma.Decimal | number | string;
  transportFee?: Prisma.Decimal | number | string;
  loadingUnloadingFee?: Prisma.Decimal | number | string;
  otherCharges?: Prisma.Decimal | number | string;
  thaiyalCharges?: Prisma.Decimal | number | string;
  extraCharges?: Prisma.Decimal | number | string;
  advanceAmount?: Prisma.Decimal | number | string;
}

export function calculatePurchaseTotals(input: PurchaseCalculationInput) {
  const ratePerUnit = new Prisma.Decimal(input.ratePerUnit);
  if (ratePerUnit.lt(1)) {
    throw new Error("Rate per unit must be at least 1");
  }

  let quantity: Prisma.Decimal;
  let bags = new Prisma.Decimal(0);
  let weightPerBag: Prisma.Decimal | null = null;
  let goodsTotal: Prisma.Decimal;

  if (input.unit === "KG") {
    if (input.quantity === undefined) {
      throw new Error("Quantity is required for unit KG");
    }

    quantity = new Prisma.Decimal(input.quantity);
    if (quantity.lte(0)) {
      throw new Error("Quantity must be greater than 0");
    }

    goodsTotal = quantity.mul(ratePerUnit).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);
  } else {
    const rawBags = input.bags !== undefined && input.bags !== null ? Number(input.bags) : 0;
    const rawQuantity = input.quantity !== undefined && input.quantity !== null ? Number(input.quantity) : 0;

    let numBags = rawBags > 0 ? rawBags : 0;
    let numQuantity = rawQuantity > 0 ? rawQuantity : 0;

    if (numBags <= 0) {
      throw new Error(
        input.unit === "THAAR"
          ? "Number of thaar must be greater than 0"
          : input.unit === "PIECES"
            ? "Number of pieces must be greater than 0"
            : "Number of bags must be greater than 0"
      );
    }

    bags = new Prisma.Decimal(numBags);
    quantity = new Prisma.Decimal(numQuantity).toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);

    if (input.rateType === "KG") {
      if (numQuantity <= 0) {
        throw new Error("Quantity must be greater than 0 when Rate Type is Rate / Kg");
      }
      goodsTotal = quantity.mul(ratePerUnit).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);
    } else {
      goodsTotal = bags.mul(ratePerUnit).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);
    }
  }

  const transportFee = new Prisma.Decimal(input.transportFee ?? 0);
  const loadingUnloadingFee = new Prisma.Decimal(input.loadingUnloadingFee ?? 0);
  const otherCharges = new Prisma.Decimal(input.otherCharges ?? 0);
  const thaiyalCharges = new Prisma.Decimal(input.thaiyalCharges ?? 0);
  const extraCharges = new Prisma.Decimal(input.extraCharges ?? 0);
  
  if (
    transportFee.lt(0) ||
    loadingUnloadingFee.lt(0) ||
    otherCharges.lt(0) ||
    thaiyalCharges.lt(0) ||
    extraCharges.lt(0)
  ) {
    throw new Error("Charges and fees cannot be negative");
  }
  
  let advanceAmount = new Prisma.Decimal(input.advanceAmount ?? 0);
  if (advanceAmount.lt(0)) {
    throw new Error("Advance amount cannot be negative");
  }

  const surchargeTotal = transportFee
    .add(loadingUnloadingFee)
    .add(otherCharges)
    .add(thaiyalCharges)
    .add(extraCharges)
    .toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);

  const grandTotal = goodsTotal.add(surchargeTotal).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);
  const costPerUnit = quantity.gt(0)
    ? grandTotal.div(quantity).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP)
    : grandTotal.div(bags).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);

  if (advanceAmount.gt(grandTotal)) {
    throw new Error(`Advance amount cannot exceed the grand total (${grandTotal.toFixed(2)}).`);
  }
  advanceAmount = advanceAmount.toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);
  const balanceAmount = grandTotal.sub(advanceAmount).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);

  return {
    quantity,
    bags,
    weightPerBag,
    goodsTotal,
    surchargeTotal,
    grandTotal,
    costPerUnit,
    advanceAmount,
    balanceAmount,
  };
}
