"""
Rule Engine для декларативной логики
"""

import logging
from typing import Any

from ui_router.state.variables import VariableRepository
from ui_router.schema import ConditionalFlag, Rule, RuleCondition, ConditionOperator, VariableScope
from ui_router.state.context import ExecutionContext
from ui_router.exceptions import RuleEngineError


logger = logging.getLogger(__name__)


class ConditionEvaluator:
    """
    Единый evaluator для проверки условий

    Объединяет логику из RuleEngine и dynamic_keyboard для проверки условий
    """

    @staticmethod
    def evaluate_simple_condition(condition: RuleCondition, data: dict[str, Any]) -> bool:
        """
        Проверить простое условие на основе данных из словаря

        Используется в dynamic_keyboard для проверки условий на item данных

        Args:
            condition: Условие для проверки (RuleCondition)
            data: Словарь с данными (например, item из списка)

        Returns:
            bool: Результат проверки
        """
        var_value = data.get(condition.variable)

        compare_value = condition.value

        if isinstance(compare_value, str) and compare_value.startswith("{") and compare_value.endswith("}"):
            var_name = compare_value[1:-1]
            compare_value = data.get(var_name)

        return ConditionEvaluator._apply_operator(var_value, condition.operator, compare_value)

    @staticmethod
    def _apply_operator(left: Any, operator: ConditionOperator, right: Any) -> bool:
        """Применить оператор сравнения"""
        try:
            if operator == ConditionOperator.EQ:
                return left == right

            if operator == ConditionOperator.NE:
                return left != right

            if operator == ConditionOperator.GT:
                return left > right

            if operator == ConditionOperator.GTE:
                return left >= right

            if operator == ConditionOperator.LT:
                return left < right

            if operator == ConditionOperator.LTE:
                return left <= right

            if operator == ConditionOperator.IN:
                return left in right

            if operator == ConditionOperator.NOT_IN:
                return left not in right

            if operator == ConditionOperator.CONTAINS:
                return str(right) in str(left)

            if operator == ConditionOperator.STARTS_WITH:
                return str(left).startswith(str(right))

            if operator == ConditionOperator.ENDS_WITH:
                return str(left).endswith(str(right))

            msg = f"Unknown operator: {operator}"
            raise RuleEngineError(msg)

        except (TypeError, ValueError, AttributeError) as e:
            logger.warning("Operator evaluation error: %s (left=%s, operator=%s, right=%s)", e, left, operator, right)
            return False


class RuleEngine:
    """Движок для выполнения правил"""

    def __init__(self, variable_repository: VariableRepository | None = None) -> None:
        self.variable_repository = variable_repository
        self.condition_evaluator = ConditionEvaluator()

    async def evaluate_conditional_flag(
        self,
        flag: ConditionalFlag,
        context: ExecutionContext,
    ) -> Any:
        """
        Вычислить значение conditional flag через правила

        Args:
            flag: Описание флага с правилами
            context: Контекст выполнения

        Returns:
            Значение флага
        """
        sorted_rules = sorted(flag.rules, key=lambda r: r.priority, reverse=True)

        for rule in sorted_rules:
            if await self._check_rule(rule, context):
                return await self._resolve_value(rule.result, context)

        return await self._resolve_value(flag.default, context)

    async def _check_rule(
        self,
        rule: Rule,
        context: ExecutionContext,
    ) -> bool:
        """Проверить все условия правила (AND)"""

        for condition in rule.conditions:
            if not await self._check_condition(condition, context):
                return False

        return True

    async def _check_condition(
        self,
        condition: RuleCondition,
        context: ExecutionContext,
    ) -> bool:
        """Проверить одно условие"""

        var_value = await self._get_variable_value(condition.variable, context)

        compare_value = await self._resolve_value(condition.value, context)

        return ConditionEvaluator._apply_operator(var_value, condition.operator, compare_value)

    async def _get_variable_value(
        self,
        var_name: str,
        context: ExecutionContext,
    ) -> Any:
        """Получить значение переменной"""

        if not self.variable_repository:
            msg = "VariableRepository not configured"
            raise RuleEngineError(msg)

        var_schema = None
        for v in (
            context.get_from_event("ui_router_schema").variables if context.get_from_event("ui_router_schema") else []
        ):
            if v.name == var_name:
                var_schema = v
                break

        if var_schema:
            value = await self.variable_repository.get(
                bot_id=context.bot.id,
                name=var_name,
                scope=var_schema.scope,
                user_id=context.user_id if var_schema.scope == VariableScope.USER else None,
                chat_id=context.chat_id if var_schema.scope == VariableScope.CHAT else None,
            )

            if value is None and var_schema.default is not None:
                return var_schema.default

            return value

        value = await self.variable_repository.get(
            bot_id=context.bot.id,
            name=var_name,
            scope=VariableScope.USER,
            user_id=context.user_id,
        )

        if value is not None:
            return value

        return await self.variable_repository.get(
            bot_id=context.bot.id,
            name=var_name,
            scope=VariableScope.BOT,
        )

    async def _resolve_value(
        self,
        value: Any,
        context: ExecutionContext,
    ) -> Any:
        """
        Разрешить значение (может быть ссылка на переменную)

        Если value = "{variable_name}" - получаем из репозитория
        Иначе возвращаем как есть
        """
        if isinstance(value, str) and value.startswith("{") and value.endswith("}"):
            var_name = value[1:-1]
            return await self._get_variable_value(var_name, context)

        return value
