<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Business\Inventory;

use App\Business\Inventory\Enums\StocktakeDifferenceStatus;
use App\Models\DberpInventoryTransaction;
use App\Models\DberpStocktakeDifference;
use App\Models\DberpStocktakeOrder;
use App\Models\DberpStocktakeOrderItem;
use App\Support\PluginIntegration\LightApprovalDocumentGuardBridge;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

final class StocktakeDifferenceService
{
    public function __construct(
        private readonly InventoryExceptionService $exceptionService,
    ) {}

    public function query(): Builder
    {
        return DberpStocktakeDifference::query()->with([
            'stocktakeOrder',
            'stocktakeOrderItem',
            'product.category',
            'warehouse',
            'inventoryTransaction',
            'handler',
        ]);
    }

    public function syncFromStocktakeItem(
        DberpStocktakeOrder $order,
        DberpStocktakeOrderItem $item,
        ?DberpInventoryTransaction $transaction = null,
    ): ?DberpStocktakeDifference {
        $differenceQuantity = (float) $item->difference_quantity;

        if (abs($differenceQuantity) < 0.0001) {
            $existing = DberpStocktakeDifference::query()
                ->where('stocktake_order_item_id', $item->getKey())
                ->first();

            if ($existing !== null) {
                $this->exceptionService->closeByStocktakeDifference($existing, '盘点结果已无差异。');
                $existing->delete();
            }

            return null;
        }

        $difference = DberpStocktakeDifference::query()->updateOrCreate(
            ['stocktake_order_item_id' => $item->getKey()],
            [
                'stocktake_order_id' => $order->getKey(),
                'inventory_transaction_id' => $transaction?->getKey(),
                'product_id' => $item->product_id,
                'product_sku_id' => $item->product_sku_id,
                'product_sku_code' => $item->product_sku_code,
                'product_sku_name' => $item->product_sku_name,
                'product_sku_spec' => $item->product_sku_spec,
                'warehouse_id' => $order->warehouse_id,
                'book_quantity' => $item->book_quantity,
                'actual_quantity' => $item->actual_quantity,
                'difference_quantity' => $item->difference_quantity,
                'result_type' => $item->result_type,
                'status' => StocktakeDifferenceStatus::Pending->value,
                'resolution_type' => null,
                'resolution_remark' => null,
                'handled_by' => null,
                'handled_at' => null,
                'sort_order' => (int) $item->sort_order,
            ],
        );

        $this->exceptionService->syncStocktakeDifference($difference);

        return $difference->refresh();
    }

    public function resolveVoidedOrder(DberpStocktakeOrder $order): void
    {
        $this->query()
            ->where('stocktake_order_id', $order->getKey())
            ->get()
            ->each(function (DberpStocktakeDifference $difference): void {
                $difference->forceFill([
                    'status' => StocktakeDifferenceStatus::Resolved->value,
                    'resolution_type' => 'voided',
                    'resolution_remark' => '盘点单已作废，差异自动关闭。',
                    'handled_by' => Auth::id(),
                    'handled_at' => now(),
                ])->save();

                $this->exceptionService->closeByStocktakeDifference($difference, '盘点单已作废，差异自动关闭。');
            });
    }

    public function markReviewed(DberpStocktakeDifference $difference, ?string $remark = null): DberpStocktakeDifference
    {
        $this->assertParentStockCountApproved($difference);

        $difference->forceFill([
            'status' => StocktakeDifferenceStatus::Reviewed->value,
            'resolution_remark' => $remark ?: $difference->resolution_remark,
            'handled_by' => Auth::id(),
            'handled_at' => now(),
        ])->save();

        $this->exceptionService->syncStocktakeDifference($difference);

        return $difference->refresh();
    }

    public function markResolved(DberpStocktakeDifference $difference, ?string $remark = null, ?string $resolutionType = null): DberpStocktakeDifference
    {
        $this->assertParentStockCountApproved($difference);

        $difference->forceFill([
            'status' => StocktakeDifferenceStatus::Resolved->value,
            'resolution_type' => $resolutionType ?: $difference->resolution_type,
            'resolution_remark' => $remark ?: $difference->resolution_remark,
            'handled_by' => Auth::id(),
            'handled_at' => now(),
        ])->save();

        $this->exceptionService->closeByStocktakeDifference($difference, $remark);

        return $difference->refresh();
    }

    public function markIgnored(DberpStocktakeDifference $difference, ?string $remark = null): DberpStocktakeDifference
    {
        $this->assertParentStockCountApproved($difference);

        $difference->forceFill([
            'status' => StocktakeDifferenceStatus::Ignored->value,
            'resolution_type' => 'ignored',
            'resolution_remark' => $remark ?: $difference->resolution_remark,
            'handled_by' => Auth::id(),
            'handled_at' => now(),
        ])->save();

        $this->exceptionService->ignoreByStocktakeDifference($difference, $remark);

        return $difference->refresh();
    }

    private function assertParentStockCountApproved(DberpStocktakeDifference $difference): void
    {
        $difference->loadMissing('stocktakeOrder');
        $order = $difference->stocktakeOrder;

        if ($order === null) {
            return;
        }

        if (! LightApprovalDocumentGuardBridge::isPluginEnabled()) {
            return;
        }

        if (! LightApprovalDocumentGuardBridge::requiresApproval(
            LightApprovalDocumentGuardBridge::ADAPTER_STOCK_COUNT,
            $order,
        )) {
            return;
        }

        if (LightApprovalDocumentGuardBridge::canConfirm(
            LightApprovalDocumentGuardBridge::ADAPTER_STOCK_COUNT,
            $order,
        )) {
            return;
        }

        throw ValidationException::withMessages([
            'approval' => (string) (LightApprovalDocumentGuardBridge::confirmBlockReason(
                LightApprovalDocumentGuardBridge::ADAPTER_STOCK_COUNT,
                $order,
            ) ?? __('plugin-dberp-light-approval::light_approval.guard_confirm_blocked')),
        ]);
    }
}
