<?php

declare(strict_types=1);

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 Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;

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
    {
        $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
    {
        $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
    {
        $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();
    }
}
