<?php

declare(strict_types=1);

namespace App\Domain\Policy;

/**
 * Outcome of evaluating an IP against a policy. Discriminates the *reason*
 * an IP is or isn't on the policy's blocklist so callers (UI ip-detail
 * page, distribution endpoint) can render the explanation.
 */
enum EvaluationOutcome: string
{
    case ExcludedByAllowlist = 'excluded_by_allowlist';
    case IncludedByManualBlock = 'included_by_manual_block';
    case IncludedByScore = 'included_by_score';
    case Excluded = 'excluded';
}

final class EvaluationResult
{
    /**
     * @param list<int> $matchedCategoryIds Category ids whose score met the threshold (only set for IncludedByScore).
     */
    public function __construct(
        public readonly EvaluationOutcome $outcome,
        public readonly array $matchedCategoryIds,
        public readonly ?float $maxScore,
    ) {
    }

    public static function excludedByAllowlist(): self
    {
        return new self(EvaluationOutcome::ExcludedByAllowlist, [], null);
    }

    public static function includedByManualBlock(): self
    {
        return new self(EvaluationOutcome::IncludedByManualBlock, [], null);
    }

    /**
     * @param list<int> $matchedCategoryIds
     */
    public static function includedByScore(array $matchedCategoryIds, float $maxScore): self
    {
        return new self(EvaluationOutcome::IncludedByScore, $matchedCategoryIds, $maxScore);
    }

    public static function excluded(): self
    {
        return new self(EvaluationOutcome::Excluded, [], null);
    }

    public function isIncluded(): bool
    {
        return $this->outcome === EvaluationOutcome::IncludedByScore
            || $this->outcome === EvaluationOutcome::IncludedByManualBlock;
    }
}
