"use server";

import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { prisma } from "@/lib/prisma";
import { logActivity } from "@/lib/activity";
import { moveStock } from "@/lib/stock";
import { actor, failure } from "./guard";
import { readLines } from "./orders";

function refreshStore() {
  revalidatePath("/store");
  revalidatePath("/store/stock");
  revalidatePath("/store/sales");
  revalidatePath("/stock");
  revalidatePath("/products");
}

// ---------------------------------------------------------------------------
// Moving goods between the main warehouse and the shop counter

/** Warehouse → shop. The warehouse ledger records it as an exit for retail selling. */
export async function transferToStore(formData: FormData) {
  const user = await actor("store", "edit");
  const lines = await readLines(formData);
  if (!lines.length) throw await failure("Add at least one product.");
  for (const { productId, quantity } of lines) {
    const product = await prisma.product.findUnique({ where: { id: productId } });
    if (!product || product.quantity < quantity) {
      throw await failure("Not enough warehouse stock for {product}.", { product: product?.name ?? "—" });
    }
    await prisma.$transaction(async (tx) => {
      await moveStock(
        { productId, delta: -quantity, type: "STORE_TRANSFER", reason: "Moved to the internal store", userId: user.id },
        tx,
      );
      await tx.storeStock.upsert({
        where: { productId },
        update: { quantity: { increment: quantity } },
        create: { productId, quantity },
      });
    });
  }
  await logActivity(user, "store", "edit", "Stock moved to the store", undefined, undefined, String(lines.length));
  refreshStore();
}

/** Shop → warehouse, for goods that did not sell. */
export async function returnToWarehouse(formData: FormData) {
  const user = await actor("store", "edit");
  const lines = await readLines(formData);
  if (!lines.length) throw await failure("Add at least one product.");
  for (const { productId, quantity } of lines) {
    const held = await prisma.storeStock.findUnique({ where: { productId }, include: { product: true } });
    if (!held || held.quantity < quantity) {
      throw await failure("Not enough store stock for {product}.", { product: held?.product.name ?? "—" });
    }
    await prisma.$transaction(async (tx) => {
      await tx.storeStock.update({ where: { productId }, data: { quantity: { decrement: quantity } } });
      await moveStock(
        { productId, delta: quantity, type: "STORE_RETURN", reason: "Returned from the internal store", userId: user.id },
        tx,
      );
    });
  }
  await logActivity(user, "store", "edit", "Stock returned from the store", undefined, undefined, String(lines.length));
  refreshStore();
}

// ---------------------------------------------------------------------------
// Counter sales (vente comptoir) → bon de vente

async function nextSaleNumber() {
  const count = await prisma.storeSale.count();
  return `BV-${String(1001 + count).padStart(5, "0")}`;
}

export async function createStoreSale(formData: FormData) {
  const user = await actor("store", "create");
  const items = await readLines(formData);
  if (!items.length) throw await failure("Add at least one product.");
  const lines: Array<{ productId: string; quantity: number; unitPrice: number; lineAmount: number }> = [];
  let subtotal = 0;
  for (const { productId, quantity } of items) {
    const product = await prisma.product.findUnique({ where: { id: productId }, include: { storeStock: true } });
    if (!product) throw await failure("Product not found");
    if ((product.storeStock?.quantity ?? 0) < quantity) {
      throw await failure("Not enough store stock for {product}.", { product: product.name });
    }
    // The counter sells at the retail price.
    const unitPrice = product.retailPrice;
    subtotal += unitPrice * quantity;
    lines.push({ productId, quantity, unitPrice, lineAmount: unitPrice * quantity });
  }
  const discount = Math.max(0, Number(formData.get("discount") ?? 0));
  const total = Math.max(0, subtotal - discount);
  const paidAmount = Number(formData.get("paidAmount") ?? 0) || total;
  const customerId = String(formData.get("customerId") ?? "") || null;

  const sale = await prisma.$transaction(async (tx) => {
    const created = await tx.storeSale.create({
      data: {
        number: await nextSaleNumber(),
        customerId,
        buyerName: String(formData.get("buyerName") ?? "").trim(),
        discount,
        total,
        paidAmount,
        method: String(formData.get("method") ?? "CASH"),
        notes: String(formData.get("notes") ?? "").trim(),
        userId: user.id,
        lines: { create: lines },
      },
    });
    for (const line of lines) {
      await tx.storeStock.update({ where: { productId: line.productId }, data: { quantity: { decrement: line.quantity } } });
    }
    return created;
  });
  await logActivity(user, "store", "create", `Counter sale ${sale.number}`, sale.id, undefined, String(total));
  refreshStore();
  redirect(`/store/sales?highlight=${sale.id}`);
}

/** Cancel a counter sale and put its goods back on the shop shelf. */
export async function cancelStoreSale(id: string, formData: FormData) {
  const user = await actor("store", "edit", formData);
  const sale = await prisma.storeSale.findUnique({ where: { id }, include: { lines: true } });
  if (!sale || sale.status === "CANCELLED") return;
  await prisma.$transaction(async (tx) => {
    for (const line of sale.lines) {
      await tx.storeStock.upsert({
        where: { productId: line.productId },
        update: { quantity: { increment: line.quantity } },
        create: { productId: line.productId, quantity: line.quantity },
      });
    }
    await tx.storeSale.update({ where: { id }, data: { status: "CANCELLED" } });
  });
  await logActivity(user, "store", "edit", `Counter sale ${sale.number}`, id, "COMPLETED", "CANCELLED");
  refreshStore();
}

// ---------------------------------------------------------------------------
// Shop expenses

export async function saveExpense(formData: FormData) {
  const id = String(formData.get("id") ?? "");
  const user = await actor("store", id ? "edit" : "create", formData);
  const amount = Number(formData.get("amount") ?? 0);
  if (!(amount > 0)) throw await failure("Enter an amount greater than zero.");
  const dateValue = String(formData.get("date") ?? "");
  const data = {
    date: dateValue ? new Date(dateValue) : new Date(),
    category: String(formData.get("category") ?? "OTHER"),
    label: String(formData.get("label") ?? "").trim(),
    amount,
    method: String(formData.get("method") ?? "CASH"),
    reference: String(formData.get("reference") ?? "").trim() || null,
    notes: String(formData.get("notes") ?? "").trim(),
  };
  if (!data.label) throw await failure("Describe what the expense is for.");
  if (id) {
    const previous = await prisma.expense.findUnique({ where: { id } });
    await prisma.expense.update({ where: { id }, data });
    await logActivity(user, "store", "edit", `Expense ${data.label}`, id, String(previous?.amount), String(amount));
  } else {
    const created = await prisma.expense.create({ data: { ...data, userId: user.id } });
    await logActivity(user, "store", "create", `Expense ${data.label}`, created.id, undefined, String(amount));
  }
  revalidatePath("/store/expenses");
}

export async function deleteExpense(id: string, formData: FormData) {
  const user = await actor("store", "delete", formData);
  const expense = await prisma.expense.delete({ where: { id } });
  await logActivity(user, "store", "delete", `Expense ${expense.label}`, id, String(expense.amount));
  revalidatePath("/store/expenses");
}
