<?php

declare(strict_types=1);

namespace App\Config;

use InvalidArgumentException;

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

    /**
     * @return string[]
     */
    public function getConversationProductPatterns(): array
    {
        return $this->stringList('conversation_product_patterns');
    }

    /**
     * @return array<string, string>
     */
    public function getFocusTermPatterns(): array
    {
        return $this->stringMap('focus_term_patterns');
    }

    /**
     * @return string[]
     */
    private function stringList(string $path): array
    {
        $value = $this->value($path);

        if (!is_array($value)) {
            throw $this->invalid($path, 'must be a list of non-empty strings');
        }

        $out = [];

        foreach ($value as $item) {
            if (!is_scalar($item)) {
                continue;
            }

            $item = trim((string) $item);

            if ($item !== '' && !in_array($item, $out, true)) {
                $out[] = $item;
            }
        }

        if ($out === []) {
            throw $this->invalid($path, 'must contain at least one non-empty string');
        }

        return $out;
    }

    /**
     * @return array<string, string>
     */
    private function stringMap(string $path): array
    {
        $value = $this->value($path);

        if (!is_array($value)) {
            throw $this->invalid($path, 'must be a map of non-empty strings');
        }

        $out = [];

        foreach ($value as $key => $item) {
            if (!is_scalar($key) || !is_scalar($item)) {
                continue;
            }

            $cleanKey = trim((string) $key);
            $cleanValue = trim((string) $item);

            if ($cleanKey !== '' && $cleanValue !== '') {
                $out[$cleanKey] = $cleanValue;
            }
        }

        if ($out === []) {
            throw $this->invalid($path, 'must contain at least one non-empty mapping');
        }

        return $out;
    }

    private function value(string $path): mixed
    {
        $current = $this->config;

        foreach (explode('.', $path) as $segment) {
            if (!is_array($current) || !array_key_exists($segment, $current)) {
                throw $this->missing($path);
            }

            $current = $current[$segment];
        }

        return $current;
    }

    private function missing(string $path): InvalidArgumentException
    {
        return new InvalidArgumentException(sprintf('RetrieX commerce reference resolver config "%s" is missing.', $path));
    }

    private function invalid(string $path, string $reason): InvalidArgumentException
    {
        return new InvalidArgumentException(sprintf('RetrieX commerce reference resolver config "%s" %s.', $path, $reason));
    }
}
