"""统一常量表达式求值器

整合了原先分散在 5 处的常量求值逻辑：
  - SymbolTable._eval_const_expr
  - CTypeInfo.TryEvalConstExpr
  - ConstEvalMixin._try_eval_const_expr
  - HandlesAssign._eval_const_expr
  - HandlesIf._eval_const_expr

ConstEvaluator 提供三个级别的求值：
  1. eval_basic(node)        — 纯 AST 常量 + 算术（无外部依赖）
  2. eval_with_symtab(node)  — + SymbolTable 中的 define 查找
  3. eval_full(node, ctx)    — + Gen._define_constants / _all_define_constants / 平台宏
"""
from __future__ import annotations

import ast
from typing import Any, Callable, Optional, TYPE_CHECKING

import llvmlite.ir as ir

from lib.core.Handles.HandlesBase import CTypeInfo

if TYPE_CHECKING:
    from lib.core.LlvmCodeGenerator import LlvmCodeGenerator


class ConstEvaluator:
    """统一常量表达式求值器"""

    # ==================================================================
    #  Level 2: + SymbolTable define 查找
    # ==================================================================

    @staticmethod
    def eval_with_symtab(node: ast.AST, symtab: Any) -> Optional[Any]:
        """求值常量表达式，支持从 SymbolTable 查找 define 值。

        支持: Constant, BinOp, UnaryOp, Name(define), Attribute(define)
        """
        if isinstance(node, ast.Constant):
            return node.value
        if isinstance(node, ast.Name):
            if hasattr(symtab, '__contains__') and node.id in symtab:
                info: CTypeInfo = symtab[node.id]
                if info.IsDefine and isinstance(info.DefineValue, int):
                    return info.DefineValue
            return None
        if isinstance(node, ast.Attribute):
            # 先尝试 t.CSizeT().Size 模式（类型构造调用 + .Size 属性）
            size_val = ConstEvaluator._eval_ctype_size_attr(node)
            if size_val is not None:
                return size_val
            return ConstEvaluator._eval_attribute_define(node, symtab)
        return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_with_symtab(n, symtab))

    # ==================================================================
    #  Level 3: + Gen._define_constants / _all_define_constants / 平台宏
    # ==================================================================

    @staticmethod
    def eval_full(node: ast.AST, ctx: 'EvalContext') -> Optional[Any]:
        """完整求值，支持 define_constants、平台宏、BoolOp、Compare。

        ctx: EvalContext 实例，封装了 Gen、SymbolTable 等上下文。
        """
        # BoolOp (HandlesIf 逻辑)
        if isinstance(node, ast.BoolOp):
            if isinstance(node.op, ast.And):
                for val in node.values:
                    result: Any = ConstEvaluator.eval_full(val, ctx)
                    if result is None:
                        return None
                    if not result:
                        return 0
                return 1
            elif isinstance(node.op, ast.Or):
                for val in node.values:
                    result: Any = ConstEvaluator.eval_full(val, ctx)
                    if result is None:
                        return None
                    if result:
                        return 1
                return 0
            return None

        # Compare (HandlesIf 逻辑)
        if isinstance(node, ast.Compare):
            left: Any = ConstEvaluator.eval_full(node.left, ctx)
            if left is None:
                return None
            op: ast.cmpop
            comparator: ast.AST
            for op, comparator in zip(node.ops, node.comparators):
                right: Any = ConstEvaluator.eval_full(comparator, ctx)
                if right is None:
                    return None
                result: bool | None = ConstEvaluator._eval_compare_op(op, left, right)
                if result is None:
                    return None
                if not result:
                    return 0
                left = right
            return 1

        # Constant — 直接返回原始值，bool→0/1 转换由 HandlesIf 自行处理
        if isinstance(node, ast.Constant):
            return node.value

        # Name — 查 define_constants / SymbolTable / 平台宏
        if isinstance(node, ast.Name):
            return ctx.lookup_name(node.id)

        # Attribute — 查 define_constants / SymbolTable / 平台宏
        if isinstance(node, ast.Attribute):
            # 先尝试 t.CSizeT().Size 模式（类型构造调用 + .Size 属性）
            size_val = ConstEvaluator._eval_ctype_size_attr(node)
            if size_val is not None:
                return size_val
            return ctx.lookup_attribute(node)

        # UnaryOp
        if isinstance(node, ast.UnaryOp):
            operand: Any = ConstEvaluator.eval_full(node.operand, ctx)
            if operand is None:
                return None
            if isinstance(node.op, ast.Not):
                return 1 if not operand else 0
            if isinstance(node.op, ast.USub):
                return -operand
            if isinstance(node.op, ast.UAdd):
                return +operand
            if isinstance(node.op, ast.Invert):
                return ~operand
            return None

        # Call — 编译时函数调用（如 ctraits.isptr(x)）
        if isinstance(node, ast.Call):
            return ConstEvaluator._eval_compile_time_call(node, ctx)

        # BinOp
        return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_full(n, ctx))

    # ==================================================================
    #  内部辅助
    # ==================================================================

    @staticmethod
    def _eval_compile_time_call(node: ast.Call, ctx: 'EvalContext') -> Optional[Any]:
        """处理编译时函数调用，如 ctraits.isptr(x)"""
        func: ast.expr = node.func
        func_name: str | None = None
        module_name: str | None = None

        if isinstance(func, ast.Attribute):
            if isinstance(func.value, ast.Name):
                module_name = func.value.id
                func_name = func.attr
        elif isinstance(func, ast.Name):
            func_name = func.id

        # ctraits.isptr(x) — 带模块前缀
        # isptr(x) — 通过 from ctraits import isptr 导入
        is_isptr: bool = False
        if module_name == 'ctraits' and func_name == 'isptr':
            is_isptr = True
        elif func_name == 'isptr' and not module_name:
            # 检查是否通过 from ctraits import isptr 导入
            t_c_imported = getattr(ctx.translator, '_t_c_imported_names', {}) if ctx.translator else {}
            if func_name in t_c_imported:
                src_module, _ = t_c_imported[func_name]
                if src_module == 'ctraits':
                    is_isptr = True
            # 也检查符号表中是否有 ctraits.isptr
            if not is_isptr and ctx.symtab:
                sym = ctx.symtab.lookup('ctraits.isptr') if hasattr(ctx.symtab, 'lookup') else None
                if sym:
                    is_isptr = True

        if is_isptr and node.args and ctx.translator:
            try:
                arg_node: ast.expr = node.args[0]
                # 局部变量：优先通过 LLVM 类型判断（GetCTypeInfo 无法解析局部变量名）
                if ctx.Gen and isinstance(arg_node, ast.Name):
                    var_name: str = arg_node.id
                    variables: dict = getattr(ctx.Gen, 'variables', {})
                    if var_name in variables:
                        var_val: Any = variables[var_name]
                        if hasattr(var_val, 'type') and isinstance(var_val.type, ir.PointerType):
                            pointee: ir.Type = var_val.type.pointee
                            if isinstance(pointee, ir.PointerType):
                                return 1
                            return 0
                # 类型名 / typedef：通过 TypeMergeHandler 获取 CTypeInfo
                type_info = ctx.translator.TypeMergeHandler.GetCTypeInfo(arg_node)
                if type_info and (type_info.IsPtr or type_info.PtrCount > 0):
                    return 1
                if type_info and not (type_info.IsPtr or type_info.PtrCount > 0):
                    return 0
                return None
            except Exception:
                return None
        return None

    @staticmethod
    def _eval_arith(node: ast.AST, recurse: Callable[[ast.AST], Any]) -> Optional[Any]:
        """通用算术求值：BinOp + UnaryOp"""
        if isinstance(node, ast.BinOp):
            left: Any = recurse(node.left)
            right: Any = recurse(node.right)
            if left is None or right is None:
                return None
            return ConstEvaluator._apply_binop(node.op, left, right)
        if isinstance(node, ast.UnaryOp):
            operand: Any = recurse(node.operand)
            if operand is None:
                return None
            if isinstance(node.op, ast.USub):
                return -operand
            if isinstance(node.op, ast.UAdd):
                return +operand
            if isinstance(node.op, ast.Invert):
                return ~operand
            return None
        return None

    @staticmethod
    def _apply_binop(op: ast.operator, left: Any, right: Any) -> Optional[Any]:
        """应用二元运算符"""
        try:
            if isinstance(op, ast.Add):
                return left + right
            elif isinstance(op, ast.Sub):
                return left - right
            elif isinstance(op, ast.Mult):
                return left * right
            elif isinstance(op, ast.Div):
                return left // right if isinstance(left, int) and isinstance(right, int) else left / right
            elif isinstance(op, ast.FloorDiv):
                if isinstance(right, (int, float)) and right == 0:
                    return None
                return left // right
            elif isinstance(op, ast.Mod):
                if isinstance(right, (int, float)) and right == 0:
                    return None
                return left % right
            elif isinstance(op, ast.Pow):
                return left ** right
            elif isinstance(op, ast.LShift):
                return left << right
            elif isinstance(op, ast.RShift):
                return left >> right
            elif isinstance(op, ast.BitOr):
                return left | right
            elif isinstance(op, ast.BitXor):
                return left ^ right
            elif isinstance(op, ast.BitAnd):
                return left & right
        except Exception:
            return None
        return None

    @staticmethod
    def _eval_compare_op(op: ast.cmpop, left: Any, right: Any) -> Optional[bool]:
        """应用比较运算符"""
        if isinstance(op, ast.Eq):
            return left == right
        elif isinstance(op, ast.NotEq):
            return left != right
        elif isinstance(op, ast.Lt):
            return left < right
        elif isinstance(op, ast.LtE):
            return left <= right
        elif isinstance(op, ast.Gt):
            return left > right
        elif isinstance(op, ast.GtE):
            return left >= right
        return None

    @staticmethod
    def _eval_ctype_size_attr(node: ast.Attribute) -> Optional[int]:
        """求值 t.CSizeT().Size 等类型构造调用 + .Size 属性模式。

        匹配 AST 形态: Attribute(attr='Size', value=Call(func=Attribute(value=Name(id='t'), attr='CSizeT')))
        或直接 Call(func=Name(id='CSizeT'))（from t import CSizeT 后的使用方式）。
        返回类型大小（位数），如 CSizeT().Size 返回 64（64 位平台）。
        """
        if node.attr != 'Size':
            return None
        if not isinstance(node.value, ast.Call):
            return None
        call_node: ast.Call = node.value
        if call_node.args or call_node.keywords:
            return None
        func: ast.expr = call_node.func
        type_name: str | None = None
        if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name) and func.value.id == 't':
            type_name = func.attr
        elif isinstance(func, ast.Name):
            type_name = func.id
        if not type_name:
            return None
        # 延迟导入避免循环依赖
        try:
            from lib.includes.t import CTypeRegistry
        except Exception:
            return None
        cls = CTypeRegistry.GetClassByName(type_name)
        if cls is None:
            return None
        try:
            instance = cls()
            size_val = getattr(instance, 'Size', None)
            if isinstance(size_val, int):
                return size_val
        except Exception:
            pass
        return None

    @staticmethod
    def _eval_attribute_define(node: ast.Attribute, symtab: Any) -> Optional[Any]:
        """从 SymbolTable 查找 Attribute 形式的 define 值"""
        parts: list[str] = []
        current: ast.AST = node
        while isinstance(current, ast.Attribute):
            parts.append(current.attr)
            current = current.value
        if isinstance(current, ast.Name):
            parts.append(current.id)
        parts.reverse()
        attr_name: str = parts[-1]
        possible_keys: list[str] = [attr_name, '.'.join(parts)]
        for key in possible_keys:
            if hasattr(symtab, '__contains__') and key in symtab:
                info: CTypeInfo = symtab[key]
                if info.IsDefine and isinstance(info.DefineValue, int):
                    return info.DefineValue
        # 模糊匹配
        if len(parts) >= 2:
            mod_key: str
            mod_info: CTypeInfo
            for mod_key, mod_info in (symtab.items() if hasattr(symtab, 'items') else []):
                if mod_info.IsDefine and isinstance(mod_info.DefineValue, int):
                    if mod_key.endswith('.' + attr_name) or mod_key == attr_name:
                        return mod_info.DefineValue
        return None

    @staticmethod
    def _intify(val: Any) -> Any:
        """float → int 转换（仅当 float 值恰好是整数时）"""
        if isinstance(val, float) and val == int(val):
            return int(val)
        return val


class EvalContext:
    """常量求值上下文，封装 Gen / SymbolTable / 平台宏查找逻辑。

    由调用方构造并传入 ConstEvaluator.eval_full()。
    """

    def __init__(self, Gen: LlvmCodeGenerator | None = None, symtab: Any = None, platform_macros: dict | None = None, translator: Any = None):
        self.Gen: LlvmCodeGenerator | None = Gen
        self.symtab: Any = symtab
        self._platform_macros: dict | None = platform_macros
        self.translator: Any = translator

    def lookup_name(self, name: str) -> Optional[Any]:
        """查找 Name 节点的值：define_constants → SymbolTable → 平台宏"""
        # 1. Gen._define_constants
        if self.Gen and name in self.Gen._define_constants:
            val: Any = self.Gen._define_constants[name]
            if isinstance(val, (int, float)):
                return ConstEvaluator._intify(val)

        # 2. Gen._DefineConstants (大写版本)
        if self.Gen and hasattr(self.Gen, '_DefineConstants') and name in self.Gen._DefineConstants:
            val: Any = self.Gen._DefineConstants[name]
            if isinstance(val, (int, float)):
                return ConstEvaluator._intify(val)

        # 3. SymbolTable
        if self.symtab and name in self.symtab:
            info: CTypeInfo = self.symtab[name]
            val: Any = info.value
            if isinstance(val, (int, float)):
                return ConstEvaluator._intify(val)
            if info.IsDefine and isinstance(info.DefineValue, (int, float)):
                return ConstEvaluator._intify(info.DefineValue)

        # 4. 平台宏
        macros: dict = self._get_platform_macros()
        if name in macros:
            return macros[name]

        return None

    def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]:
        """查找 Attribute 节点的值"""
        module_name: str | None = node.value.id if isinstance(node.value, ast.Name) else None
        attr_name: str = node.attr

        if module_name and attr_name:
            combined_key: str = f"{module_name}.{attr_name}"

            # 1. Gen._define_constants
            if self.Gen and combined_key in self.Gen._define_constants:
                val: Any = self.Gen._define_constants[combined_key]
                if isinstance(val, (int, float)):
                    return ConstEvaluator._intify(val)

            # 2. SymbolTable (精确匹配 + 后缀匹配)
            if self.symtab:
                key: str
                info: CTypeInfo
                for key, info in self.symtab.items():
                    if key.endswith(f".{combined_key}") or key == combined_key:
                        if info.IsDefine and isinstance(info.DefineValue, (int, float)):
                            return ConstEvaluator._intify(info.DefineValue)

            # 3. _all_define_constants
            all_dc: dict = self._get_all_define_constants()
            if combined_key in all_dc:
                val: Any = all_dc[combined_key]
                if isinstance(val, (int, float)):
                    return ConstEvaluator._intify(val)
            if attr_name in all_dc:
                val: Any = all_dc[attr_name]
                if isinstance(val, (int, float)):
                    return ConstEvaluator._intify(val)
            dc_key: str
            dc_val: Any
            for dc_key, dc_val in all_dc.items():
                if dc_key == attr_name or dc_key.endswith(f".{attr_name}"):
                    if isinstance(dc_val, (int, float)):
                        return ConstEvaluator._intify(dc_val)

            # 4. SymbolTable 短名查找
            if self.symtab and attr_name in self.symtab:
                info: CTypeInfo = self.symtab[attr_name]
                if info.IsDefine and isinstance(info.DefineValue, (int, float)):
                    return ConstEvaluator._intify(info.DefineValue)

            # 5. Gen._define_constants 短名
            if self.Gen and attr_name in self.Gen._define_constants:
                val: Any = self.Gen._define_constants[attr_name]
                if isinstance(val, (int, float)):
                    return ConstEvaluator._intify(val)

            # 6. 平台宏
            macros: dict = self._get_platform_macros()
            full_key: str | None = self._get_attr_full_name(node)
            if full_key:
                if full_key in macros:
                    return macros[full_key]
                short_name: str = full_key.split('.')[-1] if '.' in full_key else full_key
                if short_name in macros:
                    return macros[short_name]

        return None

    def _get_all_define_constants(self) -> dict:
        """获取 _all_define_constants"""
        if self.Gen and hasattr(self.Gen, '_all_define_constants'):
            return self.Gen._all_define_constants
        return {}

    def _get_platform_macros(self) -> dict:
        """获取平台宏"""
        if self._platform_macros is not None:
            return self._platform_macros
        if not self.Gen:
            return {}
        macros: dict[str, int] = {}
        pi: dict = self.Gen._platform_info
        ptr_size: int = self.Gen.ptr_size
        if pi.get('is_windows'):
            macros['_WIN32'] = 1
            if not pi.get('is_32bit'):
                macros['_WIN64'] = 1
                macros['WIN64'] = 1
            macros['WIN32'] = 1
        if pi.get('is_linux'):
            macros['__linux__'] = 1
        if pi.get('is_macos'):
            macros['__APPLE__'] = 1
            macros['__MACH__'] = 1
        if pi.get('is_x86_64'):
            macros['__x86_64__'] = 1
            macros['__x86_64'] = 1
            macros['_M_X64'] = 1
        elif pi.get('is_arm64'):
            macros['__aarch64__'] = 1
            macros['_M_ARM64'] = 1
        if not pi.get('is_32bit') and pi.get('is_linux'):
            macros['__LP64__'] = 1
        elif pi.get('is_32bit'):
            macros['_ILP32'] = 1
        macros['SIZEOF_VOID_P'] = ptr_size
        macros['__SIZEOF_POINTER__'] = ptr_size
        macros['NDEBUG'] = 0
        return macros

    @staticmethod
    def _get_attr_full_name(node: ast.Attribute) -> Optional[str]:
        """从 Attribute 节点提取完整名称 (e.g. 'module.name')"""
        parts: list[str] = []
        current: ast.AST = node
        while isinstance(current, ast.Attribute):
            parts.append(current.attr)
            current = current.value
        if isinstance(current, ast.Name):
            parts.append(current.id)
        parts.reverse()
        return '.'.join(parts) if parts else None
