import type { DepositType, PaymentOption, PaymentPlan, SaleMode } from "@prisma/client";
import { depositPerUnit } from "./preorder";

export type QuoteLine = {
  key: string;
  unitPrice: number;
  qty: number;
  mode: SaleMode;
  batch?: { paymentOption: PaymentOption; depositType: DepositType; depositValue: number; balanceDueAt: Date } | null;
};

export type QuoteInput = {
  lines: QuoteLine[];
  zone: { fee: number; freeOver: number | null } | null;
  discount: { type: "PERCENT" | "FIXED"; value: number; minSubtotal: number } | null;
  plan: PaymentPlan;
  splitShipment: boolean; // only meaningful for mixed carts
  methods: { full: boolean; deposit: boolean; cod: boolean };
};

export type Quote = {
  subtotal: number;
  discount: number;
  shipping: number;
  total: number;
  dueNow: number;
  balance: number;
  balanceDueAt: Date | null;
  hasPreorder: boolean;
  hasReady: boolean;
  mixed: boolean;
  allowedPlans: PaymentPlan[];
  lineDueNow: Record<string, { perUnit: number; total: number }>;
  errors: string[];
};

/** Pure, deterministic pricing used by the checkout server action. Never trust client totals. */
export function quote(input: QuoteInput): Quote {
  const errors: string[] = [];
  const lines = input.lines.filter((l) => l.qty > 0);
  const subtotal = lines.reduce((s, l) => s + l.unitPrice * l.qty, 0);
  const hasPreorder = lines.some((l) => l.mode === "PRE_ORDER");
  const hasReady = lines.some((l) => l.mode === "READY_STOCK");
  const mixed = hasPreorder && hasReady;

  // Which payment plans make sense for this cart
  const preLines = lines.filter((l) => l.mode === "PRE_ORDER" && l.batch);
  const anyDepositable = preLines.some((l) => l.batch!.paymentOption !== "FULL_ONLY");
  const anyDepositRequired = preLines.some((l) => l.batch!.paymentOption === "DEPOSIT_REQUIRED");
  const allowedPlans: PaymentPlan[] = [];
  if (input.methods.full && !anyDepositRequired) allowedPlans.push("FULL");
  if (input.methods.deposit && anyDepositable) allowedPlans.push("DEPOSIT");
  if (input.methods.cod && !hasPreorder) allowedPlans.push("COD");
  if (lines.length && !allowedPlans.includes(input.plan)) errors.push("The selected payment option isn't available for the items in your bag.");

  let discount = 0;
  if (input.discount) {
    if (subtotal < input.discount.minSubtotal) {
      errors.push(`This code needs a subtotal of at least ৳${input.discount.minSubtotal.toLocaleString("en-IN")}.`);
    } else {
      discount = input.discount.type === "PERCENT" ? Math.floor((subtotal * Math.min(100, input.discount.value)) / 100) : input.discount.value;
      discount = Math.min(discount, subtotal);
    }
  }

  let shipping = 0;
  if (!input.zone) {
    if (lines.length) errors.push("Choose a delivery district.");
  } else {
    const free = input.zone.freeOver != null && subtotal - discount >= input.zone.freeOver;
    shipping = free ? 0 : input.zone.fee;
    if (mixed && input.splitShipment && !free) shipping = input.zone.fee * 2;
  }

  const total = subtotal - discount + shipping;

  const lineDueNow: Quote["lineDueNow"] = {};
  let lineDue = 0;
  let balanceDueAt: Date | null = null;
  for (const l of lines) {
    let perUnit = l.unitPrice;
    if (input.plan === "COD") perUnit = 0;
    else if (input.plan === "DEPOSIT" && l.mode === "PRE_ORDER" && l.batch && l.batch.paymentOption !== "FULL_ONLY") {
      perUnit = depositPerUnit(l.batch, l.unitPrice);
      if (perUnit < l.unitPrice && (!balanceDueAt || l.batch.balanceDueAt < balanceDueAt)) balanceDueAt = l.batch.balanceDueAt;
    }
    lineDueNow[l.key] = { perUnit, total: perUnit * l.qty };
    lineDue += perUnit * l.qty;
  }

  // Shipping is collected with the first payment; discounts come off the balance first.
  let dueNow = input.plan === "COD" ? 0 : Math.min(lineDue + shipping, total);
  if (input.plan === "FULL") dueNow = total;
  const balance = total - dueNow;
  if (balance === 0) balanceDueAt = null;

  return { subtotal, discount, shipping, total, dueNow, balance, balanceDueAt, hasPreorder, hasReady, mixed, allowedPlans, lineDueNow, errors };
}
