/**
 * ثبت سفارش و پرداخت.
 *
 * جریان:
 * ۱) placeOrder: سبد و مبالغ دوباره در سرور محاسبه می‌شوند، موجودی به‌صورت اتمیک رزرو (کم) می‌شود،
 *    سفارش «در انتظار پرداخت» و رکورد پرداخت ساخته می‌شود و آدرس درگاه برگردانده می‌شود.
 * ۲) completePayment (بازگشت از درگاه): تراکنش نزد درگاه تأیید می‌شود؛
 *    موفق ← سفارش «پرداخت شده»، سبد خالی، پیامک؛ ناموفق ← سفارش لغو و موجودی برگردانده می‌شود.
 */
import "server-only";
import { db } from "@/lib/db";
import { env } from "@/lib/env";
import { faNumber } from "@/lib/format";
import { randomDigits } from "@/lib/crypto";
import { paymentGateway, gatewayById } from "@/integrations/payment";
import { sms } from "@/integrations/sms";
import { listAddresses } from "./addresses";
import { getCartLines } from "./cart-store";
import { priceCart } from "./cart";

export class CheckoutError extends Error {}

/** شماره سفارش خوانا: ZH-<سال شمسی>-<۵ رقم> */
function orderCode() {
  const year = new Intl.DateTimeFormat("en-US-u-ca-persian", { year: "numeric", timeZone: "Asia/Tehran" }).format(new Date()).replace(/\D/g, "");
  return `ZH-${year}-${randomDigits(5)}`;
}

export async function placeOrder(
  user: { id: string; phone: string },
  input: { addressId: string; shippingMethodId: string; discountCode?: string },
): Promise<{ redirectUrl: string }> {
  await releaseExpiredOrders();
  const address = (await listAddresses(user.id)).find((a) => a.id === input.addressId);
  if (!address) throw new CheckoutError("آدرس انتخاب‌شده معتبر نیست");

  const lines = await getCartLines();
  if (!lines.length) throw new CheckoutError("سبد خرید خالی است");

  const pricing = await priceCart({ lines, discountCode: input.discountCode, shippingMethodId: input.shippingMethodId, province: address.province });
  if (!pricing.shipping.some((m) => m.methodId === input.shippingMethodId)) throw new CheckoutError("روش ارسال برای این آدرس در دسترس نیست");
  if (pricing.lines.some((l) => l.availableQty < l.qty)) throw new CheckoutError("برخی کالاها ناموجود شده‌اند؛ سبد را اصلاح کنید");
  if (input.discountCode && pricing.discountError) throw new CheckoutError(pricing.discountError);

  const discount = input.discountCode ? await db.discountCode.findUnique({ where: { code: input.discountCode.toUpperCase() } }) : null;
  const code = orderCode();

  const order = await db.$transaction(async (tx) => {
    // رزرو موجودی: فقط اگر هنوز کافی باشد (جلوگیری از فروش بیش از موجودی در خریدهای هم‌زمان)
    for (const l of pricing.lines) {
      const r = await tx.productVariant.updateMany({ where: { id: l.variantId, stock: { gte: l.qty } }, data: { stock: { decrement: l.qty } } });
      if (r.count === 0) throw new CheckoutError(`موجودی «${l.product.name}» کافی نیست`);
    }
    return tx.order.create({
      data: {
        code,
        userId: user.id,
        status: "PENDING_PAYMENT",
        receiver: address.receiver,
        phone: address.phone,
        province: address.province,
        city: address.city,
        addressLine: address.line,
        postalCode: address.postalCode,
        shippingMethodId: input.shippingMethodId,
        shippingCost: pricing.shippingCost,
        subtotal: pricing.subtotal,
        discount: pricing.discount,
        total: pricing.total,
        discountCodeId: discount && pricing.discount > 0 ? discount.id : null,
        items: {
          create: pricing.lines.map((l) => ({
            variantId: l.variantId,
            productName: l.product.name,
            variantLabel: [l.variant.colorName, l.variant.size].filter(Boolean).join(" · ") || null,
            price: l.variant.price,
            qty: l.qty,
          })),
        },
        history: { create: { status: "PENDING_PAYMENT" } },
      },
    });
  });

  // ایجاد تراکنش در درگاه
  const gateway = paymentGateway();
  try {
    const callbackUrl = new URL(`/api/payment/callback/${gateway.id}`, env().NEXT_PUBLIC_SITE_URL).toString();
    const { authority, redirectUrl } = await gateway.request({
      amountRial: order.total * 10,
      description: `سفارش ${order.code} از زهورا`,
      callbackUrl,
      mobile: user.phone,
      orderCode: order.code,
    });
    await db.payment.create({ data: { orderId: order.id, gateway: gateway.id, amount: order.total, authority } });
    return { redirectUrl };
  } catch (err) {
    console.error("[checkout] خطای درگاه", err);
    await cancelOrder(order.id, "خطا در اتصال به درگاه");
    throw new CheckoutError("اتصال به درگاه پرداخت ممکن نشد؛ دوباره تلاش کنید");
  }
}

/** لغو سفارش در انتظار پرداخت و برگرداندن موجودی رزروشده (فقط یک بار) */
async function cancelOrder(orderId: string, note: string) {
  await db.$transaction(async (tx) => {
    const changed = await tx.order.updateMany({ where: { id: orderId, status: "PENDING_PAYMENT" }, data: { status: "CANCELED" } });
    if (changed.count === 0) return;
    const items = await tx.orderItem.findMany({ where: { orderId } });
    for (const i of items) await tx.productVariant.update({ where: { id: i.variantId }, data: { stock: { increment: i.qty } } });
    await tx.orderStatusHistory.create({ data: { orderId, status: "CANCELED", note } });
  });
}

/** پردازش بازگشت از درگاه؛ خروجی: نتیجه و شماره سفارش برای صفحه نتیجه */
export async function completePayment(gatewayId: string, params: URLSearchParams): Promise<{ ok: boolean; orderCode?: string }> {
  const gateway = gatewayById(gatewayId);
  if (!gateway) return { ok: false };
  const { authority, ok } = gateway.parseCallback(params);
  if (!authority) return { ok: false };

  const payment = await db.payment.findUnique({ where: { authority }, include: { order: true } });
  if (!payment || payment.gateway !== gateway.id) return { ok: false };
  const order = payment.order;

  // بازگشت دوباره (مثلاً رفرش صفحه): نتیجه قبلی را برگردان
  if (payment.status === "SUCCEEDED") return { ok: true, orderCode: order.code };
  if (payment.status === "FAILED") return { ok: false, orderCode: order.code };

  const verified = ok ? await gateway.verify({ authority, amountRial: payment.amount * 10 }) : { ok: false as const, message: "انصراف کاربر" };

  if (!verified.ok) {
    await db.payment.update({ where: { id: payment.id }, data: { status: "FAILED", raw: { message: verified.message } } });
    await cancelOrder(order.id, `پرداخت ناموفق: ${verified.message}`);
    return { ok: false, orderCode: order.code };
  }

  const items = await db.orderItem.findMany({ where: { orderId: order.id }, include: { variant: { select: { productId: true } } } });
  await db.$transaction([
    db.payment.update({ where: { id: payment.id }, data: { status: "SUCCEEDED", refId: verified.refId, verifiedAt: new Date(), raw: { cardPan: verified.cardPan ?? null } } }),
    db.order.update({ where: { id: order.id }, data: { status: "PAID" } }),
    db.orderStatusHistory.create({ data: { orderId: order.id, status: "PAID", note: `کد پیگیری بانک: ${verified.refId}` } }),
    ...(order.discountCodeId ? [db.discountCode.update({ where: { id: order.discountCodeId }, data: { usedCount: { increment: 1 } } })] : []),
    ...items.map((i) => db.product.update({ where: { id: i.variant.productId }, data: { soldCount: { increment: i.qty } } })),
    // خالی کردن سبد کاربر (فقط اقلامی که در این سفارش بودند)
    db.cartItem.deleteMany({ where: { cart: { userId: order.userId }, variantId: { in: items.map((i) => i.variantId) } } }),
  ]);

  // پیامک تأیید سفارش به موبایل حساب خریدار (خطای پیامک نباید سفارش را خراب کند)
  const owner = await db.user.findUnique({ where: { id: order.userId }, select: { phone: true } });
  sms()
    .send(owner?.phone ?? order.phone, `زهورا: سفارش ${order.code} به مبلغ ${faNumber(order.total)} تومان ثبت شد. پیگیری: ${env().NEXT_PUBLIC_SITE_URL}/track`)
    .catch((e) => console.error("[sms] ارسال پیامک سفارش ناموفق بود", e));

  return { ok: true, orderCode: order.code };
}

/** سفارش‌هایی که بیش از ۳۰ دقیقه در انتظار پرداخت مانده‌اند لغو و موجودی‌شان آزاد می‌شود */
export async function releaseExpiredOrders() {
  const stale = await db.order.findMany({
    where: { status: "PENDING_PAYMENT", createdAt: { lt: new Date(Date.now() - 30 * 60_000) } },
    select: { id: true },
    take: 50,
  });
  for (const o of stale) await cancelOrder(o.id, "انقضای مهلت پرداخت");
  if (stale.length) await db.payment.updateMany({ where: { orderId: { in: stale.map((o) => o.id) }, status: "PENDING" }, data: { status: "FAILED" } });
}
