import contextlib
from typing import Any, Dict, Set, Tuple


class VariableTracker:
    def __init__(self):
        self.previous_vars: Dict[str, Any] = {}
        self.new_vars: Set[str] = set()
        self.modified_vars: Set[str] = set()

    def update(self, current_vars: Dict[str, Any]):
        """ update var """
        self.new_vars.clear()
        self.modified_vars.clear()

        for name, value in current_vars.items():
            if name not in self.previous_vars:
                self.new_vars.add(name)
            elif self.previous_vars[name] != value:
                self.modified_vars.add(name)
            self.previous_vars[name] = value

        # 检查删除的变量
        deleted_vars = set(self.previous_vars.keys()) - set(current_vars.keys())
        for var in deleted_vars:
            del self.previous_vars[var]

    def get_new_vars(self) -> Dict[str, Any]:
        return {name: self.previous_vars[name] for name in self.new_vars}

    def get_modified_vars(self) -> Dict[str, Any]:
        return {name: self.previous_vars[name] for name in self.modified_vars}


class ExpressionEvaluator:
    def __init__(self, watch_expressions: Tuple[str, ...]):
        self.expressions = watch_expressions
        self.previous_values: Dict[str, Any] = {}

    def evaluate_all(
        self,
        globals_dict: Dict[str, Any],
        locals_dict: Dict[str, Any]
    ) -> Dict[str, Tuple[bool, Any]]:
        results = {}

        for expr in self.expressions:
            try:
                value = eval(expr, globals_dict, locals_dict)
                changed = expr not in self.previous_values or self.previous_values[expr] != value
                self.previous_values[expr] = value
                results[expr] = (changed, value)
            except Exception as e:
                error_msg = f"Error: {type(e).__name__}"
                results[expr] = (True, error_msg)

        return results


class WatchExploder:
    def __init__(self, explode_expressions: Tuple[str, ...]):
        self.expressions = explode_expressions

    def explode(
        self,
        globals_dict: Dict[str, Any],
        locals_dict: Dict[str, Any]
    ) -> Dict[str, Any]:
        results = {}

        for expr in self.expressions:
            with contextlib.suppress(Exception):
                obj = eval(expr, globals_dict, locals_dict)
                if hasattr(obj, '__dict__'):
                    for key, value in obj.__dict__.items():
                        results[f"{expr}.{key}"] = value

                elif isinstance(obj, dict):
                    for key, value in obj.items():
                        results[f"{expr}[{key!r}]"] = value

                elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
                    for idx, value in enumerate(obj):
                        results[f"{expr}[{idx}]"] = value

        return results
