<?php

declare(strict_types=1);

namespace App\Business\Sales;

use App\Business\Sales\Enums\PriceSourceType;
use App\Models\DberpCustomer;
use App\Models\DberpProduct;
use App\Models\DberpProductSku;
use App\Models\DberpSalesOrder;
use App\Models\DberpSalesQuote;
use App\Models\DberpSalesQuoteItem;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Validation\ValidationException;

final class SalesDocumentPricingService
{
    public function __construct(
        private readonly CustomerPriceResolverService $priceResolverService,
    ) {}

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    public function prepareQuoteItemData(DberpSalesQuote $quote, array $data): array
    {
        $customer = $quote->customer;

        if (! $customer instanceof DberpCustomer) {
            throw ValidationException::withMessages([
                'customer_id' => __('dberp.errors.sales_quote_customer_required'),
            ]);
        }

        return $this->prepareResolvedItemData($customer, $data);
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    public function prepareSalesOrderItemData(DberpSalesOrder $order, array $data): array
    {
        $customer = $order->customer;

        if (! $customer instanceof DberpCustomer) {
            throw ValidationException::withMessages([
                'customer_id' => __('dberp.errors.sales_order_customer_required'),
            ]);
        }

        return $this->prepareResolvedItemData($customer, $data);
    }

    /**
     * @return array<string, mixed>
     */
    public function makeSalesOrderItemFromQuoteItem(DberpSalesQuoteItem $quoteItem): array
    {
        $quoteSnapshotType = PriceSourceType::QuoteSnapshot;

        $productId = (int) $quoteItem->product_id;
        $skuId = (int) ($quoteItem->product_sku_id ?? 0);

        if ($skuId > 0) {
            $matchedSkuId = DberpProductSku::query()
                ->whereKey($skuId)
                ->where('product_id', $productId)
                ->where('is_enabled', true)
                ->value('id');

            $skuId = $matchedSkuId !== null ? (int) $matchedSkuId : 0;
        }

        if ($skuId <= 0) {
            $skuId = (int) (DberpProductSku::query()
                ->where('product_id', $productId)
                ->where('is_enabled', true)
                ->orderByDesc('is_default')
                ->orderBy('sort_order')
                ->orderBy('id')
                ->value('id') ?? 0);
        }

        if ($skuId <= 0) {
            $product = DberpProduct::query()->find($productId);
            if ($product instanceof DberpProduct) {
                $skuId = (int) DberpProductSku::createDefaultForProduct($product)->getKey();
            }
        }

        return [
            'product_id' => $productId,
            'product_sku_id' => $skuId !== null ? (int) $skuId : null,
            'quantity' => (string) $quoteItem->quantity,
            'unit_price' => (string) $quoteItem->unit_price,
            'discount_amount' => (string) $quoteItem->discount_amount,
            'external_product_id' => $quoteItem->external_product_id,
            'external_sku' => $quoteItem->external_sku,
            'source_quote_item_id' => $quoteItem->getKey(),
            'price_source_type' => $quoteSnapshotType->value,
            'price_source_label' => $quoteSnapshotType->label(),
            'price_rule_id' => $quoteItem->price_rule_id,
            'matched_unit_price' => (string) ($quoteItem->matched_unit_price ?? $quoteItem->unit_price),
            'reference_unit_price' => (string) ($quoteItem->reference_unit_price ?? $quoteItem->unit_price),
            'recent_transaction_unit_price' => $quoteItem->recent_transaction_unit_price,
            'recent_transaction_date' => $quoteItem->recent_transaction_date,
            'sort_order' => $quoteItem->sort_order,
            'remark' => $quoteItem->remark,
            'unit_id' => $quoteItem->unit_id,
            'unit_name' => $quoteItem->unit_name,
            'base_unit_id' => $quoteItem->base_unit_id,
            'auxiliary_unit_id' => $quoteItem->auxiliary_unit_id,
            'is_auxiliary_unit' => $quoteItem->is_auxiliary_unit,
            'auxiliary_unit_quantity' => $quoteItem->auxiliary_unit_quantity,
            'base_unit_quantity' => $quoteItem->base_unit_quantity,
        ];
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    private function prepareResolvedItemData(DberpCustomer $customer, array $data): array
    {
        $skuId = (int) ($data['product_sku_id'] ?? 0);
        $productId = (int) ($data['product_id'] ?? 0);

        if ($skuId > 0) {
            $sku = DberpProductSku::query()->with('product')->find($skuId);
            if (! $sku instanceof DberpProductSku) {
                throw ValidationException::withMessages([
                    'product_sku_id' => __('dberp.errors.sales_order_item_sku_invalid'),
                ]);
            }

            $product = $sku->product;
            if (! $product instanceof DberpProduct) {
                throw ValidationException::withMessages([
                    'product_sku_id' => __('dberp.errors.sales_order_item_sku_invalid'),
                ]);
            }

            $productId = (int) $product->getKey();
            $data['product_id'] = $productId;
        }

        $product = DberpProduct::query()->find($productId);

        if (! $product instanceof DberpProduct) {
            throw ValidationException::withMessages([
                'product_id' => __('dberp.errors.sales_item_product_required'),
            ]);
        }

        $externalSku = filled($data['external_sku'] ?? null)
            ? (string) $data['external_sku']
            : (string) ($product->external_sku ?? '');

        $resolved = $this->priceResolverService->resolvePrice($customer, $product, $externalSku !== '' ? $externalSku : null);
        $resolvedUnitPrice = number_format((float) $resolved['unit_price'], 4, '.', '');
        $inputUnitPrice = $data['unit_price'] ?? null;
        $hasManualInput = $inputUnitPrice !== null && $inputUnitPrice !== '';
        $finalUnitPrice = $resolvedUnitPrice;
        $priceSourceType = (string) $resolved['price_source_type'];
        $priceSourceLabel = (string) $resolved['price_source_label'];
        $priceRuleId = $resolved['price_rule_id'];

        if ($hasManualInput) {
            $manualUnitPrice = number_format((float) $inputUnitPrice, 4, '.', '');

            if (abs((float) $manualUnitPrice - (float) $resolvedUnitPrice) > 0.00005) {
                $finalUnitPrice = $manualUnitPrice;
                $priceSourceType = PriceSourceType::ManualOverride->value;
                $priceSourceLabel = PriceSourceType::ManualOverride->label();
                $priceRuleId = null;
            } else {
                $finalUnitPrice = $manualUnitPrice;
            }
        }

        $latestTransaction = $this->latestTransactionSnapshot($customer, $product);

        return array_merge($data, [
            'external_product_id' => $data['external_product_id'] ?? $product->external_product_id,
            'external_sku' => $externalSku !== '' ? $externalSku : $product->external_sku,
            'unit_price' => $finalUnitPrice,
            'price_source_type' => $priceSourceType,
            'price_source_label' => $priceSourceLabel,
            'price_rule_id' => $priceRuleId,
            'matched_unit_price' => $resolvedUnitPrice,
            'reference_unit_price' => number_format((float) $resolved['reference_unit_price'], 4, '.', ''),
            'recent_transaction_unit_price' => $latestTransaction['unit_price'],
            'recent_transaction_date' => $latestTransaction['business_date'],
        ]);
    }

    /**
     * @return array{unit_price: string|null, business_date: string|null}
     */
    private function latestTransactionSnapshot(DberpCustomer $customer, DberpProduct $product): array
    {
        $item = DberpSalesOrder::query()
            ->where('customer_id', $customer->getKey())
            ->whereIn('status', ['confirmed', 'partially_shipped', 'fully_shipped', 'closed'])
            ->whereHas('items', fn (Builder $query): Builder => $query->where('product_id', $product->getKey()))
            ->with([
                'items' => fn ($query) => $query
                    ->where('product_id', $product->getKey())
                    ->orderByDesc('id')
                    ->limit(1),
            ])
            ->orderByDesc('business_time')
            ->orderByDesc('id')
            ->first();

        $salesItem = $item?->items->first();

        return [
            'unit_price' => $salesItem ? number_format((float) $salesItem->unit_price, 4, '.', '') : null,
            'business_date' => $item?->business_time?->toDateString(),
        ];
    }
}
