"use server";

import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { z } from "zod";
import { db } from "@/lib/db";
import { cartId, MAX_QTY_PER_LINE, loadCart } from "@/lib/cart";
import { variantAvailability } from "@/lib/catalog";
import { ownerKey } from "@/lib/visitor";
import { CheckoutError, placeOrder, quoteCart, submitPaymentProof } from "@/lib/orders";
import { getCurrentUser, createSession, destroySession, hashPassword, verifyPassword, passwordProblem, clientIp } from "@/lib/auth";
import { loginBlocked, recordLogin } from "@/lib/rate-limit";
import { fmtDate } from "@/lib/dates";

export type ActionResult = { ok: boolean; message?: string; errors?: Record<string, string> };

// ───────── cart ─────────
export async function addToCart(variantId: string, qty: number): Promise<ActionResult & { count?: number }> {
  const q = Math.max(1, Math.min(MAX_QTY_PER_LINE, Math.floor(qty || 1)));
  const v = await db.productVariant.findUnique({ where: { id: variantId }, include: { batch: true, product: { select: { status: true } } } });
  if (!v || !v.active || v.product.status !== "PUBLISHED") return { ok: false, message: "This item is no longer available." };
  const av = variantAvailability({ ...v, images: [] } as never);
  if (!av.purchasable) return { ok: false, message: `${av.label}.` };
  const id = (await cartId(true))!;
  const existing = await db.cartItem.findUnique({ where: { cartId_variantId: { cartId: id, variantId } } });
  const next = (existing?.qty ?? 0) + q;
  const limit = v.mode === "READY_STOCK" ? v.stockQty : v.batch ? v.batch.capacity - v.batch.heldQty - v.batch.committedQty : 0;
  if (next > Math.min(limit, MAX_QTY_PER_LINE)) return { ok: false, message: `Only ${Math.min(limit, MAX_QTY_PER_LINE)} available${existing ? " (some are already in your bag)" : ""}.` };
  await db.cartItem.upsert({ where: { cartId_variantId: { cartId: id, variantId } }, create: { cartId: id, variantId, qty: q }, update: { qty: next } });
  revalidatePath("/", "layout");
  const cart = await loadCart();
  return { ok: true, message: "Added to your bag.", count: cart.count };
}

export async function setCartQty(formData: FormData) {
  const itemId = String(formData.get("itemId"));
  const qty = Math.floor(Number(formData.get("qty")));
  const id = await cartId(false);
  if (!id) return;
  if (!qty || qty < 1) await db.cartItem.deleteMany({ where: { id: itemId, cartId: id } });
  else await db.cartItem.updateMany({ where: { id: itemId, cartId: id }, data: { qty: Math.min(qty, MAX_QTY_PER_LINE) } });
  revalidatePath("/", "layout");
}

export async function removeCartItem(formData: FormData) {
  const id = await cartId(false);
  if (id) await db.cartItem.deleteMany({ where: { id: String(formData.get("itemId")), cartId: id } });
  revalidatePath("/", "layout");
}

// ───────── wishlist ─────────
export async function toggleWishlist(productId: string): Promise<{ saved: boolean }> {
  const key = (await ownerKey(true))!;
  const existing = await db.wishlistItem.findUnique({ where: { ownerKey_productId: { ownerKey: key, productId } } });
  if (existing) await db.wishlistItem.delete({ where: { id: existing.id } });
  else await db.wishlistItem.create({ data: { ownerKey: key, productId } });
  revalidatePath("/wishlist");
  return { saved: !existing };
}

// ───────── newsletter & enquiries ─────────
export async function subscribe(_: ActionResult | null, formData: FormData): Promise<ActionResult> {
  const email = String(formData.get("email") ?? "").trim().toLowerCase();
  if (!z.string().email().safeParse(email).success) return { ok: false, message: "Enter a valid email address." };
  await db.newsletterSubscriber.upsert({ where: { email }, create: { email, source: String(formData.get("source") ?? "site") }, update: {} });
  return { ok: true, message: "You're on the list. We'll email you when the next batch opens." };
}

const enquirySchema = z.object({
  name: z.string().trim().min(2, "Enter your name.").max(120),
  email: z.string().trim().email("Enter a valid email."),
  phone: z.string().trim().max(30).optional(),
  topic: z.string().trim().min(1).max(80),
  message: z.string().trim().min(10, "Tell us a little more (10+ characters).").max(4000),
});

export async function sendEnquiry(_: ActionResult | null, formData: FormData): Promise<ActionResult> {
  const parsed = enquirySchema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) return { ok: false, errors: Object.fromEntries(parsed.error.issues.map((i) => [String(i.path[0]), i.message])) };
  await db.enquiry.create({ data: { ...parsed.data, phone: parsed.data.phone || null } });
  return { ok: true, message: "Thanks. We usually reply within one working day." };
}

// ───────── checkout ─────────
const BD_PHONE = /^(?:\+?88)?01[3-9]\d{8}$/;
const checkoutSchema = z.object({
  customerName: z.string().trim().min(2, "Enter your full name.").max(120),
  phone: z.string().trim().transform((s) => s.replace(/[\s-]/g, "")).refine((s) => BD_PHONE.test(s), "Enter a Bangladeshi mobile number, e.g. 01712345678."),
  email: z.string().trim().email("Enter a valid email address."),
  address: z.string().trim().min(8, "Enter your house, road and area.").max(500),
  district: z.string().trim().min(2, "Choose your district."),
  area: z.string().trim().min(2, "Enter your area or thana.").max(120),
  instructions: z.string().trim().max(500).optional(),
  plan: z.enum(["FULL", "DEPOSIT", "COD"]),
  method: z.enum(["BKASH", "NAGAD", "BANK", "COD"]),
  splitShipment: z.coerce.boolean().default(false),
  discountCode: z.string().trim().max(40).optional(),
  acceptTerms: z.coerce.boolean().default(false),
});

export type CheckoutQuoteView = Awaited<ReturnType<typeof getCheckoutQuote>>;

export async function getCheckoutQuote(input: { district: string; plan: "FULL" | "DEPOSIT" | "COD"; splitShipment: boolean; discountCode?: string }) {
  const { quote: q, zone, discount } = await quoteCart(input);
  return {
    subtotal: q.subtotal, discount: q.discount, shipping: q.shipping, total: q.total, dueNow: q.dueNow, balance: q.balance,
    balanceDueAt: q.balanceDueAt ? fmtDate(q.balanceDueAt) : null, allowedPlans: q.allowedPlans, mixed: q.mixed, hasPreorder: q.hasPreorder,
    errors: q.errors, zoneName: zone?.name ?? null, zoneEta: zone?.etaText ?? null, discountApplied: discount?.code ?? null,
  };
}

export async function submitCheckout(_: ActionResult | null, formData: FormData): Promise<ActionResult> {
  const raw = Object.fromEntries(formData);
  const parsed = checkoutSchema.safeParse({ ...raw, splitShipment: raw.splitShipment === "on", acceptTerms: raw.acceptTerms === "on" });
  if (!parsed.success) return { ok: false, message: "Check the highlighted fields.", errors: Object.fromEntries(parsed.error.issues.map((i) => [String(i.path[0]), i.message])) };
  const user = await getCurrentUser();
  let number: string, token: string;
  try {
    const order = await placeOrder({ ...parsed.data, userId: user?.id ?? null });
    number = order.number; token = order.trackingToken;
  } catch (e) {
    if (e instanceof CheckoutError) return { ok: false, message: e.message };
    console.error(e);
    return { ok: false, message: "We couldn't place your order. Nothing was charged. Please try again." };
  }
  revalidatePath("/", "layout");
  redirect(`/order/${number}?t=${token}`);
}

const proofSchema = z.object({
  token: z.string().min(10),
  method: z.enum(["BKASH", "NAGAD", "BANK"]),
  amount: z.coerce.number().int("Enter a whole Taka amount.").positive("Enter the amount you sent."),
  reference: z.string().trim().min(4, "Enter the transaction ID.").max(60),
  senderNumber: z.string().trim().min(4, "Enter the number or account you paid from.").max(40),
});

export async function submitProof(_: ActionResult | null, formData: FormData): Promise<ActionResult> {
  const parsed = proofSchema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) return { ok: false, errors: Object.fromEntries(parsed.error.issues.map((i) => [String(i.path[0]), i.message])) };
  try {
    await submitPaymentProof(parsed.data.token, parsed.data);
  } catch (e) {
    if (e instanceof CheckoutError) return { ok: false, message: e.message };
    throw e;
  }
  revalidatePath("/order", "layout");
  return { ok: true, message: "Payment submitted. We'll verify it and update your order." };
}

export async function trackLookup(_: ActionResult | null, formData: FormData): Promise<ActionResult> {
  const number = String(formData.get("number") ?? "").trim().toUpperCase();
  const phone = String(formData.get("phone") ?? "").replace(/[\s-]/g, "");
  if (!number || !phone) return { ok: false, message: "Enter your order number and phone number." };
  const order = await db.order.findUnique({ where: { number }, select: { phone: true, trackingToken: true } });
  if (!order || order.phone.replace(/^\+?88/, "") !== phone.replace(/^\+?88/, "")) return { ok: false, message: "We couldn't find an order with those details." };
  redirect(`/order/${number}?t=${order.trackingToken}`);
}

// ───────── customer accounts ─────────
export async function customerLogin(_: ActionResult | null, formData: FormData): Promise<ActionResult> {
  const email = String(formData.get("email") ?? "").trim().toLowerCase();
  const password = String(formData.get("password") ?? "");
  const next = String(formData.get("next") || "/account");
  const ip = await clientIp();
  if (await loginBlocked(email, ip)) return { ok: false, message: "Too many attempts. Try again in 15 minutes." };
  const user = await db.user.findUnique({ where: { email } });
  const valid = !!user && user.active && (await verifyPassword(password, user.passwordHash));
  await recordLogin(email, ip, valid);
  if (!valid) return { ok: false, message: "Email or password is incorrect." };
  await createSession(user!);
  await db.user.update({ where: { id: user!.id }, data: { lastLoginAt: new Date() } });
  await attachGuestData(user!.id);
  redirect(next.startsWith("/") && !next.startsWith("//") ? next : "/account");
}

export async function customerRegister(_: ActionResult | null, formData: FormData): Promise<ActionResult> {
  const parsed = z.object({
    name: z.string().trim().min(2, "Enter your name."),
    email: z.string().trim().toLowerCase().email("Enter a valid email."),
    phone: z.string().trim().optional(),
    password: z.string(),
  }).safeParse(Object.fromEntries(formData));
  if (!parsed.success) return { ok: false, errors: Object.fromEntries(parsed.error.issues.map((i) => [String(i.path[0]), i.message])) };
  const problem = passwordProblem(parsed.data.password);
  if (problem) return { ok: false, errors: { password: problem } };
  if (await db.user.findUnique({ where: { email: parsed.data.email } })) return { ok: false, errors: { email: "An account with this email exists. Sign in instead." } };
  const user = await db.user.create({ data: { name: parsed.data.name, email: parsed.data.email, phone: parsed.data.phone || null, passwordHash: await hashPassword(parsed.data.password) } });
  await createSession(user);
  await attachGuestData(user.id);
  redirect("/account");
}

/** Links guest orders (same email) and anonymous wishlist to the account. */
async function attachGuestData(userId: string) {
  const u = await db.user.findUniqueOrThrow({ where: { id: userId } });
  await db.order.updateMany({ where: { userId: null, email: u.email }, data: { userId } });
  const { cookies } = await import("next/headers");
  const vid = (await cookies()).get("vl_vid")?.value;
  if (vid) {
    const items = await db.wishlistItem.findMany({ where: { ownerKey: vid } });
    for (const it of items) await db.wishlistItem.upsert({ where: { ownerKey_productId: { ownerKey: userId, productId: it.productId } }, create: { ownerKey: userId, productId: it.productId }, update: {} });
    await db.wishlistItem.deleteMany({ where: { ownerKey: vid } });
  }
}

export async function customerLogout() {
  await destroySession();
  redirect("/");
}
