<?php

namespace App\Policy\Template;

use App\Policy\Entity\Policy;
use App\Policy\Service\PolicySchema;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;

/**
 * الگوهای آمادهٔ قانون — راهِ ۹۰٪ کاربران.
 *
 * کاربر غیرفنی نباید شرط خام بنویسد: الگو را انتخاب می‌کند، دو-سه مقدار پر می‌کند، و
 * `condition`/`effects` درست از همین‌جا ساخته می‌شود. حالت پیشرفته برای بقیه است.
 *
 * الگو **جایگزین** اعتبارسنجی نیست؛ خروجی‌اش هم از همان `ConditionEvaluator` رد می‌شود.
 */
final class PolicyTemplateRegistry
{
    private const TEMPLATES = [
        'min_days_between_sessions' => [
            'title'       => 'حداقل فاصله بین جلسات',
            'description' => 'بین دو جلسهٔ یک خدمت، حداقل چند روز فاصله باشد.',
            'category'    => Policy::CATEGORY_SPACING,
            'inputs'      => [
                ['key' => 'days', 'type' => 'int', 'label' => 'حداقل روز', 'min' => 1, 'max' => 365],
            ],
        ],
        'complex_min_duration' => [
            'title'       => 'حداقل مدت نوبت',
            'description' => 'نوبت این خدمت کمتر از این مقدار نباشد.',
            'category'    => Policy::CATEGORY_TIMING,
            'inputs'      => [
                ['key' => 'minutes', 'type' => 'int', 'label' => 'حداقل دقیقه', 'min' => 5, 'max' => 480],
            ],
        ],
        'extra_time_for_many_items' => [
            'title'       => 'زمان اضافه برای انتخاب‌های پرتعداد',
            'description' => 'وقتی بیمار بیش از N مورد انتخاب کند، به مدت نوبت اضافه شود.',
            'category'    => Policy::CATEGORY_TIMING,
            'inputs'      => [
                ['key' => 'item_count', 'type' => 'int', 'label' => 'بیشتر از چند مورد', 'min' => 1, 'max' => 20],
                ['key' => 'minutes', 'type' => 'int', 'label' => 'دقیقهٔ اضافه', 'min' => 5, 'max' => 120],
            ],
        ],
        'surgery_needs_surgeon' => [
            'title'       => 'نیاز به نقش خاص',
            'description' => 'این خدمت بدون حضور نقش مشخصی انجام نشود.',
            'category'    => Policy::CATEGORY_RESOURCE,
            'inputs'      => [
                ['key' => 'role', 'type' => 'resource_type_select', 'label' => 'نقش لازم'],
            ],
        ],
        'minor_needs_consent' => [
            'title'       => 'رضایت والدین برای زیر سن قانونی',
            'description' => 'بیمار زیر سن مشخص، بدون تأیید رضایت والدین نوبت نگیرد.',
            'category'    => Policy::CATEGORY_ELIGIBILITY,
            'inputs'      => [
                ['key' => 'age', 'type' => 'int', 'label' => 'سن مرزی', 'min' => 1, 'max' => 100],
            ],
        ],
        'vip_discount' => [
            'title'       => 'تخفیف بیمار وفادار',
            'description' => 'بیمارانی که بیش از N ویزیت داشته‌اند، درصدی تخفیف بگیرند.',
            'category'    => Policy::CATEGORY_PRICING,
            'inputs'      => [
                ['key' => 'visit_count', 'type' => 'int', 'label' => 'بیشتر از چند ویزیت', 'min' => 1, 'max' => 100],
                ['key' => 'percent', 'type' => 'int', 'label' => 'درصد تخفیف', 'min' => 1, 'max' => 100],
            ],
        ],
    ];

    /** @return list<array<string, mixed>> */
    public function describe(): array
    {
        $out = [];

        foreach (self::TEMPLATES as $key => $template) {
            $out[] = ['key' => $key] + $template;
        }

        return $out;
    }

    /**
     * @param array<string, mixed> $values
     * @return array{category: string, condition: array<string, mixed>, effects: list<array<string, mixed>>}
     */
    public function build(string $key, array $values): array
    {
        $template = self::TEMPLATES[$key] ?? null;

        if ($template === null) {
            throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'الگوی قانون شناخته نمی‌شود', 422, 'template');
        }

        foreach ($template['inputs'] as $input) {
            if ($input['type'] === 'int' && !is_numeric($values[$input['key']] ?? null)) {
                throw new AppException(
                    ErrorCodes::ERR_VALIDATION_002,
                    sprintf('مقدار «%s» الزامی است', $input['label']),
                    422,
                    $input['key'],
                );
            }
        }

        return ['category' => $template['category']] + $this->contentFor($key, $values);
    }

    /**
     * @param array<string, mixed> $v
     * @return array{condition: array<string, mixed>, effects: list<array<string, mixed>>}
     */
    private function contentFor(string $key, array $v): array
    {
        return match ($key) {
            'min_days_between_sessions' => [
                'condition' => [],
                'effects'   => [['type' => PolicySchema::EFFECT_MIN_DAYS_BETWEEN, 'value' => (int) $v['days']]],
            ],
            'complex_min_duration' => [
                'condition' => [],
                'effects'   => [['type' => PolicySchema::EFFECT_MIN_DURATION, 'value' => (int) $v['minutes']]],
            ],
            'extra_time_for_many_items' => [
                'condition' => ['match' => 'all', 'conditions' => [
                    ['field' => 'item_count', 'operator' => PolicySchema::OP_GREATER_THAN, 'value' => (int) $v['item_count']],
                ]],
                'effects'   => [['type' => PolicySchema::EFFECT_ADD_DURATION, 'value' => (int) $v['minutes']]],
            ],
            'surgery_needs_surgeon' => [
                'condition' => [],
                'effects'   => [['type' => PolicySchema::EFFECT_REQUIRE_RESOURCE, 'value' => (string) ($v['role'] ?? '')]],
            ],
            'minor_needs_consent' => [
                'condition' => ['match' => 'all', 'conditions' => [
                    ['field' => 'patient_age', 'operator' => PolicySchema::OP_LESS_THAN, 'value' => (int) $v['age']],
                ]],
                'effects'   => [['type' => PolicySchema::EFFECT_REQUIRE_FLAG, 'value' => 'has_parental_consent']],
            ],
            'vip_discount' => [
                'condition' => ['match' => 'all', 'conditions' => [
                    ['field' => 'visit_count', 'operator' => PolicySchema::OP_GREATER_THAN, 'value' => (int) $v['visit_count']],
                ]],
                'effects'   => [['type' => PolicySchema::EFFECT_DISCOUNT_PERCENT, 'value' => (int) $v['percent']]],
            ],
            default => throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'الگوی قانون شناخته نمی‌شود', 422, 'template'),
        };
    }
}
