<?php

namespace App\Services;

use App\Models\Product;
use App\Models\ProductStock;
use App\Models\Purchase;
use App\Models\PurchaseItem;
use App\Models\StockMutation;
use App\Models\StockOpname;
use App\Models\StockOpnameDetail;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class InventoryService
{
    public function __construct(private readonly StockService $stockService)
    {
    }

    public function purchase(array $payload, User $user): Purchase
    {
        return DB::transaction(function () use ($payload, $user): Purchase {
            $items = collect($payload['items'] ?? [])->filter(fn ($item) => (float) ($item['quantity'] ?? 0) > 0);

            if ($items->isEmpty()) {
                throw ValidationException::withMessages(['items' => 'Minimal satu item pembelian wajib diisi.']);
            }

            $purchase = Purchase::query()->create([
                'branch_id' => $payload['branch_id'],
                'supplier_id' => $payload['supplier_id'] ?? null,
                'user_id' => $user->id,
                'purchase_date' => $payload['purchase_date'],
                'invoice_no' => $payload['invoice_no'] ?? null,
                'note' => $payload['note'] ?? null,
                'total' => 0,
            ]);

            $total = 0;

            foreach ($items as $item) {
                $product = Product::query()->lockForUpdate()->findOrFail($item['product_id']);

                if ($product->type !== 'retail' || ! $product->track_stock) {
                    throw ValidationException::withMessages(['items' => "{$product->name} bukan produk retail stok."]);
                }

                $quantity = (float) $item['quantity'];
                $unitCost = (float) ($item['unit_cost'] ?? $product->cost_price);
                $lineTotal = $quantity * $unitCost;

                PurchaseItem::query()->create([
                    'purchase_id' => $purchase->id,
                    'product_id' => $product->id,
                    'quantity' => $quantity,
                    'unit_cost' => $unitCost,
                    'line_total' => $lineTotal,
                ]);

                $this->stockService->increase((int) $payload['branch_id'], $product, $quantity, 'purchase', $purchase->id, $user);
                $total += $lineTotal;
            }

            $purchase->update(['total' => $total]);

            return $purchase->load('items');
        });
    }

    public function opname(array $payload, User $user): StockOpname
    {
        return DB::transaction(function () use ($payload, $user): StockOpname {
            $items = collect($payload['items'] ?? [])->filter(fn ($item) => isset($item['actual_quantity']) && $item['actual_quantity'] !== '');

            if ($items->isEmpty()) {
                throw ValidationException::withMessages(['items' => 'Minimal satu item opname wajib diisi.']);
            }

            $opname = StockOpname::query()->create([
                'branch_id' => $payload['branch_id'],
                'user_id' => $user->id,
                'opname_date' => $payload['opname_date'],
                'status' => 'posted',
                'note' => $payload['note'] ?? null,
            ]);

            foreach ($items as $item) {
                $product = Product::query()->lockForUpdate()->findOrFail($item['product_id']);
                $stock = ProductStock::query()->firstOrCreate(
                    ['branch_id' => $payload['branch_id'], 'product_id' => $product->id],
                    ['quantity' => 0],
                );

                $systemQuantity = (float) $stock->quantity;
                $actualQuantity = (float) $item['actual_quantity'];
                $difference = $actualQuantity - $systemQuantity;

                StockOpnameDetail::query()->create([
                    'stock_opname_id' => $opname->id,
                    'product_id' => $product->id,
                    'system_quantity' => $systemQuantity,
                    'actual_quantity' => $actualQuantity,
                    'difference' => $difference,
                ]);

                if ($difference !== 0.0) {
                    $stock->update(['quantity' => $actualQuantity]);
                    StockMutation::query()->create([
                        'branch_id' => $payload['branch_id'],
                        'product_id' => $product->id,
                        'user_id' => $user->id,
                        'source_type' => 'stock_opname',
                        'source_id' => $opname->id,
                        'quantity_change' => $difference,
                        'quantity_after' => $actualQuantity,
                        'note' => $payload['note'] ?? null,
                    ]);
                }
            }

            return $opname->load('details');
        });
    }

    public function adjust(array $payload, User $user): ProductStock
    {
        return DB::transaction(function () use ($payload, $user): ProductStock {
            $product = Product::query()->lockForUpdate()->findOrFail($payload['product_id']);

            if ($product->type !== 'retail' || ! $product->track_stock) {
                throw ValidationException::withMessages(['product_id' => 'Hanya produk retail stok yang bisa disesuaikan.']);
            }

            $stock = ProductStock::query()->firstOrCreate(
                ['branch_id' => $payload['branch_id'], 'product_id' => $product->id],
                ['quantity' => 0],
            );

            $before = (float) $stock->quantity;
            $after = (float) $payload['quantity'];
            $difference = $after - $before;

            $stock->update(['quantity' => $after]);

            StockMutation::query()->create([
                'branch_id' => $payload['branch_id'],
                'product_id' => $product->id,
                'user_id' => $user->id,
                'source_type' => 'stock_adjustment',
                'source_id' => $stock->id,
                'quantity_change' => $difference,
                'quantity_after' => $after,
                'note' => $payload['note'] ?? null,
            ]);

            return $stock->refresh();
        });
    }
}
