<?php

declare(strict_types=1);

namespace App\Domain\Policy;

use App\Domain\Ip\IpAddress;
use App\Domain\Reputation\CidrEvaluator;

/**
 * Evaluates a single IP against a Policy + the current allowlist/manual-block
 * snapshot.
 *
 * Resolution order (SPEC §5):
 *   1. allowlist wins over everything → `ExcludedByAllowlist`
 *   2. manual block (only if `policy.includeManualBlocks`) → `IncludedByManualBlock`
 *   3. score-vs-threshold across the policy's mapped categories →
 *      `IncludedByScore` if any category meets its threshold
 *   4. otherwise → `Excluded`
 *
 * Stateless: the same evaluator instance can be reused across many IPs.
 */
final class PolicyEvaluator
{
    public function __construct(
        private readonly Policy $policy,
        private readonly CidrEvaluator $cidrEvaluator,
    ) {
    }

    /**
     * @param array<int, float> $scoresByCategory category_id => score
     */
    public function evaluate(IpAddress $ip, array $scoresByCategory): EvaluationResult
    {
        if ($this->cidrEvaluator->isAllowlisted($ip)) {
            return EvaluationResult::excludedByAllowlist();
        }

        $matched = [];
        $maxScore = null;
        foreach ($this->policy->thresholds as $categoryId => $threshold) {
            $score = $scoresByCategory[$categoryId] ?? null;
            if ($score === null) {
                continue;
            }
            if ($score >= $threshold) {
                $matched[] = $categoryId;
                if ($maxScore === null || $score > $maxScore) {
                    $maxScore = $score;
                }
            }
        }

        if ($matched !== []) {
            return EvaluationResult::includedByScore($matched, $maxScore ?? 0.0);
        }

        if ($this->policy->includeManualBlocks && $this->cidrEvaluator->isManuallyBlocked($ip)) {
            return EvaluationResult::includedByManualBlock();
        }

        return EvaluationResult::excluded();
    }

    public function policy(): Policy
    {
        return $this->policy;
    }
}
