<?php

namespace App\Services;

use App\Models\StockBalance;
use App\Models\StockLedger;
use App\Models\Grn;
use App\Models\WarehouseCurrentStock;
use App\Models\WarehouseStockLedger;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class WarehouseService
{
    /**
     * Record a stock move and recalculate the related stock balance.
     * Direction: 'in' or 'out'
     */
    public function recordMove(
        int $itemId,
        int $warehouseId,
        int $binId,
        ?string $lotNo,
        float $quantity,
        string $direction,
        float $unitCost,
        string $txnType,
        int $txnId,
        string $refNo,
        ?\Carbon\CarbonInterface $occurredAt = null
    ): StockLedger {
        return DB::transaction(function () use ($itemId, $warehouseId, $binId, $lotNo, $quantity, $direction, $unitCost, $txnType, $txnId, $refNo, $occurredAt) {
            $qtyIn = $direction === 'in' ? $quantity : 0.0;
            $qtyOut = $direction === 'out' ? $quantity : 0.0;

            $ledger = StockLedger::create([
                'txn_type' => $txnType,
                'txn_id' => $txnId,
                'item_id' => $itemId,
                'lot_no' => $lotNo,
                'bin_id' => $binId,
                'qty_in' => $qtyIn,
                'qty_out' => $qtyOut,
                'uom_id' => 1, // default; adjust if needed
                'unit_cost' => $unitCost,
                'ref_no' => $refNo,
                'occurred_at' => $occurredAt ? $occurredAt : now(),
            ]);

            $this->recalculateBalanceForKey($itemId, $warehouseId, $binId, $lotNo);

            return $ledger;
        });
    }

    /**
     * Create an adjustment move to correct stock.
     */
    public function adjustStock(
        int $itemId,
        int $warehouseId,
        int $binId,
        ?string $lotNo,
        float $diffQuantity,
        float $unitCost,
        string $note,
        int $adjustmentRefId
    ): StockLedger {
        $direction = $diffQuantity >= 0 ? 'in' : 'out';
        $qty = abs($diffQuantity);
        return $this->recordMove(
            $itemId,
            $warehouseId,
            $binId,
            $lotNo,
            $qty,
            $direction,
            $unitCost,
            'adjustment',
            $adjustmentRefId,
            $note
        );
    }

    /**
     * Recalculate a single StockBalance row from ledger.
     */
    public function recalculateBalanceForKey(int $itemId, int $warehouseId, int $binId, ?string $lotNo): void
    {
        $totals = StockLedger::query()
            ->where('item_id', $itemId)
            ->where('bin_id', $binId)
            ->when($lotNo !== null, fn($q) => $q->where('lot_no', $lotNo))
            ->selectRaw('COALESCE(SUM(qty_in),0) as tin, COALESCE(SUM(qty_out),0) as tout, COALESCE(SUM(qty_in*unit_cost),0) as vin')
            ->first();

        $onHand = (float) ($totals->tin - $totals->tout);
        $avgCost = (float) ($totals->tin > 0 ? $totals->vin / $totals->tin : 0);

        // Upsert balance (prevent direct updates elsewhere)
        StockBalance::$allowInternalUpdate = true;
        $balance = StockBalance::firstOrNew([
            'item_id' => $itemId,
            'warehouse_id' => $warehouseId,
            'bin_id' => $binId,
            'lot_no' => $lotNo,
        ]);
        $balance->on_hand_qty = $onHand;
        $balance->reserved_qty = $balance->reserved_qty ?? 0;
        $balance->unit_cost_avg = $avgCost;
        $balance->save();
        StockBalance::$allowInternalUpdate = false;
    }

    /**
     * Get live stock by item (sum across all warehouses/bins/lots)
     */
    public function getLiveStockByItem(int $itemId): float
    {
        return (float) StockBalance::where('item_id', $itemId)->sum('on_hand_qty');
    }

    /**
     * Get live stock by item and warehouse
     */
    public function getLiveStockByItemWarehouse(int $itemId, int $warehouseId): float
    {
        return (float) StockBalance::where('item_id', $itemId)->where('warehouse_id', $warehouseId)->sum('on_hand_qty');
    }

    /**
     * Get live stock by full key
     */
    public function getLiveStockByKey(int $itemId, int $warehouseId, int $binId, ?string $lotNo): float
    {
        return (float) StockBalance::where([
            'item_id' => $itemId,
            'warehouse_id' => $warehouseId,
            'bin_id' => $binId,
            'lot_no' => $lotNo,
        ])->value('on_hand_qty') ?? 0.0;
    }
    /**
     * Add stock to quarantine when GRN is created
     */
    public function addStockToQuarantine(Grn $grn): void
    {
        DB::transaction(function () use ($grn) {
            $warehouseId = $grn->warehouse_id ?? 1; // Default warehouse

            foreach ($grn->items as $grnItem) {
                $itemId = $grnItem->item_id;
                $quantity = $grnItem->received_quantity;

                if ($quantity > 0) {
                    // Increment quarantine stock
                    $quarantineStock = WarehouseCurrentStock::getOrCreateStock($itemId, $warehouseId, 'quarantine');
                    $quarantineStock->incrementStock($quantity);

                    // Create ledger entry
                    WarehouseStockLedger::createEntry(
                        $itemId,
                        $warehouseId,
                        'quarantine',
                        'inward',
                        $quantity,
                        'GRN',
                        $grn->id
                    );
                }
            }

            Log::info('Stock added to quarantine for GRN', [
                'grn_id' => $grn->id,
                'grn_number' => $grn->grn_number,
                'warehouse_id' => $warehouseId,
                'items_count' => $grn->items->count(),
            ]);
        });
    }
    /**
     * Add stock to quarantine when GRN is created
     */
    // Legacy/current stock helpers retained for compatibility

    /**
     * Get current stock for an item in a warehouse
     */
    public function getCurrentStock(int $itemId, int $warehouseId): array
    {
        $stocks = WarehouseCurrentStock::where('item_id', $itemId)
            ->where('warehouse_id', $warehouseId)
            ->get()
            ->keyBy('bucket');

        return [
            'available' => $stocks->get('available')->current_qty ?? 0,
            'rejected' => $stocks->get('rejected')->current_qty ?? 0,
            'quarantine' => $stocks->get('quarantine')->current_qty ?? 0,
        ];
    }

    /**
     * Get stock ledger for an item in a warehouse
     */
    public function getStockLedger(int $itemId, int $warehouseId, int $limit = 50): \Illuminate\Database\Eloquent\Collection
    {
        return WarehouseStockLedger::with(['item', 'warehouse'])
            ->where('item_id', $itemId)
            ->where('warehouse_id', $warehouseId)
            ->orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();
    }

    /**
     * Create reversal entry for cancelled operations
     */
    public function createReversalEntry(
        int $itemId,
        int $warehouseId,
        string $bucket,
        float $qty,
        string $originalSourceType,
        int $originalSourceId,
        int $cancelledBy
    ): void {
        DB::transaction(function () use ($itemId, $warehouseId, $bucket, $qty, $originalSourceType, $originalSourceId, $cancelledBy) {
            // Decrement current stock
            $currentStock = WarehouseCurrentStock::getOrCreateStock($itemId, $warehouseId, $bucket);
            $currentStock->decrementStock($qty);

            // Create reversal ledger entry
            WarehouseStockLedger::createEntry(
                $itemId,
                $warehouseId,
                $bucket,
                'outward',
                $qty,
                'reversal',
                $originalSourceId
            );

            Log::info('Reversal entry created', [
                'item_id' => $itemId,
                'warehouse_id' => $warehouseId,
                'bucket' => $bucket,
                'qty' => $qty,
                'original_source_type' => $originalSourceType,
                'original_source_id' => $originalSourceId,
                'cancelled_by' => $cancelledBy,
            ]);
        });
    }

    /**
     * Get warehouse stock summary
     */
    public function getWarehouseStockSummary(int $warehouseId): array
    {
        $stocks = WarehouseCurrentStock::with('item')
            ->where('warehouse_id', $warehouseId)
            ->get()
            ->groupBy('item_id');

        $summary = [];
        foreach ($stocks as $itemId => $itemStocks) {
            $item = $itemStocks->first()->item;
            $summary[] = [
                'item_id' => $itemId,
                'item_name' => $item->name,
                'item_sku' => $item->sku,
                'available_qty' => $itemStocks->where('bucket', 'available')->first()->current_qty ?? 0,
                'rejected_qty' => $itemStocks->where('bucket', 'rejected')->first()->current_qty ?? 0,
                'quarantine_qty' => $itemStocks->where('bucket', 'quarantine')->first()->current_qty ?? 0,
                'total_qty' => $itemStocks->sum('current_qty'),
            ];
        }

        return $summary;
    }

    /**
     * Validate stock availability
     */
    public function validateStockAvailability(int $itemId, int $warehouseId, float $requiredQty): bool
    {
        $availableStock = WarehouseCurrentStock::where('item_id', $itemId)
            ->where('warehouse_id', $warehouseId)
            ->where('bucket', 'available')
            ->first();

        return $availableStock && $availableStock->current_qty >= $requiredQty;
    }

    /**
     * Get stock movement history
     */
    public function getStockMovementHistory(
        int $itemId,
        int $warehouseId,
        string $bucket = null,
        string $sourceType = null,
        int $limit = 100
    ): \Illuminate\Database\Eloquent\Collection {
        $query = WarehouseStockLedger::with(['item', 'warehouse'])
            ->where('item_id', $itemId)
            ->where('warehouse_id', $warehouseId);

        if ($bucket) {
            $query->where('bucket', $bucket);
        }

        if ($sourceType) {
            $query->where('source_type', $sourceType);
        }

        return $query->orderBy('created_at', 'desc')
            ->limit($limit)
            ->get();
    }
}
