<?php

declare(strict_types=1);

namespace App\Config;

final class CommerceIntentConfig
{
    /**
     * @param array<string, mixed> $config
     */
    public function __construct(private readonly array $config = [])
    {
    }

    /** @return string[] */
    public function getStrongSignalsList(): array
    {
        return $this->requiredStringList('strong_signals');
    }

    /** @return string[] */
    public function getAdvisorySignals(): array
    {
        return $this->requiredStringList('advisory_signals');
    }

    /** @return string[] */
    public function getAdvisoryProductSelectionPatterns(): array
    {
        return $this->requiredStringList('advisory_product_selection_patterns');
    }

    /** @return string[] */
    public function getPriceTerms(): array
    {
        return $this->requiredStringList('price_terms');
    }

    public function getPricePattern(): string
    {
        return implode('|', $this->getPriceTerms());
    }

    /** @return string[] */
    public function getColorTerms(): array
    {
        return $this->requiredStringList('color_terms');
    }

    public function getColorPattern(): string
    {
        return implode('|', $this->getColorTerms());
    }

    /** @return string[] */
    public function getSizeTokenTerms(): array
    {
        return $this->requiredStringList('size_token_terms');
    }

    public function getSizeTokenPattern(): string
    {
        return implode('|', $this->getSizeTokenTerms());
    }

    /** @return string[] */
    public function getSizeTerms(): array
    {
        return $this->requiredStringList('size_terms');
    }

    public function getSizePattern(): string
    {
        return implode('|', $this->getSizeTerms());
    }

    public function getSizeExtractionPattern(): string
    {
        return $this->renderPatternTemplate('patterns.size_extraction_template', [
            'size_pattern' => $this->getSizePattern(),
        ]);
    }

    /** @return string[] */
    public function getSupportDiagnosticPatterns(): array
    {
        return $this->requiredStringList('support_diagnostic_patterns');
    }

    /** @return string[] */
    public function getExplicitCommerceIntentPatterns(): array
    {
        return $this->requiredStringList('explicit_commerce_intent_patterns');
    }

    public function getSkuLikePattern(): string
    {
        return $this->requiredString('patterns.sku_like');
    }

    public function getPriceValuePattern(): string
    {
        return $this->renderPatternTemplate('patterns.price_value_template', [
            'price_pattern' => $this->getPricePattern(),
        ]);
    }

    public function getSizeValuePattern(): string
    {
        return $this->renderPatternTemplate('patterns.size_value_template', [
            'size_pattern' => $this->getSizePattern(),
        ]);
    }

    public function getSizeTokenValuePattern(): string
    {
        return $this->renderPatternTemplate('patterns.size_token_value_template', [
            'size_token_pattern' => $this->getSizeTokenPattern(),
        ]);
    }

    public function getColorValuePattern(): string
    {
        return $this->renderPatternTemplate('patterns.color_value_template', [
            'color_pattern' => $this->getColorPattern(),
        ]);
    }

    public function getSupportOrDiagnosticSignalLabel(): string
    {
        return $this->requiredString('labels.support_or_diagnostic_signal');
    }

    public function getSkuSignalLabel(): string
    {
        return $this->requiredString('labels.sku_signal');
    }

    public function getPriceSignalLabel(): string
    {
        return $this->requiredString('labels.price_signal');
    }

    public function getSizeSignalLabel(): string
    {
        return $this->requiredString('labels.size_signal');
    }

    public function getSizeTokenSignalLabel(): string
    {
        return $this->requiredString('labels.size_token_signal');
    }

    public function getColorSignalLabel(): string
    {
        return $this->requiredString('labels.color_signal');
    }

    public function getAdvisorySignalPrefix(): string
    {
        return $this->requiredString('labels.advisory_signal_prefix');
    }

    public function getAdvisoryProductSelectionSignalLabel(): string
    {
        return $this->requiredString('labels.advisory_product_selection_signal');
    }

    public function getProductSearchMinScore(): int
    {
        return $this->requiredInt('scores.product_search_min_score');
    }

    public function getAdvisoryProductSearchMinScore(): int
    {
        return $this->requiredInt('scores.advisory_product_search_min_score');
    }

    public function getStrongSignalScore(): int
    {
        return $this->requiredInt('scores.strong_signal_score');
    }

    public function getSkuSignalScore(): int
    {
        return $this->requiredInt('scores.sku_signal_score');
    }

    public function getPriceSignalScore(): int
    {
        return $this->requiredInt('scores.price_signal_score');
    }

    public function getSizeSignalScore(): int
    {
        return $this->requiredInt('scores.size_signal_score');
    }

    public function getSizeTokenSignalScore(): int
    {
        return $this->requiredInt('scores.size_token_signal_score');
    }

    public function getColorSignalScore(): int
    {
        return $this->requiredInt('scores.color_signal_score');
    }

    public function getAdvisorySignalScore(): int
    {
        return $this->requiredInt('scores.advisory_signal_score');
    }

    public function getAdvisoryProductSelectionSignalScore(): int
    {
        return $this->requiredInt('scores.advisory_product_selection_signal_score');
    }

    public function getModelLikeProductPattern(): string
    {
        return $this->requiredString('patterns.model_like_product');
    }

    public function getModelLikeProductSignalLabel(): string
    {
        return $this->requiredString('labels.model_like_product_signal');
    }

    public function getModelLikeProductSignalScore(): int
    {
        return $this->requiredInt('scores.model_like_product_signal_score');
    }

    private function requiredInt(string $key): int
    {
        $value = $this->getRequiredValue($key);
        if (!is_int($value)) {
            throw new \InvalidArgumentException(sprintf('RetrieX commerce intent config key "%s" must be an integer.', $key));
        }

        return $value;
    }

    private function requiredString(string $key): string
    {
        $value = $this->getRequiredValue($key);
        if (!is_scalar($value)) {
            throw new \InvalidArgumentException(sprintf('RetrieX commerce intent config key "%s" must be a string.', $key));
        }

        $value = trim((string) $value);
        if ($value === '') {
            throw new \InvalidArgumentException(sprintf('RetrieX commerce intent config key "%s" must not be empty.', $key));
        }

        return $value;
    }

    /** @return string[] */
    private function requiredStringList(string $key): array
    {
        $value = $this->getRequiredValue($key);
        if (!is_array($value)) {
            throw new \InvalidArgumentException(sprintf('RetrieX commerce intent config key "%s" must be a list.', $key));
        }

        $out = [];
        foreach ($value as $item) {
            if (!is_scalar($item)) {
                continue;
            }

            $item = trim((string) $item);
            if ($item === '' || in_array($item, $out, true)) {
                continue;
            }

            $out[] = $item;
        }

        if ($out === []) {
            throw new \InvalidArgumentException(sprintf('RetrieX commerce intent config key "%s" must contain at least one value.', $key));
        }

        return $out;
    }

    /**
     * @param array<string, string> $replacements
     */
    private function renderPatternTemplate(string $key, array $replacements): string
    {
        $template = $this->requiredString($key);
        $replace = [];
        foreach ($replacements as $placeholder => $value) {
            $replace['{' . $placeholder . '}'] = $value;
        }

        $pattern = strtr($template, $replace);
        if (preg_match('/\{[A-Za-z_][A-Za-z0-9_]*\}/', $pattern) === 1) {
            throw new \InvalidArgumentException(sprintf('RetrieX commerce intent pattern template "%s" contains unresolved placeholders.', $key));
        }

        return $pattern;
    }

    private function getRequiredValue(string $key): mixed
    {
        $parts = explode('.', $key);
        $value = $this->config;
        foreach ($parts as $part) {
            if (!is_array($value) || !array_key_exists($part, $value)) {
                throw new \InvalidArgumentException(sprintf('Missing required RetrieX commerce intent config key "%s".', $key));
            }
            $value = $value[$part];
        }

        return $value;
    }
}
