<?php

namespace Modules\Bms\Services\Transfers;

use Illuminate\Support\Facades\DB;
use Modules\Bms\Entities\BmsHall;
use Modules\Bms\Entities\BmsTransferItem;
use Modules\Bms\Entities\BmsTransferRequest;
use Modules\Bms\Services\Contracts\AssetServiceContract;
use Modules\Bms\Services\Contracts\StockServiceContract;

/**
 * Warehouse→hall / hall→hall transfers with a mandatory in-app acceptance
 * audit. Stock/asset state only moves on acceptance — the process forces the
 * tool, so there is no paper handoff. Every acceptance records the acceptor,
 * the time, and the quantity/condition confirmed per item.
 */
class TransferService
{
    /**
     * Create a transfer request with its line items.
     *
     * @param  array  $data  type, from_location_id, from_hall_id, to_hall_id,
     *                        booking_id, notes, items[] (kind/product_id/
     *                        variation_id/name/quantity)
     */
    public function createRequest(int $businessId, array $data, ?int $requestedBy = null): BmsTransferRequest
    {
        $toHall = BmsHall::where('business_id', $businessId)->findOrFail($data['to_hall_id']);
        $fromLocation = $data['from_location_id'] ?? null;
        if (! empty($data['from_hall_id'])) {
            $fromLocation = optional(BmsHall::where('business_id', $businessId)->find($data['from_hall_id']))->location_id;
        }

        return DB::transaction(function () use ($businessId, $data, $toHall, $fromLocation, $requestedBy) {
            $transfer = BmsTransferRequest::create([
                'business_id'      => $businessId,
                'type'             => $data['type'] ?? 'warehouse_to_hall',
                'from_location_id' => $fromLocation,
                'from_hall_id'     => $data['from_hall_id'] ?? null,
                'to_hall_id'       => $toHall->id,
                'to_location_id'   => $toHall->location_id,
                'booking_id'       => $data['booking_id'] ?? null,
                'status'           => 'requested',
                'requested_by'     => $requestedBy,
                'notes'            => $data['notes'] ?? null,
            ]);

            foreach ((array) ($data['items'] ?? []) as $item) {
                BmsTransferItem::create([
                    'business_id'  => $businessId,
                    'transfer_id'  => $transfer->id,
                    'kind'         => $item['kind'] ?? 'consumable',
                    'product_id'   => $item['product_id'] ?? null,
                    'variation_id' => $item['variation_id'] ?? null,
                    'asset_id'     => $item['asset_id'] ?? null,
                    'name'         => $item['name'],
                    'quantity'     => (float) ($item['quantity'] ?? 1),
                ]);
            }

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

    /**
     * Accept a transfer — records the acceptance audit and posts the ERP stock
     * movement for any mapped consumables.
     *
     * @param  array  $itemAcceptance  [item_id => ['accepted_quantity','condition']]
     */
    public function accept(BmsTransferRequest $transfer, int $acceptorId, array $itemAcceptance = []): BmsTransferRequest
    {
        if (in_array($transfer->status, ['accepted', 'rejected', 'cancelled'], true)) {
            throw new \RuntimeException('This transfer is already ' . $transfer->status . '.');
        }

        return DB::transaction(function () use ($transfer, $acceptorId, $itemAcceptance) {
            $stockLines = [];
            $shortfall = [];
            $assetMoves = 0;
            foreach ($transfer->items as $item) {
                $a = $itemAcceptance[$item->id] ?? [];
                $acceptedQty = isset($a['accepted_quantity']) ? (float) $a['accepted_quantity'] : (float) $item->quantity;
                $condition = $a['condition'] ?? 'good';
                $item->update([
                    'accepted_quantity' => $acceptedQty,
                    'condition'         => $condition,
                ]);

                if ($item->kind === 'consumable' && $item->product_id && $item->variation_id) {
                    $stockLines[] = [
                        'product_id'   => $item->product_id,
                        'variation_id' => $item->variation_id,
                        'quantity'     => $acceptedQty,
                    ];
                    // Short acceptance on a stock-mapped item = a reorder signal.
                    $gap = (float) $item->quantity - $acceptedQty;
                    if ($gap > 0) {
                        $shortfall[] = [
                            'product_id'   => $item->product_id,
                            'variation_id' => $item->variation_id,
                            'quantity'     => $gap,
                            'location_id'  => $transfer->to_location_id,
                            'notes'        => 'Short on transfer #' . $transfer->id . ' (' . $item->name . ')',
                        ];
                    }
                }

                // Registered asset → move its location through AssetService (audited).
                if ($item->kind === 'asset' && $item->asset_id && $transfer->to_location_id) {
                    $assets = app(AssetServiceContract::class);
                    if ($assets->find((int) $transfer->business_id, (int) $item->asset_id)) {
                        $assets->transferLocation(
                            (int) $transfer->business_id,
                            (int) $item->asset_id,
                            (int) $transfer->to_location_id,
                            [
                                'to_hall_id'  => $transfer->to_hall_id,
                                'condition'   => $condition,
                                'transfer_id' => $transfer->id,
                                'quantity'    => $acceptedQty,
                                'moved_by'    => $acceptorId,
                                'reason'      => 'transfer',
                            ]
                        );
                        $assetMoves++;
                    }
                }
            }

            $stockResult = null;
            if (! empty($stockLines) && $transfer->from_location_id && $transfer->to_location_id) {
                $stockResult = app(StockServiceContract::class)->postTransfer(
                    (int) $transfer->business_id,
                    (int) $transfer->from_location_id,
                    (int) $transfer->to_location_id,
                    $stockLines,
                    ['transfer_id' => $transfer->id]
                );
            }

            // Auto-raise a Purchase Requisition for the shortfall (opt-in).
            $prId = null;
            if (! empty($shortfall) && \Modules\Bms\Entities\BmsSetting::get((int) $transfer->business_id, 'auto_pr_on_shortfall', false)) {
                try {
                    $prId = app(\Modules\Bms\Services\Contracts\PurchaseServiceContract::class)->createPurchaseRequest(
                        (int) $transfer->business_id,
                        $shortfall,
                        ['location_id' => $transfer->to_location_id, 'created_by' => $acceptorId,
                         'notes' => 'Auto-raised from BMS transfer #' . $transfer->id . ' shortfall']
                    );
                } catch (\Throwable $e) {
                    \Log::warning('[BMS] shortfall PR failed: ' . $e->getMessage());
                }
            }

            $transfer->update([
                'status'      => 'accepted',
                'accepted_by' => $acceptorId,
                'accepted_at' => now(),
                'stock_refs'  => ['moved' => $stockResult, 'lines' => count($stockLines), 'assets' => $assetMoves, 'shortfall_pr' => $prId],
            ]);

            return $transfer->fresh('items');
        });
    }

    public function reject(BmsTransferRequest $transfer, int $userId, ?string $reason = null): BmsTransferRequest
    {
        $transfer->update([
            'status' => 'rejected',
            'accepted_by' => $userId,
            'accepted_at' => now(),
            'notes' => trim(($transfer->notes ? $transfer->notes . ' | ' : '') . 'Rejected: ' . ($reason ?? '')),
        ]);
        return $transfer;
    }
}
