"use server";

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { canPerformAction } from "@/lib/po-state-machine";
import { processPaymentSchema } from "@/lib/validations/po";
import { notify } from "@/lib/notifier";
import { revalidatePath } from "next/cache";

type ActionResult = { ok: true } | { error: string };

function nameToCode(name: string): string {
  const slug = name.toUpperCase()
    .replace(/[^A-Z0-9]+/g, "-")
    .replace(/^-|-$/g, "")
    .substring(0, 20);
  return `${slug}-${Date.now().toString(36).toUpperCase().slice(-5)}`;
}

// Sync product catalog after payment is confirmed:
// - Auto-create products for unlinked line items (matched by name or brand new)
// - Upsert per-vendor prices for all items
async function syncProductCatalog(
  poId: string,
  lineItems: { id: string; name: string; unitPrice: { toNumber(): number } | number; productId: string | null }[],
  vendorId: string | null,
  actorId: string
) {
  const updatedProductIds: string[] = [];

  for (const li of lineItems) {
    const unitPrice = typeof li.unitPrice === "number" ? li.unitPrice : li.unitPrice.toNumber();
    let productId = li.productId;
    let priceChanged = false;

    if (!productId) {
      // Try to find an existing product by name (case-insensitive)
      const existing = await db.product.findFirst({
        where: { name: { equals: li.name, mode: "insensitive" }, isActive: true },
        select: { id: true, lastPrice: true },
      });

      if (existing) {
        productId = existing.id;
        priceChanged = Number(existing.lastPrice ?? 0) !== unitPrice;
      } else {
        // Create a new product — first-time registration, not a price update
        const code = nameToCode(li.name);
        try {
          const created = await db.product.create({
            data: { code, name: li.name, lastPrice: unitPrice, lastVendorId: vendorId },
          });
          productId = created.id;
        } catch {
          // Code collision (extremely unlikely) — add extra entropy
          const created = await db.product.create({
            data: {
              code: `${code}-${Math.random().toString(36).slice(2, 5).toUpperCase()}`,
              name: li.name,
              lastPrice: unitPrice,
              lastVendorId: vendorId,
            },
          });
          productId = created.id;
        }
      }

      // Link the line item to the product for future reference
      await db.pOLineItem.update({ where: { id: li.id }, data: { productId } });
    } else {
      const current = await db.product.findUnique({
        where: { id: productId },
        select: { lastPrice: true },
      });
      priceChanged = !current || Number(current.lastPrice ?? 0) !== unitPrice;
    }

    // Always update lastPrice / lastVendorId on the product
    await db.product.update({
      where: { id: productId },
      data: { lastPrice: unitPrice, lastVendorId: vendorId ?? undefined },
    });

    // Upsert per-vendor price if PO has a vendor
    if (vendorId) {
      await db.productVendorPrice.upsert({
        where: { productId_vendorId: { productId, vendorId } },
        update: { price: unitPrice },
        create: { productId, vendorId, price: unitPrice },
      });
    }

    if (priceChanged) updatedProductIds.push(productId);
  }

  if (updatedProductIds.length > 0) {
    await db.pOAction.create({
      data: {
        actionType: "PRODUCT_PRICE_UPDATED",
        actorId,
        poId,
        metadata: { updatedProductIds },
      },
    });
  }
}

// Step 1: Accounts picks up the PO — MGR_APPROVED → SENT_FOR_PAYMENT
export async function processPayment({ poId }: { poId: string }): Promise<ActionResult> {
  const session = await auth();
  if (!session?.user) return { error: "Unauthorized" };

  const po = await db.purchaseOrder.findUnique({
    where: { id: poId },
    include: { submitter: true },
  });
  if (!po) return { error: "PO not found" };
  if (!canPerformAction(po.status, "process_payment", session.user.role)) {
    return { error: "You cannot process payment for this PO." };
  }

  await db.purchaseOrder.update({
    where: { id: poId },
    data: { status: "SENT_FOR_PAYMENT" },
  });

  const [managers, accounts] = await Promise.all([
    db.user.findMany({ where: { role: "MANAGER", isActive: true } }),
    db.user.findMany({ where: { role: "ACCOUNTS", isActive: true } }),
  ]);
  await notify({ event: "PAYMENT_PROCESSING", po, recipients: [...managers, ...accounts] });

  revalidatePath("/payments");
  revalidatePath(`/po/${poId}`);
  return { ok: true };
}

// Step 2: Accounts confirms payment sent — SENT_FOR_PAYMENT / PARTIALLY_PAID → PAID_DELIVERED or PARTIALLY_PAID
export async function markPaid({
  poId,
  paymentRef,
  paymentAmount,
  paymentDate,
}: {
  poId: string;
  paymentRef: string;
  paymentAmount?: number; // if omitted, treat as full remaining amount
  paymentDate: string; // ISO date (yyyy-mm-dd) entered by Accounts
}): Promise<ActionResult> {
  const session = await auth();
  if (!session?.user) return { error: "Unauthorized" };

  const parsed = processPaymentSchema.safeParse({ paymentRef, paymentAmount, paymentDate });
  if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" };

  const enteredPaymentDate = parsed.data.paymentDate;

  const po = await db.purchaseOrder.findUnique({
    where: { id: poId },
    include: { submitter: true, lineItems: true },
  });
  if (!po) return { error: "PO not found" };

  const canFullPay = canPerformAction(po.status, "mark_paid", session.user.role);
  const canPartialPay = canPerformAction(po.status, "mark_partial_payment", session.user.role);
  if (!canFullPay && !canPartialPay) {
    return { error: "You cannot confirm payment for this PO." };
  }

  const alreadyPaid = Number(po.paidAmount ?? 0);
  const total = Number(po.totalAmount);
  const remaining = total - alreadyPaid;
  const paying = parsed.data.paymentAmount ?? remaining;
  const newPaidAmount = alreadyPaid + paying;
  const isFullyPaid = newPaidAmount >= total;

  if (isFullyPaid) {
    await db.purchaseOrder.update({
      where: { id: poId },
      data: {
        status: "PAID_DELIVERED",
        paidAt: enteredPaymentDate,
        paymentDate: enteredPaymentDate,
        paymentRef: parsed.data.paymentRef,
        paidAmount: newPaidAmount,
        actions: {
          create: {
            actionType: "PAYMENT_SENT",
            actorId: session.user.id,
            metadata: { paymentRef: parsed.data.paymentRef, paymentDate: enteredPaymentDate.toISOString() },
          },
        },
      },
    });

    // Sync product catalog: auto-create new items, upsert per-vendor prices
    await syncProductCatalog(poId, po.lineItems, po.vendorId, session.user.id);

    // Auto-verify the vendor on first successful payment
    if (po.vendorId) {
      await db.vendor.update({
        where: { id: po.vendorId },
        data: { isVerified: true },
      });
    }

    await notify({ event: "PAYMENT_SENT", po, recipients: [po.submitter] });
  } else {
    await db.purchaseOrder.update({
      where: { id: poId },
      data: {
        status: "PARTIALLY_PAID",
        paymentRef: parsed.data.paymentRef,
        paymentDate: enteredPaymentDate,
        paidAmount: newPaidAmount,
        actions: {
          create: {
            actionType: "PARTIAL_PAYMENT_CONFIRMED",
            actorId: session.user.id,
            metadata: {
              paymentRef: parsed.data.paymentRef,
              paymentAmount: paying,
              totalPaid: newPaidAmount,
              paymentDate: enteredPaymentDate.toISOString(),
            },
          },
        },
      },
    });
  }

  revalidatePath("/payments");
  revalidatePath(`/po/${poId}`);
  return { ok: true };
}
