<?php

declare(strict_types=1);

namespace App\Business\Sales;

use App\Business\Sales\Enums\CustomerPriceRuleStatus;
use App\Business\Sales\Enums\CustomerPriceRuleType;
use App\Models\DberpCustomerPriceRule;
use App\Models\DberpProduct;
use App\Services\DberpAuditService;
use App\Support\DberpPriceLevelDictionary;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

final class CustomerPriceRuleService
{
    public function __construct(
        private readonly DberpAuditService $auditService,
    ) {}

    /**
     * @param  array<string, mixed>  $data
     */
    public function createRule(array $data): DberpCustomerPriceRule
    {
        return DB::transaction(function () use ($data): DberpCustomerPriceRule {
            $payload = $this->normalizePayload($data);

            /** @var DberpCustomerPriceRule $rule */
            $rule = DberpCustomerPriceRule::query()->create($payload);

            return $rule;
        });
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public function updateRule(DberpCustomerPriceRule $rule, array $data): DberpCustomerPriceRule
    {
        return DB::transaction(function () use ($rule, $data): DberpCustomerPriceRule {
            $locked = DberpCustomerPriceRule::query()->whereKey($rule->getKey())->lockForUpdate()->firstOrFail();

            $locked->fill($this->normalizePayload($data, $locked));
            $locked->save();

            return $locked;
        });
    }

    public function disableRule(DberpCustomerPriceRule $rule): void
    {
        DB::transaction(function () use ($rule): void {
            $locked = DberpCustomerPriceRule::query()->whereKey($rule->getKey())->lockForUpdate()->firstOrFail();
            $before = ['status' => $locked->status];

            $this->auditService->logSemanticSave(
                'dberp.customer_price_rule.disabled',
                $locked,
                function () use ($locked): void {
                    $locked->forceFill(['status' => CustomerPriceRuleStatus::Inactive->value])->save();
                },
                $before,
                ['status' => $locked->status],
            );
        });
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    private function normalizePayload(array $data, ?DberpCustomerPriceRule $rule = null): array
    {
        $payload = Arr::only($data, [
            'rule_name',
            'rule_type',
            'customer_id',
            'price_level_code',
            'product_id',
            'external_product_id',
            'external_sku',
            'product_spec_snapshot',
            'unit_price',
            'sort_order',
            'valid_from',
            'valid_until',
            'status',
            'source_system',
            'channel_code',
            'external_reference_id',
            'remark',
        ]);

        $payload['status'] = $payload['status'] ?? $rule?->status ?? CustomerPriceRuleStatus::Active->value;
        $payload['source_system'] = $payload['source_system'] ?? $rule?->source_system ?? 'dberp';
        $payload['sort_order'] = $payload['sort_order'] ?? 0;
        $payload['valid_from'] ??= now()->toDateString();

        $this->validateScope($payload['rule_type'] ?? $rule?->rule_type ?? null, $payload);

        $product = isset($payload['product_id']) ? DberpProduct::query()->find($payload['product_id']) : null;

        if ($product !== null) {
            $payload['product_spec_snapshot'] = $payload['product_spec_snapshot'] ?? $product->spec_model;
            $payload['external_product_id'] = $payload['external_product_id'] ?? $product->external_product_id;
            $payload['external_sku'] = $payload['external_sku'] ?? $product->external_sku;
        }

        return $payload;
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function validateScope(?string $ruleType, array &$payload): void
    {
        $type = CustomerPriceRuleType::tryFrom((string) $ruleType);

        if ($type === null) {
            throw ValidationException::withMessages([
                'rule_type' => __('dberp.errors.customer_price_rule_type_invalid'),
            ]);
        }

        if ($type === CustomerPriceRuleType::Exclusive) {
            if (empty($payload['customer_id'])) {
                throw ValidationException::withMessages([
                    'customer_id' => __('dberp.errors.customer_price_rule_customer_required'),
                ]);
            }

            $payload['price_level_code'] = null;
        }

        if ($type === CustomerPriceRuleType::Level) {
            if (blank($payload['price_level_code'] ?? null)) {
                throw ValidationException::withMessages([
                    'price_level_code' => __('dberp.errors.customer_price_rule_level_required'),
                ]);
            }

            DberpPriceLevelDictionary::assertEnabledCode((string) $payload['price_level_code']);

            $payload['customer_id'] = null;
        }

        if ($type === CustomerPriceRuleType::Default) {
            $payload['customer_id'] = null;
            $payload['price_level_code'] = null;
        }
    }
}
