#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Demo 项目清理器 - 安全版本
使用更精确的文本处理方式，避免破坏项目文件结构
"""

import sys
import os
import re
import shutil

class DemoProjectCleanerSafe:
    """Demo 项目清理器 - 安全版本"""
    
    def __init__(self, pbxproj_path):
        """
        初始化清理器
        
        参数:
            pbxproj_path (str): project.pbxproj 文件路径
        """
        self.pbxproj_path = pbxproj_path
        
        if not os.path.exists(pbxproj_path):
            raise FileNotFoundError(f"项目文件不存在: {pbxproj_path}")
    
    def clean_all_third_party_files(self):
        """
        清理所有第三方文件
        """
        print("🧹 开始清理所有第三方文件...")
        
        try:
            # 创建备份
            backup_path = f"{self.pbxproj_path}.backup_safe"
            shutil.copy2(self.pbxproj_path, backup_path)
            print(f"✅ 已创建安全备份: {backup_path}")
            
            # 读取文件内容
            with open(self.pbxproj_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            original_content = content
            
            # 1. 删除 OSFrameworks/CNFrameworks 相关的 PBXFileSystemSynchronizedRootGroup
            content = self._remove_file_system_synchronized_groups(content)
            
            # 2. 删除主组中的引用
            content = self._remove_from_main_group(content)
            
            # 3. 删除 fileSystemSynchronizedGroups 中的引用
            content = self._remove_from_file_system_synchronized_groups(content)
            
            # 4. 删除所有 .a、.framework、.bundle 文件引用
            content = self._remove_file_references(content)
            
            # 5. 删除构建文件
            content = self._remove_build_files(content)
            
            # 6. 删除构建阶段中的引用
            content = self._remove_from_build_phases(content)
            
            # 7. 清理 FRAMEWORK_SEARCH_PATHS
            content = self._clean_framework_search_paths(content)

            # 8. 删除 Frameworks 组中的引用
            content = self._clean_frameworks_group(content)

            # 9. 删除第三方组定义
            content = self._remove_third_party_groups(content)
            
            # 验证文件结构完整性
            if self._validate_file_structure(content):
                # 保存修改后的文件
                with open(self.pbxproj_path, 'w', encoding='utf-8') as f:
                    f.write(content)
                
                print("✅ 所有第三方文件清理完成")
                return True
            else:
                print("❌ 文件结构验证失败，恢复备份")
                shutil.copy2(backup_path, self.pbxproj_path)
                return False
                
        except Exception as e:
            print(f"❌ 清理失败: {e}")
            import traceback
            traceback.print_exc()
            
            # 恢复备份
            if os.path.exists(backup_path):
                shutil.copy2(backup_path, self.pbxproj_path)
                print("✅ 已恢复备份文件")
            
            return False
    
    def _validate_file_structure(self, content):
        """验证文件结构完整性"""
        print("🔍 验证文件结构...")
        
        # 检查基本结构
        required_sections = [
            '/* Begin PBXFileReference section */',
            '/* End PBXFileReference section */',
            '/* Begin PBXGroup section */',
            '/* End PBXGroup section */',
            '/* Begin PBXNativeTarget section */',
            '/* End PBXNativeTarget section */',
            '/* Begin PBXProject section */',
            '/* End PBXProject section */',
            '/* Begin XCBuildConfiguration section */',
            '/* End XCBuildConfiguration section */',
            '/* Begin XCConfigurationList section */',
            '/* End XCConfigurationList section */'
        ]
        
        for section in required_sections:
            if section not in content:
                print(f"❌ 缺少必要的节: {section}")
                return False
        
        # 检查括号匹配
        open_braces = content.count('{')
        close_braces = content.count('}')
        if open_braces != close_braces:
            print(f"❌ 括号不匹配: {{ {open_braces} vs }} {close_braces}")
            return False
        
        # 检查根对象（动态检测）
        root_object_pattern = r'rootObject = [A-F0-9]{24}'
        if not re.search(root_object_pattern, content):
            print("❌ 缺少根对象引用")
            return False
        
        print("✅ 文件结构验证通过")
        return True
    
    def _remove_file_system_synchronized_groups(self, content):
        """删除第三方相关的 PBXFileSystemSynchronizedRootGroup"""
        print("🗂️ 删除 FileSystemSynchronizedRootGroup...")

        # 通用模式匹配第三方框架和资源组
        patterns = [
            # OSFrameworks/CNFrameworks 组
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Frameworks \*/ = \{\s*isa = PBXFileSystemSynchronizedRootGroup;\s*name = [A-Z]{2}Frameworks;\s*path = \.\./[A-Z]{2}Frameworks;\s*sourceTree = SOURCE_ROOT;\s*\};\s*',
            # OSResources/CNResources 组
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Resources \*/ = \{\s*isa = PBXFileSystemSynchronizedRootGroup;\s*name = [A-Z]{2}Resources;\s*path = \.\./[A-Z]{2}Resources;\s*sourceTree = SOURCE_ROOT;\s*\};\s*'
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content, re.DOTALL)
            for match in matches:
                # 提取组名
                group_name_match = re.search(r'/\*\s*([^*]+)\s*\*/', match)
                if group_name_match:
                    group_name = group_name_match.group(1)
                    print(f"  - 删除 FileSystemSynchronizedRootGroup: {group_name}")
                content = content.replace(match, '\n')

        return content
    
    def _remove_from_main_group(self, content):
        """从主组中移除引用"""
        print("📂 从主组中移除引用...")

        # 通用模式移除主组中的第三方引用
        patterns = [
            # 第三方框架和资源组
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Frameworks \*/,\s*',
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Resources \*/,\s*',
            # 第三方 bundle 文件
            r'\s*[A-F0-9]{24} /\* [^*]*\.bundle \*/,\s*'
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                # 提取名称
                name_match = re.search(r'/\*\s*([^*]+)\s*\*/', match)
                if name_match:
                    name = name_match.group(1)
                    print(f"  - 从主组移除: {name}")
                content = content.replace(match, '\n\t\t\t\t')

        return content
    
    def _remove_from_file_system_synchronized_groups(self, content):
        """从 fileSystemSynchronizedGroups 中移除引用"""
        print("🔗 从 fileSystemSynchronizedGroups 中移除引用...")

        # 通用模式移除第三方框架和资源组引用
        patterns = [
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Frameworks \*/,\s*',
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Resources \*/,\s*'
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                # 提取组名
                group_name_match = re.search(r'/\*\s*([^*]+)\s*\*/', match)
                if group_name_match:
                    group_name = group_name_match.group(1)
                    print(f"  - 从 fileSystemSynchronizedGroups 移除: {group_name}")
                content = content.replace(match, '\n\t\t\t\t')

        return content
    
    def _remove_file_references(self, content):
        """删除文件引用"""
        print("📄 删除文件引用...")

        # 通用模式匹配所有 .a、.framework、.bundle 文件
        patterns = [
            # .a 文件 - 匹配任何 .a 文件（多种格式）
            r'\s*[A-F0-9]{24} /\* [^*]*\.a \*/ = \{isa = PBXFileReference; explicitFileType = archive\.ar; path = [^;]*\.a; sourceTree = BUILT_PRODUCTS_DIR; \};\s*',
            r'\s*[A-F0-9]{24} /\* [^*]*\.a \*/ = \{isa = PBXFileReference; lastKnownFileType = archive\.ar; path = [^;]*\.a; sourceTree = "[^"]*"; \};\s*',
            # .framework 文件 - 匹配任何 .framework 文件（多种格式）
            r'\s*[A-F0-9]{24} /\* [^*]*\.framework \*/ = \{isa = PBXFileReference; explicitFileType = wrapper\.framework; path = [^;]*\.framework; sourceTree = BUILT_PRODUCTS_DIR; \};\s*',
            r'\s*[A-F0-9]{24} /\* [^*]*\.framework \*/ = \{isa = PBXFileReference; lastKnownFileType = wrapper\.framework; path = [^;]*\.framework; sourceTree = "[^"]*"; \};\s*',
            # .bundle 文件 - 匹配任何 .bundle 文件（多种格式）
            r'\s*[A-F0-9]{24} /\* [^*]*\.bundle \*/ = \{isa = PBXFileReference; lastKnownFileType = "wrapper\.plug-in"; name = [^;]*\.bundle; path = \.\./[^;]*\.bundle; sourceTree = SOURCE_ROOT; \};\s*',
            r'\s*[A-F0-9]{24} /\* [^*]*\.bundle \*/ = \{isa = PBXFileReference; lastKnownFileType = "wrapper\.plug-in"; path = [^;]*\.bundle; sourceTree = "[^"]*"; \};\s*'
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                # 提取文件名
                file_name_match = re.search(r'/\*\s*([^*]+)\s*\*/', match)
                if file_name_match:
                    file_name = file_name_match.group(1)
                    print(f"  - 删除文件引用: {file_name}")
                content = content.replace(match, '\n')

        return content

    def _remove_build_files(self, content):
        """删除构建文件"""
        print("🔧 删除构建文件...")

        # 通用模式匹配所有 .a、.framework、.bundle 构建文件
        patterns = [
            # .a 文件构建文件
            r'\s*[A-F0-9]{24} /\* [^*]*\.a in Frameworks \*/ = \{isa = PBXBuildFile; fileRef = [A-F0-9]{24} /\* [^*]*\.a \*/; \};\s*',
            # .framework 文件构建文件
            r'\s*[A-F0-9]{24} /\* [^*]*\.framework in Frameworks \*/ = \{isa = PBXBuildFile; fileRef = [A-F0-9]{24} /\* [^*]*\.framework \*/; \};\s*',
            # .bundle 文件构建文件
            r'\s*[A-F0-9]{24} /\* [^*]*\.bundle in Resources \*/ = \{isa = PBXBuildFile; fileRef = [A-F0-9]{24} /\* [^*]*\.bundle \*/; \};\s*'
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                # 提取文件名
                file_name_match = re.search(r'/\*\s*([^*]+?)\s+in\s+\w+\s*\*/', match)
                if file_name_match:
                    file_name = file_name_match.group(1)
                    print(f"  - 删除构建文件: {file_name}")
                content = content.replace(match, '\n')

        return content

    def _remove_from_build_phases(self, content):
        """从构建阶段中移除引用"""
        print("⚙️ 从构建阶段中移除引用...")

        # 从 Frameworks Build Phase 中移除所有 .a 和 .framework 文件
        frameworks_patterns = [
            r'\s*[A-F0-9]{24} /\* [^*]*\.a in Frameworks \*/,\s*',
            r'\s*[A-F0-9]{24} /\* [^*]*\.framework in Frameworks \*/,\s*'
        ]

        for pattern in frameworks_patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                # 提取文件名
                file_name_match = re.search(r'/\*\s*([^*]+?)\s+in\s+Frameworks\s*\*/', match)
                if file_name_match:
                    file_name = file_name_match.group(1)
                    print(f"  - 从 Frameworks 构建阶段移除: {file_name}")
                content = content.replace(match, '\n\t\t\t\t')

        # 从 Resources Build Phase 中移除所有 .bundle 文件
        resources_patterns = [
            r'\s*[A-F0-9]{24} /\* [^*]*\.bundle in Resources \*/,\s*'
        ]

        for pattern in resources_patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                # 提取文件名
                file_name_match = re.search(r'/\*\s*([^*]+?)\s+in\s+Resources\s*\*/', match)
                if file_name_match:
                    file_name = file_name_match.group(1)
                    print(f"  - 从 Resources 构建阶段移除: {file_name}")
                content = content.replace(match, '\n\t\t\t\t')

        return content

    def _clean_framework_search_paths(self, content):
        """清理所有搜索路径配置"""
        print("🔍 清理搜索路径配置...")

        # 清理所有类型的搜索路径
        search_path_types = [
            'FRAMEWORK_SEARCH_PATHS',
            'HEADER_SEARCH_PATHS',
            'LIBRARY_SEARCH_PATHS'
        ]

        for search_type in search_path_types:
            print(f"  🔍 清理 {search_type}...")

            # 模式1：多行格式 SEARCH_PATHS = ( ... );
            pattern1 = rf'(\s*{search_type}\s*=\s*\([^;]*\);\s*)'
            matches1 = re.findall(pattern1, content, re.DOTALL)
            for match in matches1:
                print(f"    - 删除多行 {search_type} 块")
                content = content.replace(match, '\n')

            # 模式2：单行格式 SEARCH_PATHS = "path"; (包括转义引号)
            pattern2 = rf'(\s*{search_type}\s*=\s*"[^"]*";\s*)'
            matches2 = re.findall(pattern2, content)
            for match in matches2:
                print(f"    - 删除单行 {search_type} 配置")
                content = content.replace(match, '\n')

            # 模式2b：单行格式带转义引号 SEARCH_PATHS = "\"path\"";
            pattern2b = rf'(\s*{search_type}\s*=\s*"\\\"[^"]*\\\"";\s*)'
            matches2b = re.findall(pattern2b, content)
            for match in matches2b:
                print(f"    - 删除单行 {search_type} 配置（转义引号）")
                content = content.replace(match, '\n')

            # 模式3：单行格式 SEARCH_PATHS = path;（无引号）
            pattern3 = rf'(\s*{search_type}\s*=\s*[^;]+;\s*)'
            matches3 = re.findall(pattern3, content)
            for match in matches3:
                # 确保不是已经处理过的带引号的情况
                if '"' not in match and '(' not in match:
                    print(f"    - 删除单行 {search_type} 配置（无引号）")
                    content = content.replace(match, '\n')

        return content

    def _clean_frameworks_group(self, content):
        """清理 Frameworks 组"""
        print("📦 清理 Frameworks 组...")

        # 从 Frameworks 组中移除所有 .a 和 .framework 文件引用
        patterns = [
            r'\s*[A-F0-9]{24} /\* [^*]*\.a \*/,\s*',
            r'\s*[A-F0-9]{24} /\* [^*]*\.framework \*/,\s*'
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                # 提取文件名
                file_name_match = re.search(r'/\*\s*([^*]+)\s*\*/', match)
                if file_name_match:
                    file_name = file_name_match.group(1)
                    print(f"  - 从 Frameworks 组移除: {file_name}")
                content = content.replace(match, '\n\t\t\t\t')

        return content

    def _remove_third_party_groups(self, content):
        """删除第三方组定义"""
        print("🗂️ 删除第三方组定义...")

        # 删除 CNFrameworks/OSFrameworks 组定义
        patterns = [
            # CNFrameworks/OSFrameworks 组定义
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Frameworks \*/ = \{\s*isa = PBXGroup;\s*children = \([^}]*\);\s*name = [A-Z]{2}Frameworks;\s*path = \.\./[A-Z]{2}Frameworks;\s*sourceTree = SOURCE_ROOT;\s*\};\s*',
            # CNResources/OSResources 组定义
            r'\s*[A-F0-9]{24} /\* [A-Z]{2}Resources \*/ = \{\s*isa = PBXGroup;\s*children = \([^}]*\);\s*name = [A-Z]{2}Resources;\s*path = \.\./[A-Z]{2}Resources;\s*sourceTree = SOURCE_ROOT;\s*\};\s*',
            # 其他第三方子组（如 BDASignal, ShanYan 等）
            r'\s*[A-F0-9]{24} /\* [^*]+ \*/ = \{\s*isa = PBXGroup;\s*children = \([^}]*\);\s*path = [^;]*;\s*sourceTree = "[^"]*";\s*\};\s*'
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content, re.DOTALL)
            for match in matches:
                # 提取组名
                group_name_match = re.search(r'/\*\s*([^*]+)\s*\*/', match)
                if group_name_match:
                    group_name = group_name_match.group(1)
                    # 只删除第三方相关的组
                    if any(keyword in group_name for keyword in ['Frameworks', 'Resources', 'BDASignal', 'ShanYan']):
                        print(f"  - 删除第三方组: {group_name}")
                        content = content.replace(match, '\n')

        return content


def main():
    """主函数 - 命令行接口"""
    if len(sys.argv) < 2:
        print("用法: python demo_project_cleaner_safe.py <project.pbxproj路径>")
        print("示例: python demo_project_cleaner_safe.py project.pbxproj")
        sys.exit(1)

    pbxproj_path = sys.argv[1]

    # 创建清理器并处理项目
    try:
        cleaner = DemoProjectCleanerSafe(pbxproj_path)
        success = cleaner.clean_all_third_party_files()

        if success:
            print("✅ 清理成功！")
            sys.exit(0)
        else:
            print("❌ 清理失败！")
            sys.exit(1)

    except Exception as e:
        print(f"❌ 处理过程中出现错误: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)


if __name__ == "__main__":
    main()
