<?php

declare(strict_types=1);

namespace App\Business\Purchase;

use App\Business\Inventory\Enums\InventoryReplenishmentSuggestionStatus;
use App\Business\Inventory\InventoryReplenishmentSuggestionService;
use App\Business\Purchase\Enums\PurchaseRequestSourceType;
use App\Business\Purchase\Enums\PurchaseRequestStatus;
use App\Models\DberpInventoryReplenishmentSuggestion;
use App\Models\DberpPurchaseRequest;
use App\Support\DberpDocumentRemarkPropagation;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

final class PurchaseRequestGenerationService
{
    public function __construct(
        private readonly PurchaseRequestService $purchaseRequestService,
        private readonly InventoryReplenishmentSuggestionService $replenishmentSuggestionService,
    ) {}

    public function createFromReplenishmentSuggestion(
        DberpInventoryReplenishmentSuggestion $suggestion,
        ?int $applicantEmployeeId = null,
        ?string $departmentName = null,
        ?int $suggestedSupplierId = null,
        ?string $remark = null,
    ): DberpPurchaseRequest {
        return DB::transaction(function () use ($suggestion, $applicantEmployeeId, $departmentName, $suggestedSupplierId, $remark): DberpPurchaseRequest {
            $locked = DberpInventoryReplenishmentSuggestion::query()
                ->with(['product', 'warehouse'])
                ->whereKey($suggestion->getKey())
                ->lockForUpdate()
                ->firstOrFail();

            if (! in_array($locked->status, [
                InventoryReplenishmentSuggestionStatus::Pending->value,
                InventoryReplenishmentSuggestionStatus::Reviewed->value,
            ], true)) {
                throw ValidationException::withMessages([
                    'status' => __('dberp.errors.replenishment_suggestion_not_requestable'),
                ]);
            }

            $exists = DberpPurchaseRequest::query()
                ->whereIn('status', [
                    PurchaseRequestStatus::Draft->value,
                    PurchaseRequestStatus::Pending->value,
                    PurchaseRequestStatus::PartiallyConverted->value,
                    PurchaseRequestStatus::Converted->value,
                    PurchaseRequestStatus::Closed->value,
                ])
                ->whereHas('items', function ($query) use ($locked): void {
                    $query->where('source_type', PurchaseRequestSourceType::ReplenishmentSuggestion->value)
                        ->where('source_id', $locked->getKey());
                })
                ->exists();

            if ($exists) {
                throw ValidationException::withMessages([
                    'source_id' => __('dberp.errors.purchase_request_source_already_generated'),
                ]);
            }

            $request = $this->purchaseRequestService->createRequest([
                'request_time' => now(),
                'source_type' => PurchaseRequestSourceType::ReplenishmentSuggestion->value,
                'source_system' => 'dberp',
                'source_id' => $locked->getKey(),
                'source_reference_no' => 'REPL-'.$locked->getKey(),
                'external_reference_id' => 'REPL-'.$locked->getKey(),
                'source_snapshot' => [
                    'suggestion_id' => $locked->getKey(),
                    'product_id' => $locked->product_id,
                    'product_code' => $locked->product?->code,
                    'product_name' => $locked->product?->name,
                    'warehouse_id' => $locked->warehouse_id,
                    'warehouse_name' => $locked->warehouse?->name,
                    'system_suggested_quantity' => (string) $locked->system_suggested_quantity,
                    'adjusted_quantity' => (string) $locked->adjusted_quantity,
                    'trigger_reason' => $locked->trigger_reason,
                    'metric_snapshot' => $locked->metric_snapshot,
                ],
                'department_name' => $departmentName,
                'applicant_employee_id' => $applicantEmployeeId,
                'suggested_supplier_id' => $suggestedSupplierId,
                'request_reason' => $locked->trigger_reason,
                'status' => PurchaseRequestStatus::Pending->value,
                'submitted_at' => now(),
                'remark' => DberpDocumentRemarkPropagation::resolveConversionHeaderRemark(
                    $remark,
                    $locked->remark,
                    '由补货建议生成采购申请。',
                ),
            ]);

            $lineRemark = DberpDocumentRemarkPropagation::merge($remark, $locked->remark);

            $request->items()->create([
                'product_id' => $locked->product_id,
                'external_product_id' => $locked->product?->external_product_id,
                'external_sku' => $locked->product?->external_sku,
                'warehouse_id' => $locked->warehouse_id,
                'source_type' => PurchaseRequestSourceType::ReplenishmentSuggestion->value,
                'source_id' => $locked->getKey(),
                'source_reference_no' => 'REPL-'.$locked->getKey(),
                'source_snapshot' => [
                    'suggestion_id' => $locked->getKey(),
                    'system_suggested_quantity' => (string) $locked->system_suggested_quantity,
                    'adjusted_quantity' => (string) $locked->adjusted_quantity,
                    'trigger_reason' => $locked->trigger_reason,
                    'metric_snapshot' => $locked->metric_snapshot,
                ],
                'request_quantity' => $locked->adjusted_quantity,
                'converted_quantity' => '0.0000',
                'sort_order' => 10,
                'remark' => $lineRemark,
            ]);

            $request->refresh();
            $this->purchaseRequestService->refreshTotals($request);
            $request->refresh();

            $this->replenishmentSuggestionService->markProcessed($locked, '已生成采购申请 '.$request->code);

            return $request->refresh();
        });
    }
}
