from __future__ import annotations

import sys
import os
import ast
import shlex
import datetime
import tempfile
import subprocess
from typing import Any
from lib.includes import c
from lib.includes import t
from lib.constants.config import (
    DEFAULT_INPUT_FILE, DEFAULT_OUTPUT_FILE,
    DEFAULT_COMPILE_COMMAND,
    DEFAULT_COMPILE_FLAGS,
    HELP_MESSAGE, DEFAULT_METADATA
)
from lib.utils.helpers import (
    DetectFileType, ExecuteCommand,
    AppendFileContent
)
from lib.core.VLogger import info, warning, error, success, debug
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import CTypeInfo
from lib.Shell.Serializer import SerializeSymbolTable, SymbolFile
from lib.Shell.Config import Config


class TransPyC:
    """TransPyC 主类"""

    def __init__(self, code: str | None = None, debug: bool = False, triple: str | None = None, datalayout: str | None = None) -> None:
        """初始化TransPyC对象

        Args:
            code: 代码字符串
            debug: 调试模式
            triple: LLVM target triple (e.g. "x86_64-pc-linux-gnu")
            datalayout: LLVM data layout string
        """
        self.Args: dict[str, Any] = {}
        self.HeaderFiles: list[str] = []
        self.HelperFiles: list[str] = []
        self.AnnotationFiles: list[str] = []
        self.translator: Translator = Translator()
        self.code: str | None = code
        self.translator.Content = code
        self.config: Config = Config()
        self.translator.config = self.config
        self.config.debug = debug
        self.SymbolFiles: list[SymbolFile] = []
        self.SliceLevel: int | str = 3
        self.SliceCount: int = 0
        self.SliceInfos: list[Any] = []
        self.metadata: dict[str, Any] = DEFAULT_METADATA.copy()
        self.triple: str | None = triple
        self.datalayout: str | None = datalayout
        self._source_module_sig_files: dict[str, Any] = {}

    @staticmethod
    def PreProcessSymbol(SymbolFile: SymbolFile, debug: bool = False) -> bytes | tuple[bytes, str]:
        """预处理符号文件，提取符号表并返回二进制数据

        Args:
            SymbolFile: SymbolFile对象，包含文件路径和类型信息
            debug: 是否启用调试模式，如果为True，返回 (bytes, DebugInfo)
                  对于Python文件，DebugInfo包含p2c日志
                  对于C文件，DebugInfo包含基本处理信息

        Returns:
            如果debug为False: bytes - 序列化后的符号表二进制数据
            如果debug为True: (bytes, str) - (二进制数据, 调试日志)
        """
        translator: Translator = Translator()
        DebugLogs: list[str] | str = []

        try:
            # 读取文件内容
            if SymbolFile.FilePath:
                with open(SymbolFile.FilePath, 'r', encoding=SymbolFile.encoding) as f:
                    content: str = f.read()
            elif SymbolFile.CodeString:
                content = SymbolFile.CodeString
            else:
                raise ValueError("SymbolFile must have either FilePath or CodeString")

            # 根据文件类型处理
            FileType: str | None = SymbolFile.FileType
            if not FileType and SymbolFile.FilePath:
                # 从文件路径推断类型
                if SymbolFile.FilePath.endswith('.vp') or SymbolFile.FilePath.endswith('.py'):
                    FileType = 'py'
                elif SymbolFile.FilePath.endswith('.c') or SymbolFile.FilePath.endswith('.h'):
                    FileType = 'c'

            if FileType == 'py':
                # Python文件处理
                translator.OriginalLines = content.split('\n')
                translator.Content = content

                # 设置调试文件（如果需要）
                if debug:
                    DebugFile: str = SymbolFile.FilePath.replace('.py', '.p2c') if SymbolFile.FilePath else 'debug.p2c'
                    translator.SetDebugFile(DebugFile)
                    # 清空调试文件
                    with open(DebugFile, 'w', encoding=SymbolFile.encoding) as f:
                        f.write('')

                # 解析AST并提取符号
                tree: ast.Module = ast.parse(content)

                if debug:
                    with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f:
                        f.write('=== AST Tree (Compact) ===\n')
                        f.write(ast.dump(tree))
                        f.write('\n\n')

                # 遍历AST提取符号
                for node in ast.iter_child_nodes(tree):
                    if isinstance(node, ast.ClassDef):
                        translator.SymbolTable[node.name] = {'type': 'struct', 'members': {}}
                        for item in node.body:
                            if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
                                VarName: str = item.target.id
                                TypeInfo: CTypeInfo | None = translator.TypeMergeHandler.GetCTypeInfo(item.annotation) if item.annotation else None
                                VarType: str = TypeInfo.ToString() if TypeInfo else 'unknown'
                                IsPtr: bool = TypeInfo.IsPtr if TypeInfo else False
                                translator.SymbolTable[node.name]['members'][VarName] = {
                                    'type': VarType,
                                    'IsPtr': IsPtr
                                }
                    elif isinstance(node, ast.FunctionDef):
                        translator.SymbolTable[node.name] = {'type': 'function'}
                    elif isinstance(node, ast.AnnAssign):
                        if isinstance(node.target, ast.Name):
                            VarName: str = node.target.id
                            VarType: str = 'unknown'
                            IsPtr: bool = False
                            if node.annotation:
                                try:
                                    TypeInfo: CTypeInfo | None = translator.TypeMergeHandler.GetCTypeInfo(node.annotation)
                                    VarType = TypeInfo.ToString() if TypeInfo else 'unknown'
                                    IsPtr = TypeInfo.IsPtr if TypeInfo else False
                                except:
                                    pass
                            translator.SymbolTable[VarName] = {
                                'type': 'variable',
                                'declared_type': VarType,
                                'IsPtr': IsPtr
                            }

                if debug:
                    with open(DebugFile, 'a', encoding=SymbolFile.encoding) as f:
                        f.write('=== Symbol Table ===\n')
                        f.write(str(translator.SymbolTable))
                        f.write('\n\n')
                    # 读取调试日志
                    with open(DebugFile, 'r', encoding=SymbolFile.encoding) as f:
                        DebugLogs = f.read()

            elif FileType == 'c':
                # C文件处理 - 使用现有的ParseHelperFiles逻辑
                translator.ParseHelperFiles([SymbolFile.FilePath], SymbolFile.encoding)

                if debug:
                    DebugLogs = f"=== C File Symbol Extraction ===\n"
                    DebugLogs += f"File: {SymbolFile.FilePath}\n"
                    DebugLogs += f"=== Symbol Table ===\n"
                    DebugLogs += str(translator.SymbolTable)
                    DebugLogs += "\n\n"
            else:
                raise ValueError(f"Unsupported file type: {FileType}")

            # 序列化符号表
            BinaryData: bytes = SerializeSymbolTable(translator.SymbolTable)

            if debug:
                return BinaryData, DebugLogs
            return BinaryData

        except Exception as e:
            error_msg: str = f"Error processing symbol file: {e}"
            if debug:
                return b'', error_msg
            raise RuntimeError(error_msg)

    def ParseArgs(self) -> None:
        """解析命令行参数"""
        I: int = 1
        while I < len(sys.argv):
            if sys.argv[I] == '-f':
                if I + 1 < len(sys.argv):
                    self.Args['Input'] = sys.argv[I + 1]
                    I += 2
                else:
                    error('-f requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '-o':
                if I + 1 < len(sys.argv):
                    self.Args['Output'] = sys.argv[I + 1]
                    I += 2
                else:
                    error('-o requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '-wh':
                if I + 1 < len(sys.argv):
                    self.HeaderFiles = sys.argv[I + 1:]
                    break
                else:
                    error('-wh requires arguments')
                    sys.exit(1)
            elif sys.argv[I] == '-debug':
                if I + 1 < len(sys.argv):
                    self.Args['Debug'] = sys.argv[I + 1]
                    I += 2
                else:
                    error('-debug requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '-cc':
                # 编译命令
                if I + 1 < len(sys.argv):
                    self.Args['CompileCommand'] = sys.argv[I + 1]
                    I += 2
                else:
                    error('-cc requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '-cflags':
                # 编译标志
                if I + 1 < len(sys.argv):
                    self.Args['CompileFlags'] = sys.argv[I + 1]
                    I += 2
                else:
                    error('-cflags requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '-run':
                # 是否运行生成的程序
                self.Args['Run'] = True
                I += 1
            elif sys.argv[I] == '-args':
                # 运行时参数
                if I + 1 < len(sys.argv):
                    self.Args['RunArgs'] = sys.argv[I + 1]
                    I += 2
                else:
                    error('-args requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '-level' or sys.argv[I] == '-l':
                if I + 1 < len(sys.argv):
                    LevelArg: str = sys.argv[I + 1]
                    if LevelArg.isdigit():
                        self.Args['SliceLevel'] = int(LevelArg)
                    elif LevelArg == 's':
                        self.Args['SliceLevel'] = 's'
                    else:
                        self.Args['SliceLevel'] = 3
                    I += 1
                else:
                    self.Args['SliceLevel'] = 3
            elif sys.argv[I] == '-h':
                # 辅助文件，用于解析符号信息
                if I + 1 < len(sys.argv):
                    # 收集所有后续参数作为辅助文件，直到遇到下一个以-开头的参数
                    while I + 1 < len(sys.argv) and not sys.argv[I + 1].startswith('-'):
                        self.HelperFiles.append(sys.argv[I + 1])
                        I += 1
                    I += 1
                else:
                    error('-h requires arguments')
                    sys.exit(1)
            elif sys.argv[I] == '-presym':
                # 预处理符号文件，生成.symbin文件
                # 格式: -presym <InputFile> -o <OutputFile> [-debug <DebugFile>]
                if I + 1 < len(sys.argv):
                    InputFile: str = sys.argv[I + 1]
                    I += 2

                    # 解析可选参数
                    OutputFile: str | None = None
                    DebugFile: str | None = None
                    while I < len(sys.argv) and sys.argv[I].startswith('-'):
                        if sys.argv[I] == '-o':
                            if I + 1 < len(sys.argv):
                                OutputFile = sys.argv[I + 1]
                                I += 2
                            else:
                                error('-o requires an argument')
                                sys.exit(1)
                        elif sys.argv[I] == '-debug':
                            if I + 1 < len(sys.argv):
                                DebugFile = sys.argv[I + 1]
                                I += 2
                            else:
                                error('-debug requires an argument')
                                sys.exit(1)
                        else:
                            break

                    # 如果没有指定输出文件，使用默认名称
                    if not OutputFile:
                        OutputFile = InputFile.replace('.vp', '.symbin').replace('.py', '.symbin').replace('.c', '.symbin')

                    # 执行预处理
                    self.Args['PreSym'] = {
                        'input': InputFile,
                        'output': OutputFile,
                        'debug': DebugFile
                    }
                else:
                    error('-presym requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '-e' or sys.argv[I] == '--encoding':
                if I + 1 < len(sys.argv):
                    self.Args['Encoding'] = sys.argv[I + 1]
                    I += 2
                else:
                    error('-e/--encoding requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '--triple':
                if I + 1 < len(sys.argv):
                    self.triple = sys.argv[I + 1]
                    I += 2
                else:
                    error('--triple requires an argument')
                    sys.exit(1)
            elif sys.argv[I] == '--datalayout':
                if I + 1 < len(sys.argv):
                    self.datalayout = sys.argv[I + 1]
                    I += 2
                else:
                    error('--datalayout requires an argument')
                    sys.exit(1)
            else:
                error(f'Unknown argument {sys.argv[I]}')
                sys.exit(1)

        # 检查是否有预处理符号文件的请求，如果有则跳过输入输出检查
        if 'PreSym' not in self.Args and ('Input' not in self.Args or 'Output' not in self.Args):
            error('缺少参数')
            info(HELP_MESSAGE)
            sys.exit(1)

    def CompileAndRun(self, OutputFile: str) -> None:
        """编译并运行生成的C代码"""
        # 获取编译命令
        CompileCmd: str = self.Args.get('CompileCommand', DEFAULT_COMPILE_COMMAND)
        CompileFlags: str = self.Args.get('CompileFlags', DEFAULT_COMPILE_FLAGS)

        # 构建编译命令（使用列表形式，避免 shell 注入）
        FullCompileCmd: list[str] = shlex.split(CompileCmd) + shlex.split(CompileFlags) + [OutputFile, '-o', f'{OutputFile}.exe']
        info(f'编译命令: {" ".join(FullCompileCmd)}')

        # 执行编译命令
        try:
            CompileResult: subprocess.CompletedProcess[str] | None = ExecuteCommand(FullCompileCmd)
            if CompileResult:
                success('编译成功!')

                # 检查是否需要运行
                if self.Args.get('Run', False):
                    RunArgs: str = self.Args.get('RunArgs', '')
                    RunCmd: list[str] = [f'{OutputFile}.exe'] + (shlex.split(RunArgs) if RunArgs else [])
                    info(f'运行: {" ".join(RunCmd)}')

                    # 执行运行命令
                    RunResult: subprocess.CompletedProcess[str] | None = ExecuteCommand(RunCmd)
                    if RunResult:
                        info('执行输出:')
                        info(RunResult.stdout)
                        if RunResult.stderr:
                            warning('执行错误:')
                            warning(RunResult.stderr)
        except Exception as e:
            error(f'编译失败: {e}')

    def WriteDebugInfo(self, content: str) -> None:
        """写入调试信息到指定文件"""
        # 获取调试文件路径
        if 'Debug' in self.Args:
            DebugFile: str = self.Args['Debug']
        else:
            DebugFile: str = self.Args.get('Output', '').replace('.c', '.p2c')

        if DebugFile:
            AppendFileContent(DebugFile, content)

    def PythonToC(self, InputFile: str, OutputFile: str) -> None:
        """Python到C的转换"""
        # 获取编码参数
        encoding: str = self.Args.get('Encoding', 'utf-8')

        # 设置调试文件路径（使用 .p2c 扩展名）
        if 'Debug' in self.Args:
            DebugFile: str = self.Args['Debug']
        else:
            # 默认使用 .p2c 文件
            DebugFile: str = OutputFile.replace('.c', '.p2c')

        # 设置翻译器的调试文件
        self.translator.SetDebugFile(DebugFile)

        # 先清空调试文件
        with open(DebugFile, 'w', encoding=encoding) as f:
            f.write('')

        # 解析辅助文件，提取符号信息
        if self.HelperFiles:
            self.translator.ParseHelperFiles(self.HelperFiles, encoding)

        # 加载注解文件，提取类型定义
        if self.AnnotationFiles:
            self.translator.LoadAnnotationFiles(self.AnnotationFiles)

        # 获取编码参数
        encoding = self.Args.get('Encoding', 'utf-8')
        with open(InputFile, 'r', encoding=encoding) as F:
            Content: str = F.read()

        # 保存原始代码行
        self.translator.OriginalLines = Content.split('\n')
        self.translator.Content = Content

        # 解析Python代码为AST
        Tree: ast.Module = ast.parse(Content)

        # 解析文件以提取类型信息（用于 EmbeddedAssignments）
        self.translator.ParsePythonFile(InputFile, encoding)

        # 写入AST树信息（压缩格式）
        with open(DebugFile, 'a', encoding=encoding) as f:
            f.write('=== AST Tree (Compact) ===\n')
            f.write(ast.dump(Tree))
            f.write('\n\n')

        Target: str = 'llvm'
        CCode: str = self.translator.GenerateCCode(Tree, target=Target)

        timestamp: str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        filename: str = os.path.basename(OutputFile)

        encoding = self.Args.get('Encoding', 'utf-8')
        with open(OutputFile, 'w', encoding=encoding) as F:
            F.write(CCode)
            F.write('\n')

    def CToPython(self, InputFile: str, OutputFile: str, HeaderFiles: list[str]) -> None:
        """C到Python的转换（暂时禁用）"""
        warning("C到Python的转换功能暂时禁用，请使用Python到C的转换功能。")

    def AddSymbol(self, SymbolFiles: SymbolFile | list[SymbolFile]) -> None:
        """添加符号文件

        Args:
            SymbolFiles: 符号文件或符号文件列表
        """
        if isinstance(SymbolFiles, list):
            self.SymbolFiles.extend(SymbolFiles)
        else:
            self.SymbolFiles.append(SymbolFiles)

        # 收集文件路径和编码
        HelperFiles: list[str] = []
        encodings: list[str] = []
        for SymbolFile in self.SymbolFiles:
            FilePath: str | None = SymbolFile.FilePath
            encoding: str = SymbolFile.encoding
            if FilePath:
                HelperFiles.append(FilePath)
                encodings.append(encoding)

        # 解析辅助文件
        if HelperFiles:
            # 暂时使用第一个文件的编码
            encoding = encodings[0] if encodings else 'utf-8'
            self.translator.ParseHelperFiles(HelperFiles, encoding)

    def Convert(self, OutputFilename: str | None = None, SourceFilename: str | None = None, target: str = 'llvm') -> str | tuple[str, dict[str, Any]] | None:
        """转换代码

        Args:
            OutputFilename: 输出文件名（用于模板渲染）
            SourceFilename: 源文件路径（用于设置CurrentFile，影响导入模块的查找）
            target: 目标代码类型，仅支持 'llvm'

        Returns:
            如果debug是字符串，则返回生成的代码
            如果debug是True，则返回生成的代码和调试信息
        """
        if not self.code:
            error('未提供代码')
            return None

        from lib.core.Handles.HandlesImports import ImportHandle
        ImportHandle._struct_Load_cache.clear()

        # 如果提供了 SourceFilename，先设置 CurrentFile
        # 这样导入模块时会相对于源文件所在目录查找
        if SourceFilename:
            self.translator.CurrentFile = SourceFilename

        # 加载注解文件，提取类型定义
        if self.AnnotationFiles:
            self.translator.LoadAnnotationFiles(self.AnnotationFiles)

        # 保存原始代码行
        self.translator.OriginalLines = self.code.split('\n')

        # 解析Python代码为AST
        Tree: ast.Module = ast.parse(self.code)

        # 从源代码直接预扫描 import 语句，注册别名
        # 注意：不能依赖 Tree 中的 import 节点，因为 ParsePythonFile 可能修改了 AST
        if not getattr(self.translator, '_ImportedModules', None):
            self.translator._ImportedModules = set()
        _prescan_tree: ast.Module = ast.parse(self.code)
        for _node in ast.iter_child_nodes(_prescan_tree):
            if isinstance(_node, ast.Import):
                for _alias in _node.names:
                    _name: str = _alias.name
                    if _name in ('c', 't'):
                        continue
                    self.translator._ImportedModules.add(_name)
                    if _alias.asname:
                        self.translator.SymbolTable.import_aliases[_alias.asname] = _name
            elif isinstance(_node, ast.ImportFrom):
                _module_name: str | None = _node.module
                if _module_name and _module_name not in ('c', 't'):
                    self.translator._ImportedModules.add(_module_name)
                    for _alias in _node.names:
                        if _alias.asname:
                            self.translator.SymbolTable.import_aliases[_alias.asname] = f"{_module_name}.{_alias.name}"

        # 解析代码以提取类型信息（用于 EmbeddedAssignments 和 TypedefAssignments）
        # 创建临时文件路径用于解析
        with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f:
            f.write(self.code)
            TempFile: str = f.name
        try:
            # UpdateCurrentFile=False 表示不修改 CurrentFile
            if SourceFilename:
                self.translator.ParsePythonFile(TempFile, UpdateCurrentFile=False)
            else:
                self.translator.ParsePythonFile(TempFile)
        finally:
            os.unlink(TempFile)

        timestamp: str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        if self.triple:
            self.translator.triple = self.triple
        if self.datalayout:
            self.translator.datalayout = self.datalayout

        # 生成代码
        code: str = self.translator.GenerateCCode(Tree, target='llvm')
        filename: str = os.path.basename(OutputFilename) if OutputFilename else 'generated.ll'

        # 处理调试信息
        if isinstance(self.config.debug, str):
            # 如果debug是字符串，写入调试信息到文件
            with open(self.config.debug, 'w', encoding='utf-8') as f:
                f.write('=== Symbol Table ===\n')
                f.write(str(self.translator.SymbolTable) + '\n\n')
                f.write('=== AST Tree ===\n')
                f.write(ast.dump(Tree, indent=2) + '\n\n')
                f.write('=== Export Table ===\n')
                f.write(self.translator.Exportable.to_json() + '\n\n')
                f.write('=== Generated Code ===\n')
                f.write(code + '\n')
            return code
        elif self.config.debug:
            # 如果debug是True，返回生成的代码和调试信息
            DebugInfo: dict[str, Any] = {
                'SymbolTable': self.translator.SymbolTable,
                'ast_tree': ast.dump(Tree, indent=2),
                'Exportable': self.translator.Exportable.to_dict(),
                'generated_code': code
            }
            return code, DebugInfo
        else:
            # 如果debug是False，只返回生成的代码
            return code

    def Run(self) -> None:
        """主运行函数"""
        # 检查是否有命令行参数
        if len(sys.argv) > 1:
            # 有命令行参数，使用 ParseArgs 方法解析
            self.ParseArgs()

            # 检查是否有预处理符号文件的请求
            if 'PreSym' in self.Args:
                PresymConfig: dict[str, Any] = self.Args['PreSym']
                InputFile: str = PresymConfig['input']
                OutputFile: str = PresymConfig['output']
                DebugFile: str | None = PresymConfig['debug']

                # 获取编码参数
                encoding: str = self.Args.get('Encoding', 'utf-8')

                # 推断文件类型
                FileType: str | None = 'py' if (InputFile.endswith('.vp') or InputFile.endswith('.py')) else 'c' if InputFile.endswith('.c') else None

                # 创建 SymbolFile 对象，传入编码参数
                SymbolFile: SymbolFile = SymbolFile(file=InputFile, type=FileType, encoding=encoding)

                # 调用 PreProcessSymbol
                if DebugFile:
                    BinaryData: bytes
                    DebugInfo: str
                    BinaryData, DebugInfo = TransPyC.PreProcessSymbol(SymbolFile, debug=True)
                    # 写入调试文件
                    with open(DebugFile, 'w', encoding=encoding) as f:
                        f.write(DebugInfo)
                    info(f"调试信息写入: {DebugFile}")
                else:
                    BinaryData: bytes = TransPyC.PreProcessSymbol(SymbolFile, debug=False)

                # 写入二进制符号文件
                with open(OutputFile, 'wb') as f:
                    f.write(BinaryData)

                success(f"符号文件生成: {OutputFile}")
                return

            InputFile: str = self.Args['Input']
            OutputFile: str = self.Args['Output']
        else:
            # 没有命令行参数，使用默认的测试文件名称
            InputFile: str = DEFAULT_INPUT_FILE
            OutputFile: str = DEFAULT_OUTPUT_FILE
            info('使用默认测试文件: test.vp -> test.c')

        FileType: str = DetectFileType(InputFile)

        if FileType in ('.vp', '.py'):
            self.PythonToC(InputFile, OutputFile)
            # 检查是否需要编译和运行
            if 'CompileCommand' in self.Args or self.Args.get('Run', False):
                self.CompileAndRun(OutputFile)
        elif FileType == '.c':
            self.CToPython(InputFile, OutputFile, self.HeaderFiles)
        else:
            error(f'不支持的文件类型: {FileType}')
            sys.exit(1)
