"use server";

import { prisma } from "@/lib/prisma";
import { generateOrderNumber } from "@/lib/format";
import { getRazorpayClient, isRazorpayConfigured, verifyRazorpaySignature } from "@/lib/razorpay";
import { getCurrentCustomerId } from "@/lib/customer-auth";
import { isValidEmail } from "@/lib/customer-otp";
import { isValidPhone } from "@/lib/phone";
import { effectiveUnitPrice, buyXGetYDeduction, bestOrderDiscount } from "@/lib/pricing";
import { getShippingSettings, calculateShippingFee } from "@/lib/shipping";
import { sendNewOrderNotification } from "@/lib/resend";
import { DEFAULT_CHECKOUT_PAUSED_MESSAGE } from "@/lib/site";

export type CheckoutItem = {
  productId: string;
  variantId?: string;
  quantity: number;
  personalization?: string;
};

export type CustomerInfo = {
  name: string;
  email: string;
  phone: string;
  addressLine1: string;
  addressLine2?: string;
  city: string;
  state: string;
  pincode: string;
};

export async function createCheckoutOrder(items: CheckoutItem[], customer: CustomerInfo, couponCodeInput?: string) {
  const storeSettings = await prisma.storeSettings.findFirst();
  if (!(storeSettings?.salesEnabled ?? true)) {
    throw new Error(storeSettings?.checkoutPausedMessage?.trim() || DEFAULT_CHECKOUT_PAUSED_MESSAGE);
  }
  if (!items || items.length === 0) throw new Error("Cart is empty");
  if (!customer.name || !customer.email || !customer.phone || !customer.addressLine1 || !customer.city || !customer.state || !customer.pincode) {
    throw new Error("Missing required customer details");
  }
  const phone = customer.phone.trim();
  if (!isValidPhone(phone)) {
    throw new Error("Enter a valid 10-digit mobile number");
  }
  const email = customer.email.trim();
  if (!isValidEmail(email)) {
    throw new Error("Enter a valid email address");
  }
  const pincode = customer.pincode.trim();
  if (!/^\d{6}$/.test(pincode)) {
    throw new Error("Enter a valid 6-digit pincode");
  }
  const name = customer.name.trim();
  const addressLine1 = customer.addressLine1.trim();
  const addressLine2 = customer.addressLine2?.trim() || undefined;
  const city = customer.city.trim();
  const state = customer.state.trim();
  if (name.length > 100 || addressLine1.length > 200 || (addressLine2?.length ?? 0) > 200 || city.length > 100 || state.length > 100) {
    throw new Error("One of the fields is too long — please shorten it");
  }

  const nonServiceable = await prisma.nonServiceablePincode.findUnique({ where: { pincode } });
  if (nonServiceable) {
    throw new Error(`Sorry, we don't currently deliver to pincode ${pincode}`);
  }

  const productIds = [...new Set(items.map((i) => i.productId))];
  const [products, bulkTiers, buyXGetYOffers, festivalOffers, shippingSettings] = await Promise.all([
    prisma.product.findMany({ where: { id: { in: productIds } }, include: { variants: true, category: true } }),
    prisma.bulkDiscountTier.findMany({ where: { active: true } }),
    prisma.buyXGetYOffer.findMany({ where: { active: true } }),
    prisma.festivalOffer.findMany({ where: { active: true, startsAt: { lte: new Date() }, endsAt: { gte: new Date() } } }),
    getShippingSettings(),
  ]);
  const productMap = new Map(products.map((p) => [p.id, p]));

  let subtotal = 0;
  let totalWeightGrams = 0;
  const orderItemsData = items.map((item) => {
    const product = productMap.get(item.productId);
    if (!product) throw new Error("A product in your cart is no longer available");
    if (item.quantity < product.minOrderQty) {
      throw new Error(`"${product.name}" has a minimum order quantity of ${product.minOrderQty}`);
    }
    const variant = item.variantId ? product.variants.find((v) => v.id === item.variantId) : undefined;
    const availableStock = variant ? variant.stock : product.stock;
    if (availableStock !== null && item.quantity > availableStock) {
      throw new Error(`"${product.name}" only has ${availableStock} left in stock`);
    }
    if (item.personalization && item.personalization.length > 60) {
      throw new Error("Personalization text is too long");
    }

    const matchingTiers = bulkTiers
      .filter((t) => t.productId === product.id || t.categoryId === product.categoryId)
      .map((t) => ({ minQty: t.minQty, discountPercent: t.discountPercent }));
    const { price: discountedBase } = effectiveUnitPrice(product, item.quantity, matchingTiers);
    const unitPrice = discountedBase + (variant?.priceDelta ?? 0);

    const matchingOffer =
      buyXGetYOffers.find((o) => o.productId === product.id) ??
      buyXGetYOffers.find((o) => o.categoryId === product.categoryId) ??
      null;
    const bxgyDeduction = buyXGetYDeduction(item.quantity, unitPrice, matchingOffer);

    const lineTotal = unitPrice * item.quantity - bxgyDeduction;
    subtotal += lineTotal;
    totalWeightGrams += (product.weightGrams ?? shippingSettings.defaultItemWeightGrams) * item.quantity;
    return {
      productId: product.id,
      variantId: variant?.id,
      quantity: item.quantity,
      unitPrice,
      lineTotal,
      personalization: item.personalization,
    };
  });

  let couponRecord: { type: string; value: number; id: string } | null = null;
  const couponCode = couponCodeInput?.trim().toUpperCase() || undefined;
  if (couponCode && couponCode.length > 30) throw new Error("That coupon code isn't valid");
  if (couponCode) {
    const coupon = await prisma.coupon.findUnique({ where: { code: couponCode } });
    if (!coupon || !coupon.active) throw new Error("That coupon code isn't valid");
    if (coupon.expiresAt && coupon.expiresAt < new Date()) throw new Error("That coupon code has expired");
    if (coupon.maxUses !== null && coupon.usedCount >= coupon.maxUses) throw new Error("That coupon code has already been fully used");
    if (coupon.minOrderValue !== null && subtotal < coupon.minOrderValue) {
      throw new Error(`That coupon needs a minimum order of ₹${coupon.minOrderValue / 100}`);
    }
    if (coupon.oneUsePerCustomer) {
      const priorOrders = await prisma.order.findMany({
        where: { couponCode, status: { not: "failed" } },
        select: { email: true },
      });
      const alreadyUsed = priorOrders.some((o) => o.email.trim().toLowerCase() === email.toLowerCase());
      if (alreadyUsed) throw new Error("You've already used that coupon code");
    }
    couponRecord = { type: coupon.type, value: coupon.value, id: coupon.id };
  }

  const bestFestivalPercent = festivalOffers.length ? Math.max(...festivalOffers.map((f) => f.discountPercent)) : null;
  const { amount: discountAmount, label: discountLabel } = bestOrderDiscount(subtotal, couponRecord, bestFestivalPercent);
  const appliedCouponCode = discountLabel === "Coupon" ? couponCode! : null;

  const discountedSubtotal = subtotal - discountAmount;
  const shippingFee = calculateShippingFee(discountedSubtotal, totalWeightGrams, shippingSettings);
  const total = discountedSubtotal + shippingFee;
  const orderNumber = generateOrderNumber();
  // Logged-in customer takes priority; otherwise link to an existing account with a matching email
  // (proof of ownership already happened at OTP login — a guest checkout with that email is reasonably theirs).
  let customerId = await getCurrentCustomerId();
  if (!customerId) {
    const existingCustomer = await prisma.customer.findUnique({ where: { email: email.toLowerCase() } });
    customerId = existingCustomer?.id ?? null;
  }

  const order = await prisma.order.create({
    data: {
      orderNumber,
      customerId: customerId ?? undefined,
      customerName: name,
      email,
      phone,
      addressLine1,
      addressLine2,
      city,
      state,
      pincode,
      subtotal,
      discountAmount,
      discountLabel,
      couponCode: appliedCouponCode,
      shippingFee,
      total,
      status: "pending",
      items: { create: orderItemsData },
    },
  });

  if (!isRazorpayConfigured()) {
    return {
      orderId: order.id,
      orderNumber: order.orderNumber,
      total,
      razorpayConfigured: false as const,
    };
  }

  const razorpay = getRazorpayClient();
  const rpOrder = await razorpay.orders.create({
    amount: total,
    currency: "INR",
    receipt: orderNumber,
  });

  await prisma.order.update({ where: { id: order.id }, data: { razorpayOrderId: rpOrder.id } });

  return {
    orderId: order.id,
    orderNumber: order.orderNumber,
    total,
    razorpayConfigured: true as const,
    razorpayOrderId: rpOrder.id,
    keyId: process.env.RAZORPAY_KEY_ID as string,
  };
}

export async function verifyCheckoutPayment(
  orderId: string,
  razorpayOrderId: string,
  razorpayPaymentId: string,
  razorpaySignature: string
) {
  const valid = verifyRazorpaySignature(razorpayOrderId, razorpayPaymentId, razorpaySignature);
  if (!valid) {
    await prisma.order.update({ where: { id: orderId }, data: { status: "failed" } });
    throw new Error("Payment verification failed");
  }

  let paymentMethod: string | null = null;
  try {
    const payment = await getRazorpayClient().payments.fetch(razorpayPaymentId);
    paymentMethod = payment.method ?? null;
  } catch {
    // non-fatal — payment is already verified via signature, method is just a display nicety
  }

  let isFirstTimePaid = false;
  const order = await prisma.$transaction(async (tx) => {
    const existing = await tx.order.findUniqueOrThrow({
      where: { id: orderId },
      include: { items: { include: { product: true } } },
    });
    isFirstTimePaid = existing.status !== "paid";
    if (isFirstTimePaid) {
      for (const item of existing.items) {
        if (item.variantId) {
          await tx.productVariant.update({
            where: { id: item.variantId },
            data: { stock: { decrement: item.quantity } },
          });
        } else if (item.product.stock !== null) {
          await tx.product.update({
            where: { id: item.productId },
            data: { stock: { decrement: item.quantity } },
          });
        }
      }
      if (existing.couponCode) {
        await tx.coupon.updateMany({ where: { code: existing.couponCode }, data: { usedCount: { increment: 1 } } });
      }
    }
    return tx.order.update({
      where: { id: orderId },
      data: { status: "paid", razorpayPaymentId, razorpaySignature, paymentMethod },
    });
  });

  if (isFirstTimePaid) {
    try {
      const notifSettings = await prisma.notificationSettings.findFirst();
      if (notifSettings?.notifyOnNewOrder && notifSettings.notifyEmail) {
        await sendNewOrderNotification(notifSettings.notifyEmail, {
          orderNumber: order.orderNumber,
          customerName: order.customerName,
          total: order.total,
        });
      }
    } catch {
      // non-fatal — the order is paid regardless of whether the admin got notified
    }
  }

  return { orderNumber: order.orderNumber };
}
