<?php

declare(strict_types=1);

namespace App\Business\Purchase;

use App\Business\Purchase\Enums\PurchaseInquiryStatus;
use App\Models\DberpPurchaseInquiry;
use App\Models\DberpPurchaseInquiryItem;
use App\Models\DberpPurchaseInquiryQuote;
use App\Models\DberpPurchaseRequest;
use App\Models\DberpPurchaseRequestItem;
use App\Services\DberpAuditService;
use App\Support\DberpDocumentRemarkPropagation;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

final class PurchaseInquiryGenerationService
{
    public function __construct(
        private readonly DberpAuditService $auditService,
        private readonly PurchaseInquiryQuoteService $purchaseInquiryQuoteService,
        private readonly PurchaseSourceDocumentService $sourceDocumentService,
    ) {}

    /**
     * @param  array<int, string|int|float>  $itemQuantities
     * @param  list<int>  $supplierIds
     */
    public function generateFromPurchaseRequest(
        DberpPurchaseRequest $request,
        array $itemQuantities,
        array $supplierIds,
        ?int $handlerEmployeeId = null,
        mixed $inquiryDate = null,
        ?string $remark = null,
    ): DberpPurchaseInquiry {
        return DB::transaction(function () use ($request, $itemQuantities, $supplierIds, $handlerEmployeeId, $inquiryDate, $remark): DberpPurchaseInquiry {
            $locked = DberpPurchaseRequest::query()
                ->with(['items.product'])
                ->whereKey($request->getKey())
                ->lockForUpdate()
                ->firstOrFail();

            $this->sourceDocumentService->assertPurchaseRequestAllowsDownstreamProcessing($locked);

            $normalizedLines = $this->normalizeItemQuantities($itemQuantities);

            if ($normalizedLines === []) {
                throw ValidationException::withMessages([
                    'lines' => __('dberp.errors.purchase_request_no_inquiry_lines'),
                ]);
            }

            $normalizedSuppliers = $this->normalizeSupplierIds($supplierIds);

            if ($normalizedSuppliers === []) {
                throw ValidationException::withMessages([
                    'supplier_ids' => __('dberp.errors.purchase_inquiry_no_suppliers'),
                ]);
            }

            $inquiry = DberpPurchaseInquiry::query()->create([
                'purchase_request_id' => $locked->getKey(),
                'purchase_request_code' => $locked->code,
                'inquiry_date' => $inquiryDate ?? now()->toDateString(),
                'handler_employee_id' => $handlerEmployeeId ?? $locked->applicant_employee_id,
                'status' => PurchaseInquiryStatus::Open->value,
                'remark' => DberpDocumentRemarkPropagation::resolveConversionHeaderRemark(
                    $remark,
                    $locked->remark,
                    '由采购申请 '.$locked->code.' 生成询价。',
                ),
            ]);

            $createdItems = [];
            $createdQuotes = [];

            foreach ($locked->items as $item) {
                $inquiryQuantity = $normalizedLines[$item->getKey()] ?? 0.0;

                if ($inquiryQuantity <= 0) {
                    continue;
                }

                $this->sourceDocumentService->assertPurchaseRequestItemInquirable($item, $inquiryQuantity);

                $inquiryItem = $inquiry->items()->create($this->buildInquiryItemPayload($item, $inquiryQuantity));
                $createdItems[] = $inquiryItem;

                foreach ($normalizedSuppliers as $supplierId) {
                    $createdQuotes[] = $this->purchaseInquiryQuoteService->createPlaceholderForInquiryItem(
                        $inquiryItem,
                        $supplierId,
                        $inquiryDate,
                    );
                }
            }

            if ($createdItems === []) {
                throw ValidationException::withMessages([
                    'lines' => __('dberp.errors.purchase_request_no_inquiry_lines'),
                ]);
            }

            $inquiry = $inquiry->refresh();

            $this->auditService->log('dberp.purchase_request.generated_inquiry', $locked->refresh(), null, [
                'purchase_inquiry_id' => $inquiry->getKey(),
                'purchase_inquiry_code' => $inquiry->code,
                'supplier_ids' => $normalizedSuppliers,
                'inquiry_item_ids' => array_map(
                    static fn (DberpPurchaseInquiryItem $item): int => (int) $item->getKey(),
                    $createdItems,
                ),
                'quote_ids' => array_map(
                    static fn (DberpPurchaseInquiryQuote $quote): int => (int) $quote->getKey(),
                    $createdQuotes,
                ),
                'line_quantities' => $normalizedLines,
            ]);

            return $inquiry;
        });
    }

    /**
     * @param  array<int, string|int|float>  $itemQuantities
     * @return array<int, float>
     */
    private function normalizeItemQuantities(array $itemQuantities): array
    {
        $normalized = [];

        foreach ($itemQuantities as $itemId => $quantity) {
            $itemId = (int) $itemId;
            $quantity = (float) $quantity;

            if ($itemId <= 0 || $quantity <= 0) {
                continue;
            }

            $normalized[$itemId] = $quantity;
        }

        return $normalized;
    }

    /**
     * @param  list<int>  $supplierIds
     * @return list<int>
     */
    private function normalizeSupplierIds(array $supplierIds): array
    {
        $normalized = [];

        foreach ($supplierIds as $supplierId) {
            $supplierId = (int) $supplierId;

            if ($supplierId <= 0) {
                continue;
            }

            $normalized[$supplierId] = $supplierId;
        }

        return array_values($normalized);
    }

    /**
     * @return array<string, mixed>
     */
    private function buildInquiryItemPayload(DberpPurchaseRequestItem $item, float $inquiryQuantity): array
    {
        return [
            'purchase_request_item_id' => $item->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,
            'unit_id' => $item->unit_id,
            'unit_name' => $item->unit_name,
            'base_unit_id' => $item->base_unit_id,
            'auxiliary_unit_id' => $item->auxiliary_unit_id,
            'is_auxiliary_unit' => $item->is_auxiliary_unit,
            'auxiliary_unit_quantity' => $item->auxiliary_unit_quantity,
            'base_unit_quantity' => $item->base_unit_quantity,
            'warehouse_id' => $item->warehouse_id,
            'inquiry_quantity' => number_format($inquiryQuantity, 4, '.', ''),
            'sort_order' => $item->sort_order,
            'remark' => $item->remark,
        ];
    }
}
