<?php

namespace App\Modules\Inventory\Services;

use App\Modules\Inventory\Contracts\InventoryServiceInterface;
use App\Modules\Inventory\Models\Inventory;
use App\Modules\Inventory\Models\Warehouse;
use App\Modules\Inventory\Models\Product;
use Illuminate\Support\Facades\DB;

class InventoryService implements InventoryServiceInterface
{
    public function getAllWarehouses()
    {
        return Warehouse::all();
    }

    public function getAllProducts()
    {
        return Product::with(['baseUnit', 'largeUnit'])->get();
    }

    public function getUnits()
    {
        return \App\Modules\Inventory\Models\Unit::all();
    }

    public function getInventoriesByIds(array $ids, array $with = [])
    {
        return Inventory::whereIn('id', $ids)->with($with)->get();
    }

    public function getProduct(int $id)
    {
        return Product::with(['baseUnit', 'largeUnit'])->find($id);
    }

    public function getProductsByIds(array $ids)
    {
        return Product::whereIn('id', $ids)->with(['baseUnit', 'largeUnit'])->get();
    }

    public function getProductsByName(string $name)
    {
        return Product::where('name', 'like', "%{$name}%")->with(['baseUnit', 'largeUnit'])->get();
    }

    public function getWarehouse(int $id)
    {
        return Warehouse::find($id);
    }

    public function checkStock(int $productId, int $warehouseId, float $quantity): bool
    {
        $stock = Inventory::where('product_id', $productId)
            ->where('warehouse_id', $warehouseId)
            ->sum('quantity');
            
        return $stock >= $quantity;
    }

    public function decreaseStock(int $productId, int $warehouseId, float $quantity, ?string $reason = null, bool $force = false, ?string $slot = null): void
    {
        DB::transaction(function () use ($productId, $warehouseId, $quantity, $reason, $force, $slot) {
            $query = Inventory::where('product_id', $productId)
                ->where('warehouse_id', $warehouseId)
                ->where('quantity', '>', 0);
            
            if ($slot) {
                $query->where('location', $slot);
            }

            $inventories = $query->orderBy('arrival_date', 'asc')
                ->get();

            $remainingToDecrease = $quantity;

            foreach ($inventories as $inventory) {
                if ($remainingToDecrease <= 0) break;

                $decreaseAmount = min($inventory->quantity, $remainingToDecrease);
                $this->decreaseInventoryQuantity($inventory->id, $decreaseAmount, $reason);
                $remainingToDecrease -= $decreaseAmount;
            }

            if ($remainingToDecrease > 0) {
                if ($force) {
                    // Find any existing inventory record in this warehouse/slot to subtract from, or create one
                    $query = Inventory::where('product_id', $productId)
                        ->where('warehouse_id', $warehouseId);
                    
                    if ($slot) {
                        $query->where('location', $slot);
                    }

                    $inventory = $query->first();
                    
                    if (!$inventory) {
                        $inventory = Inventory::create([
                            'warehouse_id' => $warehouseId,
                            'product_id' => $productId,
                            'location' => $slot,
                            'quantity' => 0,
                            'unit_cost' => 0,
                            'total_value' => 0,
                            'batch_number' => 'POS-AUTO-' . ($slot ? $slot . '-' : '') . time(),
                            'arrival_date' => now(),
                            'origin_country' => 'TW',
                            'quality_status' => 'normal',
                        ]);
                    }
                    
                    $this->decreaseInventoryQuantity($inventory->id, $remainingToDecrease, $reason);
                } else {
                    throw new \Exception("庫存不足，無法扣除所有請求的數量。");
                }
            }
        });
    }

    public function getInventoriesByWarehouse(int $warehouseId)
    {
        return Inventory::with(['product.baseUnit', 'product.largeUnit'])
            ->where('warehouse_id', $warehouseId)
            ->where('quantity', '>', 0)
            ->orderBy('arrival_date', 'asc')
            ->get();
    }

    public function createInventoryRecord(array $data)
    {
        return DB::transaction(function () use ($data) {
            // 嘗試查找是否已有相同批號的庫存
            $inventory = Inventory::where('warehouse_id', $data['warehouse_id'])
                ->where('product_id', $data['product_id'])
                ->where('batch_number', $data['batch_number'] ?? null)
                ->first();

            $balanceBefore = 0;

            if ($inventory) {
                // 若存在，則更新數量與相關資訊 (鎖定行以避免併發問題)
                $inventory = Inventory::lockForUpdate()->find($inventory->id);
                $balanceBefore = $inventory->quantity;
                
                // 加權平均成本計算 (可選，這裡先採簡單邏輯：若有新成本則更新，否則沿用)
                // 若本次入庫有指定成本，則更新該批次單價 (假設同批號成本相同)
                if (isset($data['unit_cost'])) {
                    $inventory->unit_cost = $data['unit_cost'];
                }
                
                $inventory->quantity += $data['quantity'];
                // 更新總價值
                $inventory->total_value = $inventory->quantity * $inventory->unit_cost;

                // 更新其他可能變更的欄位 (如最後入庫日)
                $inventory->arrival_date = $data['arrival_date'] ?? $inventory->arrival_date;
                $inventory->save();
            } else {
                // 若不存在，則建立新紀錄
                $unitCost = $data['unit_cost'] ?? 0;
                $inventory = Inventory::create([
                    'warehouse_id' => $data['warehouse_id'],
                    'product_id' => $data['product_id'],
                    'quantity' => $data['quantity'],
                    'unit_cost' => $unitCost,
                    'total_value' => $data['quantity'] * $unitCost,
                    'batch_number' => $data['batch_number'] ?? null,
                    'box_number' => $data['box_number'] ?? null,
                    'origin_country' => $data['origin_country'] ?? 'TW',
                    'arrival_date' => $data['arrival_date'] ?? now(),
                    'expiry_date' => $data['expiry_date'] ?? null,
                    'quality_status' => $data['quality_status'] ?? 'normal',
                    'source_purchase_order_id' => $data['source_purchase_order_id'] ?? null,
                ]);
            }

            \App\Modules\Inventory\Models\InventoryTransaction::create([
                'inventory_id' => $inventory->id,
                'type' => '入庫',
                'quantity' => $data['quantity'],
                'unit_cost' => $inventory->unit_cost, // 記錄當下成本
                'balance_before' => $balanceBefore,
                'balance_after' => $inventory->quantity,
                'reason' => $data['reason'] ?? '手動入庫',
                'reference_type' => $data['reference_type'] ?? null,
                'reference_id' => $data['reference_id'] ?? null,
                'user_id' => auth()->id(),
                'actual_time' => now(),
            ]);

            return $inventory;
        });
    }

    public function decreaseInventoryQuantity(int $inventoryId, float $quantity, ?string $reason = null, ?string $referenceType = null, $referenceId = null): void
    {
        DB::transaction(function () use ($inventoryId, $quantity, $reason, $referenceType, $referenceId) {
            $inventory = Inventory::lockForUpdate()->findOrFail($inventoryId);
            $balanceBefore = $inventory->quantity;
            
            $inventory->decrement('quantity', $quantity); // decrement 不會自動觸發 total_value 更新
            // 需要手動更新總價值
            $inventory->refresh();
            $inventory->total_value = $inventory->quantity * $inventory->unit_cost;
            $inventory->save();

            \App\Modules\Inventory\Models\InventoryTransaction::create([
                'inventory_id' => $inventory->id,
                'type' => '出庫',
                'quantity' => -$quantity,
                'unit_cost' => $inventory->unit_cost, // 記錄出庫時的成本
                'balance_before' => $balanceBefore,
                'balance_after' => $inventory->quantity,
                'reason' => $reason ?? '庫存扣減',
                'reference_type' => $referenceType,
                'reference_id' => $referenceId,
                'user_id' => auth()->id(),
                'actual_time' => now(),
            ]);
        });
    }

    public function findInventoryByBatch(int $warehouseId, int $productId, ?string $batchNumber)
    {
        return Inventory::where('warehouse_id', $warehouseId)
            ->where('product_id', $productId)
            ->where('batch_number', $batchNumber)
            ->first();
    }

    /**
     * 即時庫存查詢：統計卡片 + 分頁明細
     */
    public function getStockQueryData(array $filters = [], int $perPage = 10): array
    {
        $today = now()->toDateString();
        $expiryThreshold = now()->addDays(30)->toDateString();

        // 基礎查詢
        $query = Inventory::query()
            ->join('products', 'inventories.product_id', '=', 'products.id')
            ->join('warehouses', 'inventories.warehouse_id', '=', 'warehouses.id')
            ->leftJoin('categories', 'products.category_id', '=', 'categories.id')
            ->leftJoin('warehouse_product_safety_stocks as ss', function ($join) {
                $join->on('inventories.warehouse_id', '=', 'ss.warehouse_id')
                     ->on('inventories.product_id', '=', 'ss.product_id');
            })
            ->whereNull('inventories.deleted_at')
            ->select([
                'inventories.id',
                'inventories.warehouse_id',
                'inventories.product_id',
                'inventories.quantity',
                'inventories.batch_number',
                'inventories.expiry_date',
                'inventories.location',
                'inventories.quality_status',
                'products.code as product_code',
                'products.name as product_name',
                'categories.name as category_name',
                'warehouses.name as warehouse_name',
                'ss.safety_stock',
            ]);

        // 篩選：倉庫
        if (!empty($filters['warehouse_id'])) {
            $query->where('inventories.warehouse_id', $filters['warehouse_id']);
        }

        // 篩選：分類
        if (!empty($filters['category_id'])) {
            $query->where('products.category_id', $filters['category_id']);
        }

        // 篩選：關鍵字（商品代碼或名稱）
        if (!empty($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('products.code', 'like', "%{$search}%")
                  ->orWhere('products.name', 'like', "%{$search}%");
            });
        }

        // 篩選：狀態 (改為對齊聚合統計的判斷標準)
        if (!empty($filters['status'])) {
            switch ($filters['status']) {
                case 'low_stock':
                    $query->whereIn(DB::raw('(inventories.warehouse_id, inventories.product_id)'), function ($sub) {
                        $sub->select('i2.warehouse_id', 'i2.product_id')
                            ->from('inventories as i2')
                            ->join('warehouse_product_safety_stocks as ss2', function ($join) {
                                $join->on('i2.warehouse_id', '=', 'ss2.warehouse_id')
                                     ->on('i2.product_id', '=', 'ss2.product_id');
                            })
                            ->whereNull('i2.deleted_at')
                            ->groupBy('i2.warehouse_id', 'i2.product_id', 'ss2.safety_stock')
                            ->havingRaw('SUM(i2.quantity) <= ss2.safety_stock');
                    });
                    break;
                case 'negative':
                    $query->whereIn(DB::raw('(inventories.warehouse_id, inventories.product_id)'), function ($sub) {
                        $sub->select('i2.warehouse_id', 'i2.product_id')
                            ->from('inventories as i2')
                            ->whereNull('i2.deleted_at')
                            ->groupBy('i2.warehouse_id', 'i2.product_id')
                            ->havingRaw('SUM(i2.quantity) < 0');
                    });
                    break;
                case 'expiring':
                    $query->whereNotNull('inventories.expiry_date')
                          ->where('inventories.expiry_date', '>', $today)
                          ->where('inventories.expiry_date', '<=', $expiryThreshold);
                    break;
                case 'expired':
                    $query->whereNotNull('inventories.expiry_date')
                          ->where('inventories.expiry_date', '<=', $today);
                    break;
                case 'abnormal':
                    // 只要該「倉庫-品項」對應的總庫存有低庫存、負庫存，或該批次已過期/即將過期
                    $query->where(function ($q) use ($today, $expiryThreshold) {
                        // 1. 低庫存或負庫存 (依聚合判斷)
                        $q->whereIn(DB::raw('(inventories.warehouse_id, inventories.product_id)'), function ($sub) {
                            $sub->select('i3.warehouse_id', 'i3.product_id')
                                ->from('inventories as i3')
                                ->leftJoin('warehouse_product_safety_stocks as ss3', function ($join) {
                                    $join->on('i3.warehouse_id', '=', 'ss3.warehouse_id')
                                         ->on('i3.product_id', '=', 'ss3.product_id');
                                })
                                ->whereNull('i3.deleted_at')
                                ->groupBy('i3.warehouse_id', 'i3.product_id', 'ss3.safety_stock')
                                ->havingRaw('SUM(i3.quantity) < 0 OR (ss3.safety_stock IS NOT NULL AND SUM(i3.quantity) <= ss3.safety_stock)');
                        })
                        // 2. 或該批次效期異常
                        ->orWhere(function ($q_batch) use ($expiryThreshold) {
                            $q_batch->whereNotNull('inventories.expiry_date')
                                    ->where('inventories.expiry_date', '<=', $expiryThreshold);
                        });
                    });
                    break;
            }
        }

        // 排序
        $sortBy = $filters['sort_by'] ?? 'products.code';
        $sortOrder = $filters['sort_order'] ?? 'asc';
        $allowedSorts = ['products.code', 'products.name', 'warehouses.name', 'inventories.quantity', 'inventories.expiry_date'];
        if (in_array($sortBy, $allowedSorts)) {
            $query->orderBy($sortBy, $sortOrder);
        } else {
            $query->orderBy('products.code', 'asc');
        }

        // 統計卡片（預設無篩選條件下的全域統計，改為明細筆數計數以對齊顯示）
        // 1. 庫存明細總數
        $totalItems = DB::table('inventories')
            ->whereNull('deleted_at')
            ->count();

        // 2. 低庫存明細數：只要該明細所屬的「倉庫+商品」總量低於安全庫存，則所有相關明細都計入
        $lowStockCount = DB::table('inventories as i')
            ->join('warehouse_product_safety_stocks as ss', function ($join) {
                $join->on('i.warehouse_id', '=', 'ss.warehouse_id')
                     ->on('i.product_id', '=', 'ss.product_id');
            })
            ->whereNull('i.deleted_at')
            ->whereIn(DB::raw('(i.warehouse_id, i.product_id)'), function ($sub) {
                $sub->select('i2.warehouse_id', 'i2.product_id')
                    ->from('inventories as i2')
                    ->whereNull('i2.deleted_at')
                    ->groupBy('i2.warehouse_id', 'i2.product_id')
                    ->havingRaw('SUM(i2.quantity) <= (SELECT safety_stock FROM warehouse_product_safety_stocks WHERE warehouse_id = i2.warehouse_id AND product_id = i2.product_id LIMIT 1)');
            })
            ->count();

        // 3. 負庫存明細數
        $negativeCount = DB::table('inventories as i')
            ->whereNull('i.deleted_at')
            ->whereIn(DB::raw('(i.warehouse_id, i.product_id)'), function ($sub) {
                $sub->select('i2.warehouse_id', 'i2.product_id')
                    ->from('inventories as i2')
                    ->whereNull('i2.deleted_at')
                    ->groupBy('i2.warehouse_id', 'i2.product_id')
                    ->havingRaw('SUM(i2.quantity) < 0');
            })
            ->count();

        // 4. 即將過期明細數 (必須排除已過期)
        $expiringCount = DB::table('inventories')
            ->whereNull('deleted_at')
            ->whereNotNull('expiry_date')
            ->where('expiry_date', '>', $today)
            ->where('expiry_date', '<=', $expiryThreshold)
            ->count();

        // 分頁
        $paginated = $query->paginate($perPage)->withQueryString();

        // 為每筆紀錄附加最後入庫/出庫時間 + 狀態
        $items = collect($paginated->items())->map(function ($item) use ($today, $expiryThreshold) {
            $lastIn = \App\Modules\Inventory\Models\InventoryTransaction::where('inventory_id', $item->id)
                ->where('type', '入庫')
                ->orderByDesc('actual_time')
                ->value('actual_time');

            $lastOut = \App\Modules\Inventory\Models\InventoryTransaction::where('inventory_id', $item->id)
                ->where('type', '出庫')
                ->orderByDesc('actual_time')
                ->value('actual_time');

            // 計算狀態
            $statuses = [];
            if ($item->quantity < 0) {
                $statuses[] = 'negative';
            }
            if ($item->safety_stock !== null && $item->quantity <= $item->safety_stock && $item->quantity >= 0) {
                $statuses[] = 'low_stock';
            }
            if ($item->expiry_date) {
                if ($item->expiry_date <= $today) {
                    $statuses[] = 'expired';
                } elseif ($item->expiry_date <= $expiryThreshold) {
                    $statuses[] = 'expiring';
                }
            }
            if (empty($statuses)) {
                $statuses[] = 'normal';
            }

            return [
                'id' => $item->id,
                'product_code' => $item->product_code,
                'product_name' => $item->product_name,
                'category_name' => $item->category_name,
                'warehouse_name' => $item->warehouse_name,
                'batch_number' => $item->batch_number,
                'quantity' => $item->quantity,
                'safety_stock' => $item->safety_stock,
                'expiry_date' => $item->expiry_date ? \Carbon\Carbon::parse($item->expiry_date)->toDateString() : null,
                'location' => $item->location,
                'quality_status' => $item->quality_status ?? null,
                'last_inbound' => $lastIn ? \Carbon\Carbon::parse($lastIn)->toDateString() : null,
                'last_outbound' => $lastOut ? \Carbon\Carbon::parse($lastOut)->toDateString() : null,
                'statuses' => $statuses,
            ];
        });

        return [
            'summary' => [
                'totalItems' => $totalItems,
                'lowStockCount' => $lowStockCount,
                'negativeCount' => $negativeCount,
                'expiringCount' => $expiringCount,
            ],
            'data' => $items->toArray(),
            'pagination' => [
                'total' => $paginated->total(),
                'per_page' => $paginated->perPage(),
                'current_page' => $paginated->currentPage(),
                'last_page' => $paginated->lastPage(),
                'links' => $paginated->linkCollection()->toArray(),
            ],
        ];
    }

    public function getDashboardStats(): array
    {
        $today = now()->toDateString();
        $expiryThreshold = now()->addDays(30)->toDateString();

        // 1. 庫存品項數 (明細總數)
        $totalItems = DB::table('inventories')
            ->whereNull('deleted_at')
            ->count();

        // 2. 低庫存 (明細計數：只要該明細所屬的「倉庫+商品」總量低於安全庫存，則所有相關明細都計入)
        $lowStockCount = DB::table('inventories as i')
            ->join('warehouse_product_safety_stocks as ss', function ($join) {
                $join->on('i.warehouse_id', '=', 'ss.warehouse_id')
                     ->on('i.product_id', '=', 'ss.product_id');
            })
            ->whereNull('i.deleted_at')
            ->whereIn(DB::raw('(i.warehouse_id, i.product_id)'), function ($sub) {
                $sub->select('i2.warehouse_id', 'i2.product_id')
                    ->from('inventories as i2')
                    ->whereNull('i2.deleted_at')
                    ->groupBy('i2.warehouse_id', 'i2.product_id')
                    ->havingRaw('SUM(i2.quantity) <= (SELECT safety_stock FROM warehouse_product_safety_stocks WHERE warehouse_id = i2.warehouse_id AND product_id = i2.product_id LIMIT 1)');
            })
            ->count();

        // 3. 負庫存 (明細計數)
        $negativeCount = DB::table('inventories as i')
            ->whereNull('i.deleted_at')
            ->whereIn(DB::raw('(i.warehouse_id, i.product_id)'), function ($sub) {
                $sub->select('i2.warehouse_id', 'i2.product_id')
                    ->from('inventories as i2')
                    ->whereNull('i2.deleted_at')
                    ->groupBy('i2.warehouse_id', 'i2.product_id')
                    ->havingRaw('SUM(i2.quantity) < 0');
            })
            ->count();

        // 4. 即將過期 (明細計數)
        $expiringCount = DB::table('inventories')
            ->whereNull('deleted_at')
            ->whereNotNull('expiry_date')
            ->where('expiry_date', '>', $today) // 確保不過期 (getStockQueryData 沒加這個但這裡加上以防與 expired 混淆? 不，stock query 是 > today && <= threshold)
            ->where('expiry_date', '<=', $expiryThreshold)
            ->count();

        // 異常庫存前 10 筆 (明細面依然以個別批次為主，供快速跳轉)
        $abnormalItems = Inventory::query()
            ->join('products', 'inventories.product_id', '=', 'products.id')
            ->join('warehouses', 'inventories.warehouse_id', '=', 'warehouses.id')
            ->leftJoin('warehouse_product_safety_stocks as ss', function ($join) {
                $join->on('inventories.warehouse_id', '=', 'ss.warehouse_id')
                     ->on('inventories.product_id', '=', 'ss.product_id');
            })
            ->whereNull('inventories.deleted_at')
            ->where(function ($q) use ($today, $expiryThreshold) {
                // 1. 屬於低庫存或負庫存品項的批次
                $q->whereIn(DB::raw('(inventories.warehouse_id, inventories.product_id)'), function ($sub) {
                    $sub->select('i3.warehouse_id', 'i3.product_id')
                        ->from('inventories as i3')
                        ->leftJoin('warehouse_product_safety_stocks as ss3', function ($join) {
                            $join->on('i3.warehouse_id', '=', 'ss3.warehouse_id')
                                 ->on('i3.product_id', '=', 'ss3.product_id');
                        })
                        ->whereNull('i3.deleted_at')
                        ->groupBy('i3.warehouse_id', 'i3.product_id', 'ss3.safety_stock')
                        ->havingRaw('SUM(i3.quantity) < 0 OR (ss3.safety_stock IS NOT NULL AND SUM(i3.quantity) <= ss3.safety_stock)');
                })
                // 2. 或單一批次效期異常
                ->orWhere(function ($q2) use ($expiryThreshold) {
                    $q2->whereNotNull('inventories.expiry_date')
                       ->where('inventories.expiry_date', '<=', $expiryThreshold);
                });
            })
            ->select([
                'inventories.id',
                'inventories.quantity',
                'inventories.expiry_date',
                'products.code as product_code',
                'products.name as product_name',
                'warehouses.name as warehouse_name',
                'ss.safety_stock',
            ])
            ->orderBy('inventories.id', 'desc')
            ->limit(10)
            ->get()
            ->map(function ($item) use ($today, $expiryThreshold) {
                $statuses = [];
                if ($item->quantity < 0) {
                    $statuses[] = 'negative';
                }
                if ($item->safety_stock !== null && $item->quantity <= $item->safety_stock && $item->quantity >= 0) {
                    $statuses[] = 'low_stock';
                }
                if ($item->expiry_date) {
                    if ($item->expiry_date <= $today) {
                        $statuses[] = 'expired';
                    } elseif ($item->expiry_date <= $expiryThreshold) {
                        $statuses[] = 'expiring';
                    }
                }
                return [
                    'id' => $item->id,
                    'product_code' => $item->product_code,
                    'product_name' => $item->product_name,
                    'warehouse_name' => $item->warehouse_name,
                    'quantity' => $item->quantity,
                    'safety_stock' => $item->safety_stock,
                    'expiry_date' => $item->expiry_date,
                    'statuses' => $statuses,
                ];
            })
            ->toArray();

        return [
            'productsCount' => $totalItems, 
            'warehousesCount' => Warehouse::count(),
            'lowStockCount' => $lowStockCount,
            'negativeCount' => $negativeCount,
            'expiringCount' => $expiringCount,
            'totalInventoryQuantity' => Inventory::sum('quantity'),
            'abnormalItems' => $abnormalItems,
        ];
    }
}

