/** جدول الأقساط والترحيل الآلي (v8.1)
 *  - توليد جدول 8 أقساط شهرية لكل طالب من تاريخ التسجيل
 *  - توزيع (ترحيل) أي دفعة جديدة تلقائياً على الأقساط غير المسددة بالأقدم أولاً
 *  - إعادة بناء الجدول عند تغيير القسط أو الخصم أو تاريخ التسجيل (مع الحفاظ على المسدد)
 *  تُستخدم هذه المكتبة في الخادم فقط (API الطلبة والمدفوعات). */

import { db } from "@/lib/db";
import { INSTALLMENT_MONTHS } from "@/lib/fee-schedule";

/** قسط الطالب الصافي بعد الخصم */
export function netFeeOf(totalFee: number, discountPercent: number): number {
  return Math.max(0, Math.round(totalFee * (1 - (discountPercent || 0) / 100)));
}

/** تاريخ استحقاق القسط رقم seq — شهر التسجيل نفسه هو القسط الأول */
export function installmentDueDate(registeredAt: Date, seq: number): Date {
  return new Date(registeredAt.getFullYear(), registeredAt.getMonth() + (seq - 1), 28);
}

/** توليد/إعادة بناء جدول أقساط طالب — يُستدعى عند إنشاء الطالب وعند تعديل بيانات القسط.
 *  يحفظ المبلغ المسدد سابقاً ويعيد توزيعه على الجدول الجديد. */
export async function rebuildInstallmentScheduleTx(
  tx: import("@prisma/client").Prisma.TransactionClient,
  studentId: string,
  paidTotalOverride?: number,
): Promise<void> {
  const student = await tx.student.findUnique({
    where: { id: studentId },
    select: { id: true, totalFee: true, discountPercent: true, registeredAt: true },
  });
  if (!student) return;

  const paymentTotals = paidTotalOverride === undefined
    ? await tx.payment.aggregate({ where: { studentId }, _sum: { amount: true } })
    : null;
  const net = netFeeOf(student.totalFee, student.discountPercent);
  const monthly = net / INSTALLMENT_MONTHS;
  let remainingPaid = paidTotalOverride ?? paymentTotals?._sum.amount ?? 0;

  await tx.installment.deleteMany({ where: { studentId } });

  const rows: { studentId: string; seq: number; dueDate: Date; amount: number; paidAmount: number; status: string }[] = [];
  for (let seq = 1; seq <= INSTALLMENT_MONTHS; seq++) {
    const amount = Math.round(monthly);
    const paid = Math.min(amount, Math.max(0, remainingPaid));
    remainingPaid -= paid;
    rows.push({
      studentId,
      seq,
      dueDate: installmentDueDate(new Date(student.registeredAt), seq),
      amount,
      paidAmount: paid,
      status: paid <= 0 ? "UNPAID" : paid >= amount ? "PAID" : "PARTIAL",
    });
  }

  // تصحيح التقريب ثم حفظ أي رصيد زائد على آخر قسط كما يفعل الترحيل الآلي.
  const roundedSum = rows.reduce((sum, row) => sum + row.amount, 0);
  if (rows.length > 0 && roundedSum !== net) rows[rows.length - 1].amount += net - roundedSum;
  if (rows.length > 0 && remainingPaid > 0) rows[rows.length - 1].paidAmount += remainingPaid;
  if (rows.length > 0) {
    const last = rows[rows.length - 1];
    last.status = last.paidAmount <= 0 ? "UNPAID" : last.paidAmount >= last.amount ? "PAID" : "PARTIAL";
  }

  await tx.installment.createMany({ data: rows });
}

/** إعادة بناء الجدول اعتماداً على سجل الدفعات بوصفه المصدر الأساسي للمبلغ المسدد. */
export async function rebuildInstallmentSchedule(studentId: string): Promise<void> {
  await db.$transaction(async (tx) => {
    // احتفظ بأرصدة الأقساط الموجودة في قواعد البيانات القديمة التي قد لا
    // تحتوي على سجل Payment كامل. إذا لم يوجد رصيد في الجدول نستخدم الدفعات.
    const scheduled = await tx.installment.aggregate({ where: { studentId }, _sum: { paidAmount: true } });
    const paidTotal = scheduled._sum.paidAmount;
    await rebuildInstallmentScheduleTx(tx, studentId, paidTotal && paidTotal > 0 ? paidTotal : undefined);
  });
}
/** الترحيل الآلي: تخصيص مبلغ دفعة جديدة على الأقساط غير المسددة (الأقدم أولاً).
 *  يُستدعى داخل معاملة إنشاء الدفعة. */
export async function allocatePaymentToInstallments(
  tx: { installment: { findMany: Function; update: Function } },
  studentId: string,
  paymentAmount: number
): Promise<void> {
  const unpaid = await tx.installment.findMany({
    where: { studentId, status: { not: "PAID" } },
    orderBy: { seq: "asc" },
  });
  let remain = paymentAmount;
  for (const inst of unpaid) {
    if (remain <= 0) break;
    const space = inst.amount - inst.paidAmount;
    if (space <= 0) continue;
    const take = Math.min(space, remain);
    const newPaid = inst.paidAmount + take;
    await tx.installment.update({
      where: { id: inst.id },
      data: {
        paidAmount: newPaid,
        status: newPaid >= inst.amount ? "PAID" : "PARTIAL",
      },
    });
    remain -= take;
  }
  // إذا بقي مبلغ فوق كل الأقساط (دفع زائد) يُضاف لآخر قسط كرصيد
  if (remain > 0 && unpaid.length > 0) {
    const last = unpaid[unpaid.length - 1];
    const newPaid = last.paidAmount + remain;
    await tx.installment.update({
      where: { id: last.id },
      data: {
        paidAmount: newPaid,
        status: newPaid >= last.amount ? "PAID" : "PARTIAL",
      },
    });
  }
}

/** ضمان وجود جدول أقساط للطالب (يُستخدم للترحيل الخلفي عند أول حاجة) */
export async function ensureInstallmentSchedule(studentId: string): Promise<void> {
  const count = await db.installment.count({ where: { studentId } });
  if (count === 0) await rebuildInstallmentSchedule(studentId);
}
