import { AppSetting, Booking } from "@/db/models";
import { createId } from "@/db/ids";
import { toNumber } from "@/lib/serialize";
import { isSettingsMysql } from "@/db/activeDatabase";
import { getAppSettingMysql, upsertAppSettingMysql } from "@/db/mysql/settings";

export type InvoiceSnapshot = {
  _id: string;
  invoiceNumber: string;
  total: number;
  currency: string;
  issuedAt: Date;
  createdAt: Date;
  /** Frozen copy of booking/pricing at issue time */
  bookingId: string;
  guestName: string;
  guestEmail?: string | null;
  guestPhone?: string | null;
  propertyTitleAr: string;
  propertyTitleEn: string;
  propertyAddress?: string | null;
  cityNameAr?: string | null;
  cityNameEn?: string | null;
  hostName?: string | null;
  checkIn: Date;
  checkOut: Date;
  nights: number;
  guests: number;
  nightlyRateTnd: number;
  subtotalTnd: number;
  cleaningFeeTnd: number;
  platformFeeTnd: number;
  taxesTnd: number;
  discountTnd: number;
  totalTnd: number;
  totalLyd: number;
  exchangeRateRate: number;
  paymentStatus: string;
  paymentProvider: string;
  bookingStatus: string;
};

/** Atomic yearly sequence → INV-2026-00001 */
export async function nextInvoiceNumber(now = new Date()): Promise<string> {
  const year = now.getFullYear();
  const key = `invoice_seq_${year}`;

  if (isSettingsMysql()) {
    const { sqlQuery } = await import("@/db/mysql/pool");
    type Row = import("mysql2/promise").RowDataPacket;
    const current = (await getAppSettingMysql<{ seq?: number }>(key)) || { seq: 0 };
    const rows = await sqlQuery<Row[]>(
      `SELECT invoice_number FROM bookings
       WHERE invoice_number LIKE ?
       ORDER BY invoice_number DESC
       LIMIT 1`,
      [`INV-${year}-%`],
    );
    let maxExisting = 0;
    const last = rows[0]?.invoice_number ? String(rows[0].invoice_number) : "";
    const m = last.match(new RegExp(`^INV-${year}-(\\d+)$`));
    if (m) maxExisting = Number(m[1]);
    const seq = Math.max(Number(current.seq || 0), maxExisting) + 1;
    await upsertAppSettingMysql(key, { seq });
    return `INV-${year}-${String(seq).padStart(5, "0")}`;
  }

  await AppSetting.updateOne({ key }, { $setOnInsert: { value: { seq: 0 } } }, { upsert: true });

  const updated = await AppSetting.findOneAndUpdate(
    { key },
    { $inc: { "value.seq": 1 } },
    { new: true },
  );

  const seq = Number((updated?.value as { seq?: number } | undefined)?.seq) || 1;
  return `INV-${year}-${String(seq).padStart(5, "0")}`;
}

type BuildArgs = {
  booking: InstanceType<typeof Booking> | Record<string, any>;
  bookingStatus: string;
  customer?: { fullName?: string; email?: string; phone?: string | null } | null;
  property?: {
    titleAr?: string;
    titleEn?: string;
    address?: string | null;
    city?: { nameAr?: string; nameEn?: string } | null;
  } | null;
  owner?: { fullName?: string } | null;
  invoiceNumber?: string;
  issuedAt?: Date;
};

export async function buildInvoiceSnapshot(args: BuildArgs): Promise<InvoiceSnapshot> {
  const booking = args.booking as any;
  const issuedAt = args.issuedAt || new Date();
  const invoiceNumber = args.invoiceNumber || (await nextInvoiceNumber(issuedAt));
  const nights = Math.max(1, Number(booking.nights) || 1);
  const subtotalTnd = toNumber(booking.subtotalTnd);
  const nightlyRateTnd = Math.round((subtotalTnd / nights) * 100) / 100;

  return {
    _id: createId(),
    invoiceNumber,
    total: toNumber(booking.totalLyd),
    currency: "LYD",
    issuedAt,
    createdAt: issuedAt,
    bookingId: String(booking._id || booking.id),
    guestName: args.customer?.fullName || "ضيف سفر ليبيا",
    guestEmail: args.customer?.email ?? null,
    guestPhone: args.customer?.phone ?? null,
    propertyTitleAr: args.property?.titleAr || args.property?.titleEn || "إقامة",
    propertyTitleEn: args.property?.titleEn || args.property?.titleAr || "Stay",
    propertyAddress: args.property?.address ?? null,
    cityNameAr: args.property?.city?.nameAr ?? null,
    cityNameEn: args.property?.city?.nameEn ?? null,
    hostName: args.owner?.fullName ?? null,
    checkIn: new Date(booking.checkIn),
    checkOut: new Date(booking.checkOut),
    nights,
    guests: Number(booking.guests) || 1,
    nightlyRateTnd,
    subtotalTnd,
    cleaningFeeTnd: toNumber(booking.cleaningFeeTnd),
    platformFeeTnd: toNumber(booking.platformFeeTnd),
    taxesTnd: toNumber(booking.taxesTnd),
    discountTnd: toNumber(booking.discountTnd),
    totalTnd: toNumber(booking.totalTnd),
    totalLyd: toNumber(booking.totalLyd),
    exchangeRateRate: toNumber(booking.exchangeRateRate),
    paymentStatus: booking.payment?.status || "PAID",
    paymentProvider: booking.payment?.provider || "DEMO",
    bookingStatus: args.bookingStatus,
  };
}

/** Attach invoice if missing (idempotent). */
export async function ensureBookingInvoice(args: BuildArgs): Promise<InvoiceSnapshot | null> {
  const booking = args.booking as any;
  if (booking.invoice?.invoiceNumber) {
    return booking.invoice as InvoiceSnapshot;
  }
  const snapshot = await buildInvoiceSnapshot(args);
  booking.invoice = snapshot;
  if (typeof booking.save === "function") {
    await booking.save();
  }
  return snapshot;
}
