<?php

namespace App\Services;

use App\Models\Product;
use App\Models\StockMovement;
use App\Models\WarehouseStock;
use Illuminate\Support\Facades\DB;

class StockService
{
    /**
     * Kurangi stok saat ada penjualan.
     */
    public function decreaseForSale(Product $product, int $qty, int $userId, string $referenceNo, int $warehouseId): StockMovement
    {
        return DB::transaction(function () use ($product, $qty, $userId, $referenceNo, $warehouseId) {
            $whStock = $this->getOrCreateWarehouseStock($warehouseId, $product->id);
            $stockBefore = $whStock->current_stock;
            $stockAfter  = max(0, $stockBefore - $qty);

            $whStock->update(['current_stock' => $stockAfter]);
            $this->syncProductAggregate($product);

            return StockMovement::create([
                'product_id'   => $product->id,
                'sku'          => $product->sku,
                'product_name' => $product->name,
                'type'         => 'out',
                'reason'       => 'sale',
                'qty'          => $qty,
                'stock_before' => $stockBefore,
                'stock_after'  => $stockAfter,
                'reference_no' => $referenceNo,
                'user_id'      => $userId,
                'warehouse_id' => $warehouseId,
                'notes'        => "Penjualan #{$referenceNo}",
            ]);
        });
    }

    /**
     * Kurangi stok untuk barang gratis promo.
     */
    public function decreaseForPromotion(Product $product, int $qty, int $userId, string $referenceNo, int $warehouseId): StockMovement
    {
        return DB::transaction(function () use ($product, $qty, $userId, $referenceNo, $warehouseId) {
            $whStock = $this->getOrCreateWarehouseStock($warehouseId, $product->id);
            $stockBefore = $whStock->current_stock;
            $stockAfter  = max(0, $stockBefore - $qty);

            $whStock->update(['current_stock' => $stockAfter]);
            $this->syncProductAggregate($product);

            return StockMovement::create([
                'product_id'   => $product->id,
                'sku'          => $product->sku,
                'product_name' => $product->name,
                'type'         => 'out',
                'reason'       => 'promotion',
                'qty'          => $qty,
                'stock_before' => $stockBefore,
                'stock_after'  => $stockAfter,
                'reference_no' => $referenceNo,
                'user_id'      => $userId,
                'warehouse_id' => $warehouseId,
                'notes'        => "Promo gratis #{$referenceNo}",
            ]);
        });
    }

    /**
     * Tambah stok saat PO diterima.
     */
    public function increaseForPurchase(Product $product, int $qty, int $userId, string $referenceNo, int $warehouseId): StockMovement
    {
        return DB::transaction(function () use ($product, $qty, $userId, $referenceNo, $warehouseId) {
            $whStock = $this->getOrCreateWarehouseStock($warehouseId, $product->id);
            $stockBefore = $whStock->current_stock;
            $stockAfter  = $stockBefore + $qty;

            $whStock->increment('current_stock', $qty);
            $this->syncProductAggregate($product);

            return StockMovement::create([
                'product_id'   => $product->id,
                'sku'          => $product->sku,
                'product_name' => $product->name,
                'type'         => 'in',
                'reason'       => 'purchase',
                'qty'          => $qty,
                'stock_before' => $stockBefore,
                'stock_after'  => $stockAfter,
                'reference_no' => $referenceNo,
                'user_id'      => $userId,
                'warehouse_id' => $warehouseId,
                'notes'        => "Penerimaan PO #{$referenceNo}",
            ]);
        });
    }

    /**
     * Tambah stok untuk retur dari pelanggan.
     */
    public function returnFromSale(Product $product, int $qty, int $userId, string $referenceNo, int $warehouseId): StockMovement
    {
        return DB::transaction(function () use ($product, $qty, $userId, $referenceNo, $warehouseId) {
            $whStock = $this->getOrCreateWarehouseStock($warehouseId, $product->id);
            $stockBefore = $whStock->current_stock;
            $stockAfter  = $stockBefore + $qty;

            $whStock->increment('current_stock', $qty);
            $this->syncProductAggregate($product);

            return StockMovement::create([
                'product_id'   => $product->id,
                'sku'          => $product->sku,
                'product_name' => $product->name,
                'type'         => 'in',
                'reason'       => 'return_in',
                'qty'          => $qty,
                'stock_before' => $stockBefore,
                'stock_after'  => $stockAfter,
                'reference_no' => $referenceNo,
                'user_id'      => $userId,
                'warehouse_id' => $warehouseId,
                'notes'        => "Retur masuk #{$referenceNo}",
            ]);
        });
    }

    /**
     * Adjustment manual stok (opname / koreksi).
     */
    public function adjust(Product $product, int $newStock, string $reason, int $userId, ?string $notes = null, ?string $overrideType = null, ?string $referenceNo = null, ?int $warehouseId = null): StockMovement
    {
        return DB::transaction(function () use ($product, $newStock, $reason, $userId, $notes, $overrideType, $referenceNo, $warehouseId) {
            if ($warehouseId) {
                $whStock = $this->getOrCreateWarehouseStock($warehouseId, $product->id);
                $stockBefore = $whStock->current_stock;
                $whStock->update(['current_stock' => $newStock]);
                $this->syncProductAggregate($product);
            } else {
                // Fallback: direct product stock (legacy)
                $stockBefore = $product->current_stock;
                $product->update(['current_stock' => $newStock]);
            }

            $diff = $newStock - $stockBefore;
            $type = $overrideType ?? 'adjustment';

            return StockMovement::create([
                'product_id'   => $product->id,
                'sku'          => $product->sku,
                'product_name' => $product->name,
                'type'         => $type,
                'reason'       => $reason,
                'qty'          => abs($diff),
                'stock_before' => $stockBefore,
                'stock_after'  => $newStock,
                'reference_no' => $referenceNo,
                'user_id'      => $userId,
                'warehouse_id' => $warehouseId,
                'notes'        => $notes,
            ]);
        });
    }

    /**
     * Get or create a warehouse_stock record.
     */
    private function getOrCreateWarehouseStock(int $warehouseId, int $productId): WarehouseStock
    {
        return WarehouseStock::firstOrCreate(
            ['warehouse_id' => $warehouseId, 'product_id' => $productId],
            ['current_stock' => 0, 'min_stock' => 5]
        );
    }

    /**
     * Sync products.current_stock as aggregate of all warehouse_stock.
     */
    private function syncProductAggregate(Product $product): void
    {
        $totalStock = WarehouseStock::where('product_id', $product->id)->sum('current_stock');
        $product->updateQuietly(['current_stock' => $totalStock]);
    }
}
