<?php

namespace App\Policy\Engine;

use App\Doctor\Entity\DoctorAddress;
use App\ClinicService\Entity\ServiceItem;
use App\Policy\Entity\Policy;
use App\Policy\Service\PolicyResolver;
use App\Policy\ValueObject\PolicyOutcome;

/**
 * پایهٔ شش موتور دسته‌ای — بند ۸ مستند.
 *
 * هر دسته موتور خودش را دارد تا نقطهٔ مصرف بگوید «موتور قیمت را صدا می‌زنم»، نه «resolver
 * را با رشتهٔ `pricing` صدا می‌زنم». تفاوتش در **تایپ** است نه در منطق: حل تناقض و ترکیب
 * اثرها یکی است و در `PolicyResolver` می‌ماند؛ شش نسخهٔ کپی‌شدهٔ آن یعنی شش جای شکستن.
 *
 * `evaluateIsolated()` روی هر موتور هست چون آزمایشگاه قانون (تسک ۱۰) باید بتواند یک قانون
 * را بدون بقیه بسنجد — «این قانون تنها چه می‌کرد» سؤالی است که ترکیب جوابش را می‌پوشاند.
 */
abstract class PolicyEngine
{
    public function __construct(protected readonly PolicyResolver $resolver) {}

    abstract public function category(): string;

    /**
     * @param array<string, mixed> $facts
     */
    public function evaluate(
        string $entityType,
        int $entityId,
        array $facts,
        ?DoctorAddress $address = null,
        ?ServiceItem $service = null,
        ?int $at = null,
    ): PolicyOutcome {
        return $this->resolver->resolve($this->category(), $entityType, $entityId, $facts, $address, $service, $at);
    }

    /**
     * فقط همین یک قانون، بدون بقیه — برای آزمایشگاه.
     *
     * @param array<string, mixed> $facts
     */
    public function evaluateIsolated(Policy $policy, array $facts): PolicyOutcome
    {
        if ($policy->getCategory() !== $this->category()) {
            throw new \InvalidArgumentException(sprintf(
                'Policy "%s" belongs to category "%s", not "%s".',
                $policy->getUuid(),
                $policy->getCategory(),
                $this->category(),
            ));
        }

        return $this->resolver->evaluateOne($policy, $facts);
    }
}
