<?php

declare(strict_types=1);

namespace App\Config;

/**
 * YAML-backed thresholds for deterministic catalog-entity detection.
 *
 * This class intentionally has no PHP fallback values. Missing or invalid
 * configuration must be fixed in config/retriex/intent.yaml.
 */
final class CatalogIntentConfig
{
    /**
     * @param array<string, mixed> $config
     */
    public function __construct(private readonly array $config)
    {
    }

    public function getMinScore(): float
    {
        return $this->requiredFloatInRange('min_score', 0.0, 1.0);
    }

    public function getAmbiguityDelta(): float
    {
        return $this->requiredFloatInRange('ambiguity_delta', 0.0, 1.0);
    }

    public function getIntentSearchLimit(): int
    {
        return $this->requiredPositiveInt('intent_search_limit');
    }

    public function getListSearchLimit(): int
    {
        return $this->requiredPositiveInt('list_search_limit');
    }

    public function getMinAllowedScore(): float
    {
        return $this->requiredFloatInRange('min_allowed_score', 0.0, 1.0);
    }

    public function getMaxAllowedScore(): float
    {
        return $this->requiredFloatInRange('max_allowed_score', 0.0, 1.0);
    }

    public function isScoreAccepted(float $score): bool
    {
        return $score >= $this->getMinScore();
    }

    public function isAmbiguous(float $bestScore, float $secondScore): bool
    {
        return abs($bestScore - $secondScore) < $this->getAmbiguityDelta();
    }

    public function clampScore(float $score): float
    {
        return max($this->getMinAllowedScore(), min($this->getMaxAllowedScore(), $score));
    }

    private function requiredPositiveInt(string $key): int
    {
        if (!array_key_exists($key, $this->config)) {
            throw new \InvalidArgumentException(sprintf('Missing required RetrieX catalog intent config key "%s".', $key));
        }

        $value = $this->config[$key];

        if (is_int($value)) {
            $intValue = $value;
        } elseif (is_string($value) && preg_match('/^-?\d+$/', trim($value)) === 1) {
            $intValue = (int) trim($value);
        } else {
            throw new \InvalidArgumentException(sprintf('RetrieX catalog intent config key "%s" must be an integer.', $key));
        }

        if ($intValue <= 0) {
            throw new \InvalidArgumentException(sprintf('RetrieX catalog intent config key "%s" must be greater than 0.', $key));
        }

        return $intValue;
    }

    private function requiredFloatInRange(string $key, float $min, float $max): float
    {
        if (!array_key_exists($key, $this->config)) {
            throw new \InvalidArgumentException(sprintf('Missing required RetrieX catalog intent config key "%s".', $key));
        }

        $value = $this->config[$key];

        if (is_int($value) || is_float($value) || (is_string($value) && is_numeric(trim($value)))) {
            $floatValue = (float) $value;
        } else {
            throw new \InvalidArgumentException(sprintf('RetrieX catalog intent config key "%s" must be numeric.', $key));
        }

        if ($floatValue < $min || $floatValue > $max) {
            throw new \InvalidArgumentException(sprintf(
                'RetrieX catalog intent config key "%s" must be between %s and %s.',
                $key,
                (string) $min,
                (string) $max
            ));
        }

        return $floatValue;
    }
}
