import { Prisma } from "@prisma/client";
import { MOVEMENT_TYPE } from "./enums";

// Helper transaksional untuk manipulasi stok. Semua dipanggil di dalam
// prisma.$transaction agar konsisten. Stok tidak boleh diedit langsung —
// selalu lewat movement + update saldo.

type Tx = Prisma.TransactionClient;

async function getOrCreateStock(tx: Tx, branchId: string, productId: string) {
  const existing = await tx.stock.findUnique({
    where: { branchId_productId: { branchId, productId } },
  });
  if (existing) return existing;
  return tx.stock.create({ data: { branchId, productId } });
}

// Reservasi stok saat order dikonfirmasi / kurir ditugaskan.
export async function reserveStock(
  tx: Tx,
  branchId: string,
  productId: string,
  qty: number
) {
  const s = await getOrCreateStock(tx, branchId, productId);
  if (s.quantityAvailable < qty)
    throw new Error(`Stok tidak cukup untuk produk ${productId}`);
  await tx.stock.update({
    where: { id: s.id },
    data: {
      quantityAvailable: { decrement: qty },
      quantityReserved: { increment: qty },
    },
  });
}

// Lepas reservasi (order gagal / dibatalkan) → kembali ke available.
export async function releaseReservation(
  tx: Tx,
  branchId: string,
  productId: string,
  qty: number
) {
  const s = await getOrCreateStock(tx, branchId, productId);
  await tx.stock.update({
    where: { id: s.id },
    data: {
      quantityReserved: { decrement: Math.min(qty, s.quantityReserved) },
      quantityAvailable: { increment: qty },
    },
  });
}

// Barang benar-benar keluar (terkirim) → kurangi reserved, catat movement 'out'.
export async function commitDelivery(
  tx: Tx,
  branchId: string,
  productId: string,
  qty: number,
  userId: string,
  orderId: string
) {
  const s = await getOrCreateStock(tx, branchId, productId);
  await tx.stock.update({
    where: { id: s.id },
    data: { quantityReserved: { decrement: Math.min(qty, s.quantityReserved) } },
  });
  await tx.stockMovement.create({
    data: {
      branchId,
      productId,
      type: MOVEMENT_TYPE.OUT,
      quantity: qty,
      referenceOrderId: orderId,
      createdBy: userId,
    },
  });
}

// Retur baik → kembali ke available. Retur rusak → masuk damaged (write-off).
export async function applyReturn(
  tx: Tx,
  opts: {
    branchId: string;
    productId: string;
    qty: number;
    damaged: boolean;
    reason: string;
    userId: string;
    orderId?: string;
  }
) {
  const s = await getOrCreateStock(tx, opts.branchId, opts.productId);
  await tx.stock.update({
    where: { id: s.id },
    data: opts.damaged
      ? { quantityDamaged: { increment: opts.qty } }
      : { quantityAvailable: { increment: opts.qty } },
  });
  await tx.stockMovement.create({
    data: {
      branchId: opts.branchId,
      productId: opts.productId,
      type: opts.damaged ? MOVEMENT_TYPE.RETURN_DAMAGED : MOVEMENT_TYPE.RETURN_GOOD,
      quantity: opts.qty,
      reason: opts.reason,
      referenceOrderId: opts.orderId,
      createdBy: opts.userId,
    },
  });
}

// Barang masuk (pengadaan dari pusat).
export async function stockIn(
  tx: Tx,
  branchId: string,
  productId: string,
  qty: number,
  userId: string,
  reason?: string
) {
  const s = await getOrCreateStock(tx, branchId, productId);
  await tx.stock.update({
    where: { id: s.id },
    data: { quantityAvailable: { increment: qty } },
  });
  await tx.stockMovement.create({
    data: {
      branchId,
      productId,
      type: MOVEMENT_TYPE.IN,
      quantity: qty,
      reason: reason ?? null,
      createdBy: userId,
    },
  });
}

// Penyesuaian manual (adjustment) dengan auto-flag jika melebihi threshold %.
export async function adjustStock(
  tx: Tx,
  branchId: string,
  productId: string,
  newAvailable: number,
  reason: string,
  userId: string
) {
  const s = await getOrCreateStock(tx, branchId, productId);
  const delta = newAvailable - s.quantityAvailable;
  const threshold = Number(process.env.STOCK_ADJUSTMENT_FLAG_THRESHOLD_PCT || 20);
  const base = s.quantityAvailable || 1;
  const flagged = Math.abs(delta) / base * 100 > threshold;

  await tx.stock.update({
    where: { id: s.id },
    data: { quantityAvailable: newAvailable },
  });
  await tx.stockMovement.create({
    data: {
      branchId,
      productId,
      type: MOVEMENT_TYPE.ADJUSTMENT,
      quantity: delta,
      reason,
      isFlaggedForReview: flagged,
      createdBy: userId,
    },
  });
  return { delta, flagged };
}
