<?php

declare(strict_types=1);

namespace App\Business\Sales;

use App\Business\Sales\Enums\SalesQuoteStatus;
use App\Business\Sales\Enums\SalesSourceType;
use App\Models\DberpSalesOrder;
use App\Models\DberpSalesQuote;
use App\Services\DberpAuditService;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

final class SalesQuoteConversionService
{
    public function __construct(
        private readonly DberpAuditService $auditService,
        private readonly SalesDocumentPricingService $salesDocumentPricingService,
        private readonly SalesOrderService $salesOrderService,
        private readonly SalesQuoteService $salesQuoteService,
    ) {}

    public function convertToSalesOrder(DberpSalesQuote $quote): DberpSalesOrder
    {
        return DB::transaction(function () use ($quote): DberpSalesOrder {
            $locked = DberpSalesQuote::query()
                ->with(['customer', 'items'])
                ->whereKey($quote->getKey())
                ->lockForUpdate()
                ->firstOrFail();

            $currentStatus = $this->salesQuoteService->resolveCurrentStatus($locked);

            if ($currentStatus !== SalesQuoteStatus::Effective) {
                throw ValidationException::withMessages([
                    'status' => __('dberp.errors.sales_quote_not_convertible'),
                ]);
            }

            if ($locked->converted_sales_order_id !== null) {
                throw ValidationException::withMessages([
                    'status' => __('dberp.errors.sales_quote_already_converted'),
                ]);
            }

            if ($locked->items->isEmpty()) {
                throw ValidationException::withMessages([
                    'items' => __('dberp.errors.sales_quote_no_items'),
                ]);
            }

            $businessTime = now();
            $paymentTermDays = $locked->suggested_payment_term_days
                ?? $locked->customer?->default_payment_term_days
                ?? 0;

            $order = $this->salesOrderService->createOrder([
                'customer_id' => $locked->customer_id,
                'quote_id' => $locked->getKey(),
                'quote_code' => $locked->code,
                'handler_employee_id' => $locked->handler_employee_id,
                'source_type' => SalesSourceType::Manual->value,
                'source_platform' => $locked->source_system ?? 'dberp',
                'business_time' => $businessTime,
                'payment_term_days' => $paymentTermDays,
                'remark' => $locked->remark,
            ]);

            foreach ($locked->items as $quoteItem) {
                $order->items()->create($this->salesDocumentPricingService->makeSalesOrderItemFromQuoteItem($quoteItem));
            }

            $this->salesOrderService->refreshTotals($order->refresh());

            $before = [
                'status' => $locked->status,
                'converted_sales_order_id' => $locked->converted_sales_order_id,
                'converted_sales_order_no' => $locked->converted_sales_order_no,
                'converted_at' => $locked->converted_at,
            ];

            $this->auditService->logSemanticSave(
                'dberp.sales_quote.converted',
                $locked,
                function () use ($locked, $order): void {
                    $locked->forceFill([
                        'status' => SalesQuoteStatus::Converted->value,
                        'converted_sales_order_id' => $order->getKey(),
                        'converted_sales_order_no' => $order->code,
                        'converted_at' => now(),
                    ])->save();
                },
                $before,
                [
                    'status' => $locked->status,
                    'converted_sales_order_id' => $locked->converted_sales_order_id,
                    'converted_sales_order_no' => $locked->converted_sales_order_no,
                    'converted_at' => $locked->converted_at,
                ],
            );

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