<?php

declare(strict_types=1);

namespace App\Business\Inventory\Exceptions;

use App\Business\Inventory\Enums\InventoryBusinessType;
use App\Models\DberpProduct;
use App\Models\DberpProductSku;
use App\Models\DberpWarehouse;
use App\Support\DberpNumberFormatter;
use Illuminate\Validation\ValidationException;

final class InsufficientStockException extends \RuntimeException
{
    public function __construct(
        public readonly int $productId,
        public readonly ?int $productSkuId,
        public readonly int $warehouseId,
        public readonly string $required,
        public readonly string $available,
        public readonly string $businessType,
        public readonly string $businessNumber,
        public readonly string $displayProductLabel,
        string $message,
    ) {
        parent::__construct($message);
    }

    public static function fromModels(
        DberpProduct $product,
        DberpWarehouse $warehouse,
        string $required,
        string $available,
        InventoryBusinessType $businessType,
        string $businessNumber,
        ?DberpProductSku $productSku = null,
    ): self {
        $displayProduct = self::resolveDisplayProductLabel($product, $productSku);
        $requiredFormatted = self::formatQuantityForMessage($required);
        $availableFormatted = self::formatQuantityForMessage($available);

        if (bccomp($available, '0', 4) !== 1) {
            $message = (string) __('dberp.errors.outbound_no_stock_in_warehouse');
        } else {
            $message = (string) __('dberp.errors.outbound_insufficient_stock_detail', [
                'product' => $displayProduct,
                'available' => $availableFormatted,
                'required' => $requiredFormatted,
            ]);
        }

        return new self(
            productId: (int) $product->getKey(),
            productSkuId: $productSku?->getKey(),
            warehouseId: (int) $warehouse->getKey(),
            required: $required,
            available: $available,
            businessType: $businessType->value,
            businessNumber: $businessNumber,
            displayProductLabel: $displayProduct,
            message: $message,
        );
    }

    private static function resolveDisplayProductLabel(DberpProduct $product, ?DberpProductSku $productSku): string
    {
        $displayProduct = (string) $product->name;
        if ($productSku !== null) {
            $spec = trim((string) ($productSku->spec_text ?? ''));
            $code = trim((string) ($productSku->sku_code ?? ''));
            $suffix = $spec !== '' ? '（'.$spec.'）' : ($code !== '' ? '（'.$code.'）' : '');
            $displayProduct = (string) $product->name.$suffix;
        }

        return $displayProduct;
    }

    private static function formatQuantityForMessage(string $quantity): string
    {
        return DberpNumberFormatter::quantity($quantity);
    }

    /**
     * @return array<string, int|string|null>
     */
    public function auditContext(): array
    {
        return [
            'product_id' => $this->productId,
            'product_sku_id' => $this->productSkuId,
            'warehouse_id' => $this->warehouseId,
            'required' => $this->required,
            'available' => $this->available,
            'business_type' => $this->businessType,
            'business_number' => $this->businessNumber,
        ];
    }

    public function validationException(): ValidationException
    {
        return ValidationException::withMessages([
            'confirm' => $this->getMessage(),
        ]);
    }
}
