<?php

namespace App\Domains\Routing\Services;

class SmartRoutingEngine
{
    /**
     * @param  array<string, mixed>  $config
     * @param  array<string, mixed>  $context
     */
    public function resolve(array $config, array $context): string
    {
        /** @var array<int, array<string, mixed>> $rawRules */
        $rawRules = $config['rules'] ?? [];
        $rules = collect($rawRules)->sortBy('priority');

        foreach ($rules as $rule) {
            if ($this->ruleMatches($rule, $context)) {
                return $rule['target'];
            }
        }

        return $config['fallback'] ?? '';
    }

    /**
     * @param  array<string, mixed>  $rule
     * @param  array<string, mixed>  $context
     */
    private function ruleMatches(array $rule, array $context): bool
    {
        $conditions = $rule['conditions'] ?? [];
        $matchMode = $rule['match'] ?? 'all';

        if (empty($conditions)) {
            return false;
        }

        $results = array_map(
            fn ($condition) => $this->evaluateCondition($condition, $context),
            $conditions
        );

        return $matchMode === 'all'
            ? ! in_array(false, $results, true)
            : in_array(true, $results, true);
    }

    /**
     * @param  array<string, mixed>  $condition
     * @param  array<string, mixed>  $context
     */
    private function evaluateCondition(array $condition, array $context): bool
    {
        if (! isset($condition['field'], $condition['op'], $condition['value'])) {
            return false;
        }

        $field = $condition['field'];
        $op = $condition['op'];
        $expected = $condition['value'];
        $actual = $context[$field] ?? null;

        return match ($op) {
            'equals' => $actual === $expected,
            'not_equals' => $actual !== $expected,
            'in' => in_array($actual, (array) $expected, true),
            'not_in' => ! in_array($actual, (array) $expected, true),
            'contains' => str_contains((string) $actual, (string) $expected),
            'starts_with' => str_starts_with((string) $actual, (string) $expected),
            'between' => is_array($expected) && count($expected) === 2 && $actual >= $expected[0] && $actual <= $expected[1],
            'greater_than' => $actual > $expected,
            'less_than' => $actual < $expected,
            default => false,
        };
    }
}
