import { Types } from 'mongoose';
import { PaymentType } from '@/shared/constants/enum.constant';
import { getObjectId } from '@/shared/utils/commonHelper';
import { calculateStageStatus, PaymentStatus } from '@/modules/customer/payment/payment.helper';

type PaymentPlanLike = {
  downPayment?: number;
  downPaymentType?: string;
  installmentMonth?: number;
};

/**
 * Scheduled booking / down payment for the plan (matches BookOrHoldUnitForm).
 */
export const computeScheduledBookingFromPlan = (
  totalAmount: number,
  paymentPlan: PaymentPlanLike,
): number => {
  const t = Math.round(Number(totalAmount) || 0);
  const down = Number(paymentPlan?.downPayment ?? 0);
  const typ = String(paymentPlan?.downPaymentType ?? 'percentage').toLowerCase();
  if (typ === PaymentType.PERCENTAGE || typ === 'percentage') {
    return Math.round((t * down) / 100);
  }
  return Math.round(down);
};

/**
 * Split (total - scheduled booking) across N installments; last row matches
 * legacy customer.service formula so totals reconcile with totalAmount.
 */
export const buildEmiScheduledAmounts = (
  totalAmount: number,
  scheduledBooking: number,
  installmentMonth: number,
): number[] => {
  const n = Math.max(0, Math.floor(Number(installmentMonth) || 0));
  if (n === 0) return [];

  const ta = Number(totalAmount) || 0;
  const sb = Number(scheduledBooking) || 0;
  const remainingAfterBooking = Math.max(0, ta - sb);
  const installmentAmount = n > 0 ? remainingAfterBooking / n : 0;

  const amounts: number[] = [];
  for (let i = 1; i <= n; i++) {
    if (i === n) {
      const finalInstallmentAmount = ta - sb - installmentAmount * (i - 1);
      amounts.push(Math.round(finalInstallmentAmount));
    } else {
      amounts.push(Math.round(installmentAmount));
    }
  }

  return amounts;
};

export type InitialTimelineStage = {
  label: string;
  dueDate?: Date;
  dueAmount: number;
  paid: number;
  status: PaymentStatus;
  bankAccountId?: string | Types.ObjectId;
  /** Ledger lines so `addPaymentEntry` sum matches `paid` and UI history has `_id` for edit/delete */
  payments?: Array<{
    amount: number;
    receivedAt: Date;
    bankAccountId?: Types.ObjectId;
  }>;
};

/**
 * Apply received cash to booking milestone first, then EMIs in order.
 * `dueAmount` on each stage is **remaining** balance for that line.
 */
export const buildInitialTimelineWaterfall = (input: {
  scheduledBooking: number;
  emiAmounts: number[];
  receivedTotal: number;
  dueDateBase: Date;
  bankAccountId?: string | Types.ObjectId;
}): InitialTimelineStage[] => {
  const {
    scheduledBooking,
    emiAmounts,
    receivedTotal,
    dueDateBase,
    bankAccountId,
  } = input;

  const sb = Math.max(0, Math.round(Number(scheduledBooking) || 0));
  let cash = Math.max(0, Number(receivedTotal) || 0);

  const bPaid = Math.min(cash, sb);
  const bRem = Math.max(0, sb - bPaid);
  cash -= bPaid;

  const bookingDueDate = new Date(dueDateBase);
  const bookingPayments =
    bPaid > 0
      ? [
          {
            amount: bPaid,
            receivedAt: bookingDueDate,
            ...(bankAccountId
              ? { bankAccountId: getObjectId(bankAccountId as any) }
              : {}),
          },
        ]
      : undefined;

  const timeline: InitialTimelineStage[] = [
    {
      label: 'Booking Amount',
      dueDate: bookingDueDate,
      dueAmount: bRem,
      paid: bPaid,
      status: calculateStageStatus(
        bPaid,
        bRem,
        bookingDueDate,
      ) as PaymentStatus,
      ...(bankAccountId ? { bankAccountId } : {}),
      ...(bookingPayments ? { payments: bookingPayments } : {}),
    },
  ];

  for (let i = 0; i < emiAmounts.length; i++) {
    const s = Math.max(0, Math.round(Number(emiAmounts[i]) || 0));
    const p = Math.min(cash, s);
    const rem = Math.max(0, s - p);
    cash -= p;

    const dueDate = new Date(
      dueDateBase.getFullYear(),
      dueDateBase.getMonth() + i + 1,
      1,
    );

    const instPayments =
      p > 0 ? [{ amount: p, receivedAt: new Date(dueDate) }] : undefined;

    timeline.push({
      label: `Installment ${i + 1}`,
      dueDate,
      dueAmount: rem,
      paid: p,
      status: calculateStageStatus(p, rem, dueDate) as PaymentStatus,
      ...(instPayments ? { payments: instPayments } : {}),
    });
  }

  return timeline;
};

/**
 * Convenience: plan + totals + received → full timeline for createCustomer.
 */
export const buildBuilderCustomerPaymentTimeline = (params: {
  totalAmount: number;
  paymentPlan: PaymentPlanLike;
  receivedTotal: number;
  dueDateBase?: Date;
  bankAccountId?: string | Types.ObjectId;
}): InitialTimelineStage[] => {
  const { totalAmount, paymentPlan, receivedTotal, dueDateBase, bankAccountId } =
    params;

  const ta = Math.round(Number(totalAmount) || 0);
  const scheduledBooking = computeScheduledBookingFromPlan(ta, paymentPlan);
  const n = Math.max(0, Math.floor(Number(paymentPlan?.installmentMonth) || 0));
  const emiAmounts = buildEmiScheduledAmounts(ta, scheduledBooking, n);

  return buildInitialTimelineWaterfall({
    scheduledBooking,
    emiAmounts,
    receivedTotal,
    dueDateBase: dueDateBase ? new Date(dueDateBase) : new Date(),
    bankAccountId,
  });
};
