<?php

namespace App\Modules\Inventory\Services;

use App\Modules\Inventory\Models\StoreRequisition;
use App\Modules\Inventory\Models\StoreRequisitionItem;
use App\Modules\Inventory\Models\InventoryTransferOrder;
use App\Modules\Inventory\Models\InventoryTransferItem;
use App\Modules\Inventory\Notifications\StoreRequisitionNotification;
use App\Modules\Core\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class StoreRequisitionService
{
    protected TransferService $transferService;

    public function __construct(TransferService $transferService)
    {
        $this->transferService = $transferService;
    }

    /**
     * 建立叫貨單（含明細）
     */
    public function create(array $data, array $items, int $userId, bool $submitImmediately = false): StoreRequisition
    {
        return DB::transaction(function () use ($data, $items, $userId, $submitImmediately) {
            $requisition = new StoreRequisition([
                'store_warehouse_id' => $data['store_warehouse_id'],
                'status' => $submitImmediately ? 'pending' : 'draft',
                'submitted_at' => $submitImmediately ? now() : null,
                'remark' => $data['remark'] ?? null,
                'created_by' => $userId,
            ]);
            
            // 手動產生單號，因為 saveQuietly 會繞過模型事件
            if (empty($requisition->doc_no)) {
                $today = date('Ymd');
                $prefix = 'SR-' . $today . '-';
                $lastDoc = StoreRequisition::where('doc_no', 'like', $prefix . '%')
                    ->orderBy('doc_no', 'desc')
                    ->first();
                if ($lastDoc) {
                    $lastNumber = substr($lastDoc->doc_no, -2);
                    $nextNumber = str_pad((int)$lastNumber + 1, 2, '0', STR_PAD_LEFT);
                } else {
                    $nextNumber = '01';
                }
                $requisition->doc_no = $prefix . $nextNumber;
            }

            // 靜默建立以抑制自動日誌
            $requisition->saveQuietly();

            $itemsToInsert = [];
            $productIds = collect($items)->pluck('product_id')->unique()->toArray();
            $products = \App\Modules\Inventory\Models\Product::whereIn('id', $productIds)->get()->keyBy('id');
            
            $diff = ['added' => [], 'removed' => [], 'updated' => []];
            foreach ($items as $item) {
                $itemsToInsert[] = [
                    'store_requisition_id' => $requisition->id,
                    'product_id' => $item['product_id'],
                    'requested_qty' => $item['requested_qty'],
                    'remark' => $item['remark'] ?? null,
                    'created_at' => now(),
                    'updated_at' => now(),
                ];
                
                $product = $products->get($item['product_id']);
                $diff['added'][] = [
                    'product_name' => $product?->name ?? '未知商品',
                    'new' => [
                        'quantity' => (float)$item['requested_qty'],
                        'remark' => $item['remark'] ?? null,
                    ]
                ];
            }
            StoreRequisitionItem::insert($itemsToInsert);

            // 如果需直接提交，觸發通知
            if ($submitImmediately) {
                $this->notifyApprovers($requisition, 'submitted', $userId);
            }

            // 手動發送高品質日誌
            activity()
                ->performedOn($requisition)
                ->causedBy($userId)
                ->event('created')
                ->withProperties([
                    'items_diff' => $diff,
                    'attributes' => [
                        'doc_no' => $requisition->doc_no,
                        'store_warehouse_id' => $requisition->store_warehouse_id,
                        'status' => $requisition->status,
                        'remark' => $requisition->remark,
                        'created_by' => $requisition->created_by,
                        'submitted_at' => $requisition->submitted_at,
                    ]
                ])
                ->log('created');

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

    /**
     * 更新叫貨單（僅限 draft / rejected 狀態）
     */
    public function update(StoreRequisition $requisition, array $data, array $items): StoreRequisition
    {
        if (!in_array($requisition->status, ['draft', 'rejected'])) {
            throw ValidationException::withMessages([
                'status' => '僅能編輯草稿或被駁回的叫貨單',
            ]);
        }

        return DB::transaction(function () use ($requisition, $data, $items) {
            // 擷取舊狀態供日誌對照
            $oldAttributes = [
                'store_warehouse_id' => $requisition->store_warehouse_id,
                'remark' => $requisition->remark,
            ];

            // 手動更新屬性
            $requisition->store_warehouse_id = $data['store_warehouse_id'];
            $requisition->remark = $data['remark'] ?? null;
            $requisition->reject_reason = null; // 清除駁回原因

            // 品項對比邏輯
            $oldItems = $requisition->items()->with('product:id,name')->get();
            $oldItemsMap = $oldItems->keyBy('product_id');
            $newItemsMap = collect($items)->keyBy('product_id');

            $diff = [
                'added' => [],
                'removed' => [],
                'updated' => [],
            ];

            // 1. 處理更新與新增
            foreach ($items as $itemData) {
                $productId = $itemData['product_id'];
                $newQty = (float)$itemData['requested_qty'];
                $newRemark = $itemData['remark'] ?? null;

                if ($oldItemsMap->has($productId)) {
                    $oldItem = $oldItemsMap->get($productId);
                    if ((float)$oldItem->requested_qty !== $newQty || $oldItem->remark !== $newRemark) {
                        $diff['updated'][] = [
                            'product_name' => $oldItem->product?->name ?? '未知商品',
                            'old' => [
                                'quantity' => (float)$oldItem->requested_qty,
                                'remark' => $oldItem->remark,
                            ],
                            'new' => [
                                'quantity' => $newQty,
                                'remark' => $newRemark,
                            ]
                        ];
                    }
                    $oldItemsMap->forget($productId);
                } else {
                    $product = \App\Modules\Inventory\Models\Product::find($productId);
                    $diff['added'][] = [
                        'product_name' => $product?->name ?? '未知商品',
                        'new' => [
                            'quantity' => $newQty,
                            'remark' => $newRemark,
                        ]
                    ];
                }
            }

            // 2. 處理移除
            foreach ($oldItemsMap as $productId => $oldItem) {
                $diff['removed'][] = [
                    'product_name' => $oldItem->product?->name ?? '未知商品',
                    'old' => [
                        'quantity' => (float)$oldItem->requested_qty,
                        'remark' => $oldItem->remark,
                    ]
                ];
            }

            // 儲存實際變動
            $requisition->items()->delete();
            $itemsToInsert = [];
            foreach ($items as $item) {
                $itemsToInsert[] = [
                    'store_requisition_id' => $requisition->id,
                    'product_id' => $item['product_id'],
                    'requested_qty' => $item['requested_qty'],
                    'remark' => $item['remark'] ?? null,
                    'created_at' => now(),
                    'updated_at' => now(),
                ];
            }
            StoreRequisitionItem::insert($itemsToInsert);

            // 檢查是否有任何變動 (主表或明細)
            $isDirty = $requisition->isDirty();
            $hasItemsDiff = !empty($diff['added']) || !empty($diff['removed']) || !empty($diff['updated']);

            if ($isDirty || $hasItemsDiff) {
                // 擷取新狀態
                $newAttributes = [
                    'store_warehouse_id' => $requisition->store_warehouse_id,
                    'remark' => $requisition->remark,
                ];

                // 靜默更新
                $requisition->saveQuietly();

                // 手動發送紀錄
                activity()
                    ->performedOn($requisition)
                    ->event('updated')
                    ->withProperties([
                        'items_diff' => $diff,
                        'attributes' => $newAttributes,
                        'old' => $oldAttributes
                    ])
                    ->log('updated');
            }

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

    /**
     * 提交審核（draft → pending）
     */
    public function submit(StoreRequisition $requisition, int $userId): StoreRequisition
    {
        if ($requisition->status !== 'draft' && $requisition->status !== 'rejected') {
            throw ValidationException::withMessages([
                'status' => '僅能提交草稿或被駁回的叫貨單',
            ]);
        }

        if ($requisition->items()->count() === 0) {
            throw ValidationException::withMessages([
                'items' => '叫貨單必須至少有一項商品',
            ]);
        }

        $requisition->update([
            'status' => 'pending',
            'submitted_at' => now(),
            'reject_reason' => null,
        ]);

        // 通知有審核權限的使用者
        $this->notifyApprovers($requisition, 'submitted', $userId);

        return $requisition;
    }

    /**
     * 核准叫貨單（pending → approved），選擇供貨倉庫並自動產生調撥單
     */
    public function approve(StoreRequisition $requisition, array $data, int $userId): StoreRequisition
    {
        if ($requisition->status !== 'pending') {
            throw ValidationException::withMessages([
                'status' => '僅能核准待審核的叫貨單',
            ]);
        }

        return DB::transaction(function () use ($requisition, $data, $userId) {
            // 處理前端傳來的明細與批號資料
            $processedItems = []; // 暫存處理後的明細，用於轉入調撥單
            
            if (isset($data['items'])) {
                $requisition->load('items.product');
                $reqItemMap = $requisition->items->keyBy('id');

                foreach ($data['items'] as $itemData) {
                    $reqItemId = $itemData['id'];
                    $reqItem = $reqItemMap->get($reqItemId);
                    $productName = $reqItem?->product?->name ?? '未知商品';
                    $totalApprovedQty = 0;
                    $batches = $itemData['batches'] ?? [];

                    // 如果有批號，根據批號展開。若有多個無批號(null)的批次(例如來自不同貨道)，則將其數量加總
                    if (!empty($batches)) {
                        $batchGroups = [];
                        foreach ($batches as $batch) {
                            $qty = (float)($batch['qty'] ?? 0);
                            $bNum = $batch['batch_number'] ?? null;
                            $invId = $batch['inventory_id'] ?? null;
                            
                            if ($qty > 0) {
                                if ($invId) {
                                    $inventory = \App\Modules\Inventory\Models\Inventory::lockForUpdate()->find($invId);
                                    if ($inventory) {
                                        $available = max(0, $inventory->quantity - $inventory->reserved_quantity);
                                        if ($qty > $available) {
                                            $batchStr = $bNum ? "批號 {$bNum}" : "無批號";
                                            throw ValidationException::withMessages([
                                                'items' => "「{$productName}」的 {$batchStr} 數量({$qty})不可大於可用庫存({$available})",
                                            ]);
                                        }
                                    }
                                }

                                $totalApprovedQty += $qty;
                                $batchKey = $bNum ?? '';
                                $batchGroups[$batchKey] = ($batchGroups[$batchKey] ?? 0) + $qty;
                            }
                        }
                        
                        foreach ($batchGroups as $bNumKey => $qty) {
                            $processedItems[] = [
                                'req_item_id' => $reqItemId,
                                'batch_number' => $bNumKey === '' ? null : $bNumKey,
                                'quantity' => $qty,
                            ];
                        }
                    } else {
                        // 無批號，傳統輸入
                        $qty = (float)($itemData['approved_qty'] ?? 0);
                        if ($qty > 0) {
                            $supplyWarehouseId = $requisition->supply_warehouse_id;
                            $totalAvailable = \App\Modules\Inventory\Models\Inventory::where('warehouse_id', $supplyWarehouseId)
                                ->where('product_id', $reqItem->product_id)
                                ->lockForUpdate() // 補上鎖定
                                ->selectRaw('SUM(quantity - reserved_quantity) as available')
                                ->value('available') ?? 0;
                                
                            if ($qty > $totalAvailable) {
                                throw ValidationException::withMessages([
                                    'items' => "「{$productName}」的數量({$qty})不可大於供貨倉可用總庫存({$totalAvailable})",
                                ]);
                            }

                            $totalApprovedQty += $qty;
                            $processedItems[] = [
                                'req_item_id' => $reqItemId,
                                'batch_number' => null,
                                'quantity' => $qty,
                            ];
                        }
                    }

                    // 更新叫貨單明細的核准數量總和
                    StoreRequisitionItem::where('id', $reqItemId)
                        ->where('store_requisition_id', $requisition->id)
                        ->update(['approved_qty' => $totalApprovedQty]);
                }
            }

            // 優先使用傳入的供貨倉庫，若無則從單據中取得
            $supplyWarehouseId = $requisition->supply_warehouse_id;

            if (!$supplyWarehouseId) {
                throw ValidationException::withMessages([
                    'supply_warehouse_id' => '請指定供貨倉庫',
                ]);
            }

            // 查詢供貨倉庫是否有預設在途倉
            $supplyWarehouse = \App\Modules\Inventory\Models\Warehouse::find($supplyWarehouseId);
            $defaultTransitId = $supplyWarehouse?->default_transit_warehouse_id;

            // 產生調撥單（供貨倉庫 → 門市倉庫）
            $transferOrder = $this->transferService->createOrder(
                fromWarehouseId: $supplyWarehouseId,
                toWarehouseId: $requisition->store_warehouse_id,
                remarks: "由叫貨單 {$requisition->doc_no} 自動產生",
                userId: $userId,
                transitWarehouseId: $defaultTransitId,
            );

            // 將核准的明細寫入調撥單
            $requisition->load('items');
            $transferItems = [];
            
            // 建立 req_item_id 對應 product_id 的 lookup
            $reqItemMap = $requisition->items->keyBy('id');

            foreach ($processedItems as $pItem) {
                $reqItem = $reqItemMap->get($pItem['req_item_id']);
                if ($reqItem) {
                    $transferItems[] = [
                        'product_id' => $reqItem->product_id,
                        'batch_number' => $pItem['batch_number'],
                        'quantity' => $pItem['quantity'],
                    ];
                }
            }

            if (!empty($transferItems)) {
                $this->transferService->updateItems($transferOrder, $transferItems);
                
                // 手動發送調撥單的「已建立」合併日誌，包含初始明細
                activity()
                    ->performedOn($transferOrder)
                    ->causedBy($userId)
                    ->event('created')
                    ->withProperties(array_merge(
                        ['items_diff' => $transferOrder->activityProperties['items_diff'] ?? []],
                        [
                            'attributes' => [
                                'doc_no' => $transferOrder->doc_no,
                                'from_warehouse_id' => $transferOrder->from_warehouse_id,
                                'to_warehouse_id' => $transferOrder->to_warehouse_id,
                                'transit_warehouse_id' => $transferOrder->transit_warehouse_id,
                                'remarks' => $transferOrder->remarks,
                                'status' => $transferOrder->status,
                                'created_by' => $transferOrder->created_by,
                            ]
                        ]
                    ))
                    ->log('created');
            }

            // 更新叫貨單狀態
            $requisition->update([
                'status' => 'approved',
                'supply_warehouse_id' => $supplyWarehouseId,
                'approved_by' => $userId,
                'approved_at' => now(),
                'transfer_order_id' => $transferOrder->id,
            ]);

            // 通知申請人
            $this->notifyCreator($requisition, 'approved', $userId);

            return $requisition->load(['items', 'transferOrder']);
        });
    }

    /**
     * 駁回叫貨單（pending → rejected）
     */
    public function reject(StoreRequisition $requisition, string $reason, int $userId): StoreRequisition
    {
        if ($requisition->status !== 'pending') {
            throw ValidationException::withMessages([
                'status' => '僅能駁回待審核的叫貨單',
            ]);
        }

        $requisition->update([
            'status' => 'rejected',
            'reject_reason' => $reason,
            'approved_by' => $userId,
            'approved_at' => now(),
        ]);

        // 通知申請人
        $this->notifyCreator($requisition, 'rejected', $userId);

        return $requisition;
    }

    /**
     * 取消叫貨單
     */
    public function cancel(StoreRequisition $requisition): StoreRequisition
    {
        if (!in_array($requisition->status, ['draft', 'pending'])) {
            throw ValidationException::withMessages([
                'status' => '僅能取消草稿或待審核的叫貨單',
            ]);
        }

        $requisition->update(['status' => 'cancelled']);

        return $requisition;
    }

    /**
     * 通知有審核權限的使用者
     */
    protected function notifyApprovers(StoreRequisition $requisition, string $action, int $actorId): void
    {
        $actor = User::find($actorId);
        $actorName = $actor?->name ?? 'System';

        // 找出有 store_requisitions.approve 權限的使用者
        $approvers = User::permission('store_requisitions.approve')->get();

        foreach ($approvers as $approver) {
            if ($approver->id !== $actorId) {
                $approver->notify(new StoreRequisitionNotification($requisition, $action, $actorName));
            }
        }
    }

    /**
     * 通知叫貨單申請人
     */
    protected function notifyCreator(StoreRequisition $requisition, string $action, int $actorId): void
    {
        $actor = User::find($actorId);
        $actorName = $actor?->name ?? 'System';

        $creator = User::find($requisition->created_by);
        if ($creator && $creator->id !== $actorId) {
            $creator->notify(new StoreRequisitionNotification($requisition, $action, $actorName));
        }
    }
}
