import { Booking } from "@/db/models";
import { AppError } from "@/lib/errors";
import { toNumber } from "@/lib/serialize";
import { getCommerceSettings, refundPercentForHours } from "@/services/commerce";
import { recordRefund } from "@/services/ledger";

function roundMoney(value: number) {
  return Math.round(value * 100) / 100;
}

/**
 * Apply cancellation/rejection refund into the traveler wallet + ledger.
 * Returns refund amount (0 if none). Mutates and saves booking.
 */
export async function applyRefundToWallet(params: {
  booking: InstanceType<typeof Booking>;
  reason: "CANCEL" | "OWNER_REJECT";
}) {
  const booking = params.booking;
  const paymentStatus = booking.payment?.status;
  if (paymentStatus !== "PAID" && paymentStatus !== "PARTIALLY_REFUNDED") {
    if (booking.payment && paymentStatus === "PENDING") {
      booking.payment.status = "CANCELLED";
      booking.payment.updatedAt = new Date();
    }
    await booking.save();
    return { refundLyd: 0, refundPercent: 0 };
  }

  if (toNumber(booking.refundLyd) > 0) {
    return {
      refundLyd: toNumber(booking.refundLyd),
      refundPercent: toNumber(booking.refundPercent),
    };
  }

  const settings = await getCommerceSettings();
  let refundPercent = 0;
  if (params.reason === "OWNER_REJECT") {
    refundPercent = settings.ownerRejectRefundPercent;
  } else {
    const hours =
      (new Date(booking.checkIn).getTime() - Date.now()) / (1000 * 60 * 60);
    refundPercent = refundPercentForHours(settings.refundTiers, hours);
  }

  const paidLyd = toNumber(booking.totalLyd);
  const refundLyd = roundMoney((paidLyd * refundPercent) / 100);
  if (!(refundLyd > 0)) {
    await booking.save();
    return { refundLyd: 0, refundPercent };
  }

  const result = await recordRefund({
    booking,
    type: refundPercent >= 100 ? "FULL" : "PARTIAL",
    amountLyd: refundLyd,
    reasonCode: params.reason === "OWNER_REJECT" ? "HOST_CANCEL" : "GUEST_CANCEL",
    source: params.reason === "OWNER_REJECT" ? "OWNER_REJECT" : "CANCEL",
  });

  return {
    refundLyd: result.refundLyd,
    refundPercent: result.refundPercent,
  };
}

export async function previewCancelRefund(bookingId: string, userId: string) {
  const { isBookingsMysql } = await import("@/db/activeDatabase");
  let booking: any = null;
  if (isBookingsMysql()) {
    const { findBookingByIdMysql } = await import("@/db/mysql/bookings");
    booking = await findBookingByIdMysql(bookingId);
  } else {
    booking = await Booking.findOne({ _id: bookingId, deletedAt: null });
  }
  if (!booking) throw new AppError(404, "Not found");
  const customerId = String(booking.customerId);
  if (customerId !== userId) throw new AppError(403, "Forbidden");

  const settings = await getCommerceSettings();
  const paymentStatus = booking.payment?.status;
  if (paymentStatus !== "PAID" && paymentStatus !== "PARTIALLY_REFUNDED") {
    return { refundLyd: 0, refundPercent: 0, paid: false };
  }
  const hours =
    (new Date(booking.checkIn).getTime() - Date.now()) / (1000 * 60 * 60);
  const refundPercent = refundPercentForHours(settings.refundTiers, hours);
  const already = toNumber(booking.refundLyd);
  const remaining = Math.max(0, toNumber(booking.totalLyd) - already);
  const refundLyd = roundMoney(Math.min(remaining, (toNumber(booking.totalLyd) * refundPercent) / 100));
  return { refundLyd, refundPercent, paid: true, hoursUntilCheckIn: hours };
}
