import os
import re
import json
import argparse
from pbxproj import XcodeProject
from pbxproj.pbxsections import PBXShellScriptBuildPhase
import subprocess
import plistlib

#TODO:待修改字段
IOSMINIVERSION = '12.0'
LOCAL_SOURCE_LINE = 'http://tygit.tuyoo.com/dev_platform/ios/frameworks/pods/private-pod-repo.git'
SOURCE_LINE = 'ssh://git@tygit.tuyoo.com:2222/dev_platform/ios/frameworks/pods/private-pod-repo.git'
CUSTOM_OSS_HTTP_PATH = 'https://sa201-ptsdk-res.oss-cn-beijing.aliyuncs.com'

build_config = {}
build_package_config = {}
build_sdk_names_list = []
dependencies = []

CHANNEL_AND_NAME_LIST = ["wechat",
                         "wangyiyidun",
                         "yidunloginv2",
                         "douyin",
                         "rongheguiyin",
                         "yidunzhiwen",
                         "yiduncaptcha",
                         "yidunlivedetect",
                         "appsflyer",
                         "firebaseanalytics",
                         "firebasecrashlytics",
                         "firebaseodm",
                         "yuenanlogin",
                         "facebook",
                         "google",
                         "line",
                         "twitter",
                         "idfa",
                         "haiwailogin",
                         "aihelp",
                         "bugly",
                         "gdt",
                         "push",
                         "fcm"]

BUNDLE_SOURCE = {
    "yidunloginv2":["NTESResource.bundle","TDKYiDunLoginV2Bundle.bundle"],
    "yidunzhiwen":["FPSupport.bundle"],
    "yiduncaptcha":["NTESVerifyCodeResources.bundle","TDKYidunCaptchaBundle.bundle"],
    "yidunlivedetect":["NTESLiveDetectBundle.bundle","TDKYidunLiveDetectBundle.bundle"],
    "haiwailogin":["TDKHaiWaiLoginSource.bundle"],
    "aihelp":["AIHelpSupportSDK.bundle"]
}

EMBEM_FRAMEWORK_SOURCE = {
    "douyin":["UnionOpenPlatformCore.framework","UnionOpenPlatformCPS.framework"],
    "yidunlivedetect":["NEYDnn.framework"]
}

def parse_build_config_json(build_config_json_path, coustom_plist_path):
    print(f"1️⃣ parse_build_config_json, path = {build_config_json_path} begin")
    abs_build_config_json_path = os.path.abspath(build_config_json_path)
    if os.path.isfile(abs_build_config_json_path):   
        with open(abs_build_config_json_path, 'r', encoding='utf-8') as json_file:
            data = json.loads(json_file.read())
        # print(f"1️⃣ parse_build_config_json, json = {data} ing")
        # 将数据写入 plist 文件
        with open(coustom_plist_path, 'wb') as plist_file:
            plistlib.dump(data, plist_file)
        global coustom_plist_file_path
        coustom_plist_file_path = coustom_plist_path
        global build_config 
        build_config = data
        global build_package_config
        build_package_config = data.get('buildConfig')
        global build_sdk_names_list
        names = data.get('login').get('sdks')
        build_sdk_names_list = list(set([name for name in names if name]))
        fcm = data.get('fcm')
        push = data.get('push')
        if fcm:
            build_sdk_names_list.append('fcm')
        if push:
            build_sdk_names_list.append('push')
        if ('firebasecrashlytics' in build_sdk_names_list or 'firebaseodm' in build_sdk_names_list) and 'firebaseanalytics' not in build_sdk_names_list:
            build_sdk_names_list.append('firebaseanalytics')
    else:
        print(f"❌ parse_build_config_json file error")
        exit(1)

def get_add_project_capability_flag():
    if build_package_config and "addCapability" in build_package_config:
        return build_package_config.get("addCapability")
    else:
        return False

def get_git_access_method():
    if build_package_config and "gitAccessMethod" in build_package_config:
        return build_package_config.get("gitAccessMethod")
    else:
        return "ssh"

def get_sdk_source_method():
    if build_package_config and "getSourceMethod" in build_package_config:
        return build_package_config.get("getSourceMethod")
    else:
        return "git"

def add_file_to_project(project, file_path, skip_if_exsit=True, target_name = None):
    print(f"💡 add_file_to_project {file_path}")
    abs_file_path = os.path.abspath(file_path)
    if os.path.exists(abs_file_path):
        if not project.get_files_by_path(abs_file_path):
            project.add_file(abs_file_path, target_name = target_name)
            print(f"⚠️ add_file_to_project {abs_file_path} add")
        elif not skip_if_exsit:
            project.remove_files_by_path(abs_file_path)
            print(f"🗑️ 已删除旧的 {abs_file_path}")
            file_ref = project.add_file(abs_file_path, target_name = target_name)
            print(f"✅ 已添加: {abs_file_path} -> {file_ref}")
        else:
            print(f"⚠️ add_file_to_project {abs_file_path} already exist")
    else:
        print(f"⚠️ add_file_to_project {abs_file_path} not exist")

def set_library_search_paths(project, search_paths, target_name=None):
    print(f"💡 set_library_search_paths {search_paths}")
    library_search_paths = None
    for target in project.objects.get_targets():
        build_configurations = project.objects.get_configurations_on_targets(target.name)
        for config in build_configurations:
            library_search_paths = config.buildSettings.get("LIBRARY_SEARCH_PATHS", [])
            default_paths = ['$(inherited)']
            if library_search_paths:
                if not isinstance(library_search_paths, list):
                    library_search_paths = [library_search_paths]
                print(f"💡 set_library_search_paths {library_search_paths}")
                library_search_paths = default_paths + list(dict.fromkeys(search_paths + library_search_paths))

                library_search_paths = default_paths + [item for item in library_search_paths if item not in default_paths]
            else:
                library_search_paths = default_paths
            config.buildSettings["LIBRARY_SEARCH_PATHS"] = library_search_paths
            print(f"💡 set_library_search_paths {target.name} - {library_search_paths}")
    else:
        print(f"💡 set_library_search_paths fail")
        return None
  
# **处理 Info.plist Begin**
def add_bool_to_info_plist(plist_file_path, key, value, skip_existing_elements = True):
    print(f"💡 add_bool_to_info_plist {plist_file_path} - {key} - {value}")
    if not key and key == '':
        print(f"⚠️ add_bool_to_info_plist {key} invalue")
        return
    if not isinstance(value, bool):
        print(f"⚠️ add_bool_to_info_plist {value} invalue")
        return
    with open(plist_file_path, 'rb') as f:
        plist_data = plistlib.load(f)
    if not skip_existing_elements or key not in plist_data:
        # 如果存在，但不跳过，则直接覆盖, 如果不存在，则创建一个新的列表并添加 schemes_to_add
        plist_data[key] = value

    # 将修改后的数据写回 info.plist 文件
    with open(plist_file_path, 'wb') as f:
        plistlib.dump(plist_data, f)

def add_string_to_info_plist(plist_file_path, key, value, skip_existing_elements = True):
    print(f"💡 add_string_to_info_plist {plist_file_path} - {key} - {value}")
    if not key and key == '':
        print(f"⚠️ add_string_to_info_plist {key} invalue")
        return
    if not isinstance(value, str):
        print(f"⚠️ add_array_to_info_plist {value} invalue")
        return
    with open(plist_file_path, 'rb') as f:
        plist_data = plistlib.load(f)
    if not skip_existing_elements or key not in plist_data:
        # 如果存在，但不跳过，则直接覆盖, 如果不存在，则创建一个新的列表并添加 schemes_to_add
        plist_data[key] = value

    # 将修改后的数据写回 info.plist 文件
    with open(plist_file_path, 'wb') as f:
        plistlib.dump(plist_data, f)

def add_array_to_info_plist(plist_file_path, key, value, skip_existing_elements = True):
    print(f"💡 add_array_to_info_plist {plist_file_path} - {key} - {value}")
    if not os.path.exists(plist_file_path):
        print(f"⚠️ add_array_to_info_plist {plist_file_path} invalue")
        return
    if not key or key == '':
        print(f"⚠️ add_array_to_info_plist {key} invalue")
        return
    if not isinstance(value, list):
        print(f"⚠️ add_array_to_info_plist {value} invalue")
        return
    
    with open(plist_file_path, 'rb') as f:
        plist_data = plistlib.load(f)
    
    # 检查 key 是否存在
    if key not in plist_data:
        plist_data[key] = []
    current_array = plist_data[key]
    for item in value:
        if not skip_existing_elements or item not in current_array:
            current_array.append(item)
    with open(plist_file_path, 'wb') as f:
        plistlib.dump(plist_data, f)

def is_json(variable):
    try:
        json.loads(variable)
        return True
    except (json.JSONDecodeError, TypeError):
        return False

def add_dictionary_to_info_plist(plist_file_path, key, value, skip_existing_elements = True):
    print(f"💡 add_dictionary_to_info_plist {plist_file_path} - {key} - {value}")
    if not key and key == '':
        print(f"⚠️ add_dictionary_to_info_plist {key} invalue")
        return
    if not is_json(value):
        print(f"⚠️ add_dictionary_to_info_plist {value} invalue")
        return
    with open(plist_file_path, 'rb') as f:
        plist_data = plistlib.load(f)
    if not skip_existing_elements or key not in plist_data:
        # 如果存在，但不跳过，则直接覆盖, 如果不存在，则创建一个新的列表并添加 schemes_to_add
        plist_data[key] = value

    # 将修改后的数据写回 info.plist 文件
    with open(plist_file_path, 'wb') as f:
        plistlib.dump(plist_data, f)
# **处理 Info.plist End**

# **处理 BuildSetting-ShellScript Begin**
def normalize_script(script: str):
    if not script:
        return ''
    return script.strip().strip('"').strip("'")

def add_shell_script(project, run_script, input_files=None, output_files=None ,target_name=None, insert_before_compile=False, run_install_build=0):
    # 检查是否已经存在该脚本，避免重复添加
    for target in project.objects.get_targets(target_name):
        print(f'target = {target}')
        for build_phase_id in target.buildPhases:
            build_phase = project.objects[build_phase_id]
            print(f'build_phase = {build_phase_id} = {build_phase}')
            if not isinstance(build_phase, PBXShellScriptBuildPhase):
                continue
            shellScript = build_phase.shellScript or ""
            print(f'build_phase = start1 {shellScript}')
            print(f'build_phase = start2 {run_script}')
            if normalize_script(shellScript) == normalize_script(run_script):
                print("⚠️ 已存在 先删除 ")
                del project.objects[build_phase_id]
                target.remove_build_phase(build_phase)
        project.add_run_script(
            script=run_script,
            input_files=input_files,
            output_files=output_files,
            target_name=target_name,
            insert_before_compile=insert_before_compile,
            run_install_build=run_install_build
        )
        print("✅ 已添加 Run Script Phase。")
    project.save()
# **处理 BuildSetting-ShellScript Begin**

# **处理 entitlements Begin**
def get_entitlements_file_path(project, skip_file_not_exist=True, app_target_name=None):
    print(f"💡 get_entitlements_file_path ")
    entitlements_path = None
    for target in project.objects.get_targets():
        build_configurations = project.objects.get_configurations_on_targets(target.name)
        for config in build_configurations:
            entitlements_path = config.buildSettings.get("CODE_SIGN_ENTITLEMENTS", "")
            if entitlements_path:
                print(f"💡 get_entitlements_file_path {entitlements_path}")
                return os.path.abspath(entitlements_path)
    if not skip_file_not_exist and app_target_name:
        entitlements_file_path = f"{app_target_name}/{app_target_name}.entitlements"
        with open(entitlements_file_path, "wb") as f:
                    plistlib.dump({}, f)
        for target in project.objects.get_targets():
            build_configurations = project.objects.get_configurations_on_targets(target.name)
            for config in build_configurations:
                config.buildSettings["CODE_SIGN_ENTITLEMENTS"] = entitlements_file_path
        project.save()
        return os.path.abspath(entitlements_file_path)
    else:    
        return None

def add_sign_in_with_apple_capability(project, app_target_name):
    print(f"💡 add_sign_in_with_apple_capability")
    if not get_add_project_capability_flag():
        print(f"💡 add_sign_in_with_apple_capability skip")
        return
    entitlements_file_path = get_entitlements_file_path(project, False, app_target_name)
    SIGNIN_KEY = "com.apple.developer.applesignin"
    if os.path.exists(entitlements_file_path):
        with open(entitlements_file_path, "rb") as f:
            entitlements = plistlib.load(f)
        if SIGNIN_KEY in entitlements:
            print(f"✅ add_sign_in_with_apple_capability 已启用")
        else:
            print(f"➕ add_sign_in_with_apple_capability 新增 ")
            entitlements[SIGNIN_KEY] = ["Default"]
        with open(entitlements_file_path, "wb") as f:
            plistlib.dump(entitlements, f)
        print(f"✅ add_sign_in_with_apple_capability `.entitlements` 文件已更新")
    else:
        print(f"⚠️ add_sign_in_with_apple_capability `.entitlements` not exist")

def add_notification_capability(project, app_target_name):
    print(f"💡 add_notification_capability")
    if not get_add_project_capability_flag():
        print(f"💡 add_notification_capability skip")
        return
    entitlements_file_path = get_entitlements_file_path(project, False, app_target_name)
    APS_ENV_KEY = "aps-environment"
    if os.path.exists(entitlements_file_path):
        with open(entitlements_file_path, "rb") as f:
            entitlements = plistlib.load(f)
        if APS_ENV_KEY in entitlements:
            print(f"✅ add_notification_capability 已启用")
        else:
            print(f"➕ add_notification_capability 新增 ")
            entitlements[APS_ENV_KEY] = "production"
        with open(entitlements_file_path, "wb") as f:
            plistlib.dump(entitlements, f)
        print(f"✅ add_notification_capability `.entitlements` 文件已更新")
    else:
        print(f"⚠️ add_notification_capability `.entitlements` not exist")

def add_background_modes_capability(info_plist_path):
    print(f"💡 add_background_modes_capability")
    if not get_add_project_capability_flag():
        print(f"💡 add_background_modes_capability skip")
        return
    print(f"✅ `info.plist` 文件已更新 UIBackgroundModes 和 remote-notification 权限")
    add_array_to_info_plist(info_plist_path, 'UIBackgroundModes', ['remote-notification'])

def add_keychain_capability(project, app_target_name):
    print(f"💡 add_keychain_capability")
    if not get_add_project_capability_flag():
        print(f"💡 add_keychain_capability skip")
        return
    entitlements_file_path = get_entitlements_file_path(project, False, app_target_name)
    KEY_CHANIN_KEY = "keychain-access-groups"
    if os.path.exists(entitlements_file_path):
        with open(entitlements_file_path, "rb") as f:
            entitlements = plistlib.load(f)
        if KEY_CHANIN_KEY in entitlements:
            print(f"✅ add_keychain_capability 已启用")
        else:
            print(f"➕ add_keychain_capability 新增 ")
            entitlements[KEY_CHANIN_KEY] = [""]
        with open(entitlements_file_path, "wb") as f:
            plistlib.dump(entitlements, f)
        print(f"✅ add_keychain_capability `.entitlements` 文件已更新")
    else:
        print(f"⚠️ add_keychain_capability `.entitlements` not exist")

def process_applinks(applinks):
    """
    处理 applinks 列表，确保每个 URL 被转换为符合 Apple Universal Links 规范的格式。
    """
    processed_applinks = []
    for url in applinks:
        if url.startswith("http://"):
            domain = url[len("http://"):]
        elif url.startswith("https://"):
            domain = url[len("https://"):]
        else:
            domain = url
        
        domain = domain.split('/')[0]
        processed_applinks.append(f"applinks:{domain}")
    
    return processed_applinks

def add_associated_domains_capability(project, applinks, app_target_name):
    print(f"💡 add_associated_domains_capability")
    if not get_add_project_capability_flag():
        print(f"💡 add_associated_domains_capability skip")
        return
    entitlements_file_path = get_entitlements_file_path(project, False, app_target_name)
    DOMAINS_KEY = "com.apple.developer.associated-domains"
    processed_applinks = process_applinks(applinks)
    if os.path.exists(entitlements_file_path):
        with open(entitlements_file_path, "rb") as f:
            entitlements = plistlib.load(f)
        if DOMAINS_KEY in entitlements:
            print(f"✅ 已启用 Associated Domains，正在补充信息")
            current_domains = entitlements[DOMAINS_KEY]
            # 合并两个列表，去重
            updated_domains = list(set(current_domains + processed_applinks))
            entitlements[DOMAINS_KEY] = updated_domains
        else:
            print(f"➕ add_associated_domains_capability 新增 ")
            entitlements[DOMAINS_KEY] = processed_applinks
        with open(entitlements_file_path, "wb") as f:
            plistlib.dump(entitlements, f)
        print(f"✅ add_associated_domains_capability `.entitlements` 文件已更新")
    else:
        print(f"⚠️ add_associated_domains_capability `.entitlements` not exist")
# **处理 entitlements End**

def add_dependencies_set(dependency):
    if dependency and isinstance(dependency, list):
        global dependencies
        dependencies = list(set(dependencies+dependency))

def create_podfile(podfile_path, miniversion, app_target_name, target_name):
    """创建一个新的 Podfile 文件"""
    print(f"💡 create_podfile {podfile_path} - {target_name}")
    with open(podfile_path, 'w') as f:
        f.write(f"platform :ios, '{miniversion}'\n")
        # if app_target_name != target_name:
        #     f.write(f"target '{app_target_name}' do\n")
        #     f.write(f"  use_frameworks!\n")
        #     f.write(f"  target '{target_name}' do\n")
        #     f.write(f"      use_frameworks!\n")
        #     f.write("   end\n")
        #     f.write("end\n")
        # else:
        f.write(f"target '{target_name}' do\n")
        f.write(f"  use_frameworks!\n")
        f.write("end\n")
def read_podfile(podfile_path):
    """读取 Podfile 内容"""
    with open(podfile_path, 'r') as f:
        return f.readlines()

def write_podfile(podfile_path, lines):
    """写入更新后的 Podfile 内容"""
    with open(podfile_path, 'w') as f:
        f.writelines(lines)

def extract_pod_info(pod_line):
    # 正则表达式模式，处理可能的多个空格
    # 解释：
    # \s* 匹配任意数量的空格（包括0个）
    # '([^']+)' 匹配单引号内的内容（组件名和版本号）
    pattern = r"pod\s+'([^']+)'\s*,\s*'([^']+)'"
    
    # 执行匹配
    match = re.match(pattern, pod_line.strip())
    
    if match:
        # 返回组件名和版本号
        return match.group(1), match.group(2)
    else:
        return None, None

def update_dependencies(lines, app_target_name, target_name, dependencies):
    """更新 Podfile 中的依赖项 - 根据目标名称判断更新方式"""
    
    # 检查 source 信息
    source_found = False
    # 检查 source 获取方式
    if get_sdk_source_method() == 'http':
        source_line = f"OSS_HTTP_PATH = '{CUSTOM_OSS_HTTP_PATH}'"
    else:
        if get_git_access_method() == 'ssh':
            source_line = f"source '{SOURCE_LINE}'"
        else:
            source_line = f"source '{LOCAL_SOURCE_LINE}'"
        for i, line in enumerate(lines):
            if line.strip() == source_line.strip():
                source_found = True
                break
    
    # 处理 target_name 和 app_target_name 是否相同的情况
    # if app_target_name == target_name:
    print(f"🔄 Updating dependencies for target '{target_name}'")
    
    # 直接处理 target_name 闭包
    target_found = False
    target_index = -1
    end_index = -1
    # 查找target闭包
    for i, line in enumerate(lines):
        if f"target '{target_name}' do" in line:
            target_found = True
            target_index = i
            # 在目标的 do 之前插入 source
            if not source_found:
                lines.insert(i, f'{source_line}\n')
                target_index = target_index + 1
            break
    
    if not target_found:
        print(f"➕ Adding new target '{target_name}'")
        lines.append(f"\ntarget '{target_name}' do\n")
        lines.append("    use_frameworks!\n")
        lines.append("end\n")
        target_index = len(lines) - 3
        end_index = len(lines) - 1
    else:
        for j in range(target_index + 1, len(lines)):
            if 'end' in lines[j]:
                end_index = j
                break
    
    # 查找依赖项并更新
    existing_deps = [
        dep.strip().split(',')[0].strip() for dep in lines[target_index + 1:end_index]
        if dep.strip().lower().startswith("pod ")
    ]

    dealDependencies = []
    # 处理 http 资源
    if get_sdk_source_method() == 'http':
        # "pod 'TYWeChatHandle', '1.1.0'"
        for dep in dependencies:
            podName, podVersion = extract_pod_info(dep)
            if podName != None and podVersion != None:
                # TODO: 得把test改掉！！
                dealDependencies.append(f"pod '{podName}', :podspec => \"#{{OSS_HTTP_PATH}}/{podName}/test/{podVersion}/{podName}.podspec\"")
    else :
        dealDependencies = dependencies
    
    new_lines = lines.copy()
    for dep in dealDependencies:
        dep_name = dep.strip().split(',')[0].strip()
        if dep_name and dep_name not in existing_deps:
            new_lines.insert(target_index + 1, f"    {dep}\n")
            end_index += 1
        else:
            for i in range(len(new_lines)):
                if i > target_index and i < end_index:
                    line = new_lines[i]
                    if line.strip().startswith('pod ') and line.strip().split(',')[0].strip() == dep_name:
                        new_lines[i] = f"    {dep}\n"
    
    lines[:] = new_lines
    return True
    
    # else:
    #     # 如果 target_name 和 app_target_name 不同，则嵌套 target_name 在 app_target_name 内部
    #     print(f"🔄 Updating dependencies for target '{target_name}' inside the '{app_target_name}' target.")
        
    #     # 处理 app_target_name 闭包
    #     target_found = False
    #     app_target_index = -1
    #     app_end_index = -1
    #     for i, line in enumerate(lines):
    #         if f"target '{app_target_name}' do" in line:
    #             target_found = True
    #             app_target_index = i
    #             # 在目标的 do 之前插入 source
    #             if not source_found:
    #                 lines.insert(i, f'{source_line}\n')
    #             break
        
    #     if not target_found:
    #         print(f"➕ Adding new target '{app_target_name}'")
    #         lines.append(f"\ntarget '{app_target_name}' do\n")
    #         lines.append("    use_frameworks!\n")
    #         lines.append("end\n")
    #         app_target_index = len(lines) - 3
    #         app_end_index = len(lines) - 1
    #     else:
    #         for j in range(app_target_index + 1, len(lines)):
    #             if 'end' in lines[j]:
    #                 app_end_index = j
    #                 break
        
    #     # 查找 app_target_name 下的子目标 target_name 的闭包
    #     child_target_found = False
    #     child_target_index = -1
    #     for i, line in enumerate(lines[app_target_index + 1: app_end_index]):
    #         if f"target '{target_name}' do" in line:
    #             child_target_found = True
    #             child_target_index = i + app_target_index + 1
    #             break
        
    #     if not child_target_found:
    #         print(f"➕ Adding target '{target_name}' inside '{app_target_name}'")
    #         lines.insert(app_end_index, f"\ntarget '{target_name}' do\n")
    #         lines.insert(app_end_index + 1, "    use_frameworks!\n")
    #         lines.insert(app_end_index + 2, "end\n")
    #         child_target_index = app_end_index + 1
    
    #     # 在 target_name 的闭包中添加依赖项
    #     for dep in dependencies:
    #         lines.insert(child_target_index + 1, f"    {dep}\n")
    #         child_target_index += 1

    #     return True

def process_podfile(podfile_path, app_target_name, target_name, miniversion, dependencies):
    """处理 Podfile
        1. podfile_path：podfile文件的路径
        2. target_name：项目名称
        3. dependencies：pod依赖信息的列表（示例： ["pod 'XYSDK'", "pod 'TDKWechatHandle'"]）
    """
    print(f"process_podfile {podfile_path} - {target_name} - {miniversion} - {dependencies}")
    if not os.path.exists(podfile_path):
        create_podfile(podfile_path, miniversion, app_target_name, target_name)

    lines = read_podfile(podfile_path)

    # 更新依赖项
    if update_dependencies(lines, app_target_name, target_name, dependencies):
        write_podfile(podfile_path, lines)

def install_podfile(project_dir):
    # 执行 pod 命令
    print("执行 pod deintegrate && pod install")
    result = subprocess.run(["pod", "deintegrate"], cwd=project_dir)
    if result.returncode != 0:
        print("❌ pod deintegrate 失败")
        exit(4)

    result = subprocess.run(["pod", "install", "--repo-update"], cwd=project_dir)
    if result.returncode != 0:
        print("❌ pod install 失败")
        exit(4)

    print("✅ Podfile 处理完成！")

def find_app_target_names(project):
    print(f"💡 find_app_targets ")
    # 遍历所有的targets，找到主工程target
    app_targets = []
    for target in project.objects.get_targets():
        if target.productType == "com.apple.product-type.application":
            app_targets.append(target.name)
    return app_targets

def copy_bundle_to_main_projet(project, project_path):
    print(f"💡 copy_bundle_to_main_projet {project_path}")
    pod_source_path = os.path.join(project_path, "Pods")
    if os.path.exists(pod_source_path):
        bundle_paths = []
        bundle_sources = [bundle for bundles in BUNDLE_SOURCE.values() for bundle in bundles]
        for root, dirs, files in os.walk(pod_source_path):
            folder_name = os.path.basename(root)
            if "Pods" in root and root.endswith(".bundle") and folder_name in bundle_sources:
                bundle_paths.append(os.path.abspath(root))
        target_names = find_app_target_names(project)
        print(f"💡 copy_bundle_to_main_projet target_names {target_names}")
        for bundle in bundle_paths:
            for target_name in target_names:
                add_file_to_project(project, bundle, target_name=target_name, skip_if_exsit=False)
    project.save()

def add_file_to_embed_frameworks(project, project_path):
    print(f"💡 add_file_to_embed_frameworks {project_path}")
    pod_source_path = os.path.join(project_path, "Pods")
    if os.path.exists(pod_source_path):
        embed_framework_paths = []
        embed_framework_sources = [framework for frameworks in EMBEM_FRAMEWORK_SOURCE.values() for framework in frameworks]
        for root, dirs, files in os.walk(pod_source_path):
            folder_name = os.path.basename(root)
            if "Pods" in root and (root.endswith(".framework") or root.endswith(".xcframework")) and folder_name in embed_framework_sources:
                embed_framework_paths.append(os.path.abspath(root))
        target_names = find_app_target_names(project)
        print(f"💡 add_file_to_embed_frameworks target_names {embed_framework_paths}")
        print(f"💡 add_file_to_embed_frameworks target_names {target_names}")
        for framework in embed_framework_paths:
            for target_name in target_names:
                add_file_to_project(project, framework, target_name=target_name, skip_if_exsit=False)
    project.save()

def process_wechat_setting(project,info_plist_path, app_target_name):
    print(f"💡 process_wechat_setting ")
    project.add_other_ldflags(['$(inherited)', '-all_load'])
    wechat_config = build_config.get('login').get('wechat')
    if wechat_config.get('wxAppId') and wechat_config.get('wxUniversalLink'):
        add_array_to_info_plist(info_plist_path, 'LSApplicationQueriesSchemes', ['weixin','weixinULAPI','weixinURLParamsAPI','wechat'])
        url_scheme = {
            'CFBundleURLName': 'wx',
            'CFBundleURLSchemes': [wechat_config.get('wxAppId')]
        }
        add_array_to_info_plist(info_plist_path, 'CFBundleURLTypes', [url_scheme])
        add_associated_domains_capability(project, wechat_config.get('wxUniversalLink'), app_target_name)
    else:
        print(f"❌ process_wechat_setting configs are incomplete ")

def process_yidunlivedetect_setting(project,info_plist_path):
    print(f"💡 process_yidunlivedetect_setting ")
    channel_config = build_config.get('login').get('yidunlivedetect')
    if channel_config.get('businessId'):
        add_string_to_info_plist(info_plist_path, 'kYiDunLiveDetectBusinessId', channel_config.get('businessId'), False)
        if not channel_config.get('NSPhotoLibraryUsageDescription'):
            add_string_to_info_plist(info_plist_path, 'NSPhotoLibraryUsageDescription', '相册权限文案')
        else:
            add_string_to_info_plist(info_plist_path, 'NSPhotoLibraryUsageDescription', channel_config.get('NSPhotoLibraryUsageDescription'))
        if not channel_config.get('NSCameraUsageDescription'):
            add_string_to_info_plist(info_plist_path, 'NSCameraUsageDescription', '相机权限文案')
        else:
            add_string_to_info_plist(info_plist_path, 'NSCameraUsageDescription', channel_config.get('NSCameraUsageDescription'))
    else:
        print(f"❌ process_yidunlivedetect_setting configs are incomplete ")

def process_facebook_setting(project,info_plist_path):
    print(f"💡 process_facebook_setting ")
    channel_config = build_config.get('login').get('facebook')
    if channel_config.get('facebookAppId') and channel_config.get('facebookClientToken') and channel_config.get('facebookDisplayName'):
        facebookAppId = channel_config.get('facebookAppId')
        add_string_to_info_plist(info_plist_path, 'FacebookAppID', facebookAppId, False)
        add_string_to_info_plist(info_plist_path, 'FacebookClientToken', channel_config.get('facebookClientToken'), False)
        add_string_to_info_plist(info_plist_path, 'FacebookDisplayName', channel_config.get('facebookDisplayName'), False)
        add_array_to_info_plist(info_plist_path, 'LSApplicationQueriesSchemes', ['fb-messenger-share-api','fbapi'])
        url_scheme = {
            'CFBundleURLName': 'fb',
            'CFBundleURLSchemes': [f'fb{facebookAppId}']
        }
        add_array_to_info_plist(info_plist_path, 'CFBundleURLTypes', [url_scheme])
    else:
        print(f"❌ process_facebook_setting configs are incomplete ")

def process_google_setting(project,info_plist_path):
    print(f"💡 process_google_setting ")
    channel_config = build_config.get('login').get('google')
    googleiOSClientId = channel_config.get('googleiOSClientId')
    if googleiOSClientId:
        add_string_to_info_plist(info_plist_path, 'GIDClientID', googleiOSClientId, False)
        fanzhuan_google_client_id = ".".join(googleiOSClientId.split(".")[::-1])
        url_scheme = {
            'CFBundleURLName': 'google',
            'CFBundleURLSchemes': [fanzhuan_google_client_id]
        }
        add_array_to_info_plist(info_plist_path, 'CFBundleURLTypes', [url_scheme])
    else:
        print(f"❌ process_google_setting configs are incomplete ")

def process_line_setting(project,info_plist_path):
    print(f"💡 process_line_setting ")
    channel_config = build_config.get('login').get('line')
    if channel_config.get('TYLineChannelID') and channel_config.get('TYLineLinkURL'):
        add_string_to_info_plist(info_plist_path, 'TYLineChannelID', channel_config.get('TYLineChannelID'), False)
        add_string_to_info_plist(info_plist_path, 'TYLineLinkURL', channel_config.get('TYLineLinkURL'), False)
        add_array_to_info_plist(info_plist_path, 'LSApplicationQueriesSchemes', ['lineauth2'])
        package = build_config.get('package')
        url_scheme = {
            'CFBundleURLName': 'line',
            'CFBundleURLSchemes': [f'line3rdp.{package}']
        }
        add_array_to_info_plist(info_plist_path, 'CFBundleURLTypes', [url_scheme])
        try:
            result = subprocess.run(["where", "carthage"], capture_output=True, text=True, check=True)
            carthage_path = result.stdout.strip().split("\n")[0]
            shell_scrip = f"{carthage_path} copy-frameworks"
            target_names = find_app_target_names(project)
            add_shell_script(project, shell_scrip, target_name=target_names)
        except subprocess.CalledProcessError:
            print(f"❌ process_line_setting Carthage not exist ")
            exit(1)
    else:
        print(f"❌ process_line_setting configs are incomplete ")

def process_twitter_setting(project,info_plist_path):
    print(f"💡 process_twitter_setting ")
    channel_config = build_config.get('login').get('twitter')
    if channel_config.get('twitterClientid') and channel_config.get('twitterSpoce'):
        add_string_to_info_plist(info_plist_path, 'TYTwitterClientid', channel_config.get('twitterClientid'), False)
        add_array_to_info_plist(info_plist_path, 'TYTwitterSpoce', list(channel_config.get('twitterSpoce')))
    else:
        print(f"❌ process_twitter_setting configs are incomplete ")

def process_rongheguiyin_setting(project, info_plist_path):
    channel_config = build_config.get('login').get('rongheguiyin')
    url_scheme = {
        'CFBundleURLName': 'rongheguiyin',
        'CFBundleURLSchemes': [build_config.get('package')]
    }
    add_array_to_info_plist(info_plist_path, 'CFBundleURLTypes', [url_scheme])
    if channel_config and 'RongHeGuiYinStrategy' in channel_config:
        add_bool_to_info_plist(info_plist_path, "RongHeGuiYinStrategy", channel_config.get('RongHeGuiYinStrategy'), False)

def process_aihelp_setting(project, info_plist_path):
    channel_config = build_config.get('login').get('aihelp')
    if channel_config.get('TYAIHelpAppID') and channel_config.get('TYAIHelpDomainName') and channel_config.get('TYAIHelpLanguage'):
        add_string_to_info_plist(info_plist_path, 'TYAIHelpAppID', channel_config.get('TYAIHelpAppID'), False)
        add_string_to_info_plist(info_plist_path, 'TYAIHelpDomainName', channel_config.get('TYAIHelpDomainName'), False)
        add_string_to_info_plist(info_plist_path, 'TYAIHelpLanguage', channel_config.get('TYAIHelpLanguage'), False)
        add_string_to_info_plist(info_plist_path, 'NSPhotoLibraryUsageDescription', channel_config.get('NSPhotoLibraryUsageDescription'), False)
        add_bool_to_info_plist(info_plist_path, 'PHPhotoLibraryPreventAutomaticLimitedAccessAlert', channel_config.get('PHPhotoLibraryPreventAutomaticLimitedAccessAlert'), False)
    else:
        print(f"❌ process_aihelp_setting configs are incomplete ")

def process_bugly_setting(project, info_plist_path):    
    channel_config = build_config.get('login').get('bugly')
    if channel_config.get('BuglyAppID'):
        add_string_to_info_plist(info_plist_path, 'BuglyAppID', channel_config.get('BuglyAppID'), False)
    else:
        print(f"❌ process_bugly_setting configs are incomplete ")

def get_info_plist_path(project_path, target_name):
    path = os.path.join(project_path, f'Info.plist')
    if os.path.exists(path):
        info_plist_path = path
    else:
        path = os.path.join(project_path, f'{target_name}/Info.plist')
        if os.path.exists(path):
            info_plist_path = path
    print(f"💡 get_info_plist_path {info_plist_path}")
    return info_plist_path

def process_project_setting(project_path, project_name, miniversion, target_name):
    print("2️⃣ process_project_setting")
    project_pbxproj_path = os.path.join(project_path, f'{project_name}/project.pbxproj')
    # 工程
    project = XcodeProject.load(project_pbxproj_path)
    # plist文件
    info_plist_path = get_info_plist_path(project_path, target_name)
    target_names = find_app_target_names(project)
    if len(target_names) != 0:
        app_target_name = target_names[0]
    else:
        app_target_name = None
    # 基础设置： 钥匙串 apple登录 applink C++兼容 swift
    project.add_other_ldflags(['$(inherited)', '-ObjC'])
    add_file_to_project(project, coustom_plist_file_path, skip_if_exsit=False)
    add_sign_in_with_apple_capability(project, app_target_name)
    set_library_search_paths(project, [])
    # add_keychain_capability(project, app_target_name)

    # 遍历设置每一个渠道所需信息
    print(f"2️⃣ process_project_setting {build_sdk_names_list}")
    for channel_name in build_sdk_names_list:
        if channel_name in CHANNEL_AND_NAME_LIST:
            if channel_name == "fcm" or channel_name == "push":
                print(f"💡 process_{channel_name}_setting ")
                channel_config = build_config.get(channel_name)
                add_dependencies_set(channel_config.get('podDependencies'))
                if channel_name == "push":
                    add_notification_capability(project, app_target_name)
                    add_background_modes_capability(info_plist_path)
            else:
                channel_config = build_config.get('login').get(channel_name)
                add_dependencies_set(channel_config.get('podDependencies'))
                if channel_name == "wechat":
                    process_wechat_setting(project, info_plist_path, app_target_name)
                elif channel_name == "wangyiyidun" or channel_name == "yidunloginv2":
                    if channel_config.get('unionSpecialLogo'):
                        union_special_logo_path = os.path.join(project_path, 'union_special_logo.png')
                        add_file_to_project(project, union_special_logo_path, skip_if_exsit=False)
                    project.add_other_ldflags(['$(inherited)', '-fprofile-instr-generate'])
                elif channel_name == "yidunzhiwen":
                    add_string_to_info_plist(info_plist_path, 'kYiDunDeviceBusinessId', channel_config.get('businessId'), False)
                elif channel_name == "yiduncaptcha":
                    add_string_to_info_plist(info_plist_path, 'kYiDunCaptchaBusinessId', channel_config.get('businessId'), False)
                    if channel_config.get('kYiDunCaptchaCloseBtn'):
                        add_string_to_info_plist(info_plist_path, 'kYiDunCaptchaCloseBtn', "1", False)
                elif channel_name == "yidunlivedetect":
                    process_yidunlivedetect_setting(project, info_plist_path)
                elif channel_name == "appsflyer":
                    add_string_to_info_plist(info_plist_path, 'TYAppsFlyerAppleAppId', channel_config.get('appsFlyerAppleAppId'), False)
                    add_string_to_info_plist(info_plist_path, 'TYAppsFlyerDevkey', channel_config.get('appsFlyerDevkey'), False)
                elif channel_name == "firebaseanalytics":
                    google_plist_path = os.path.join(project_path, 'GoogleService-Info.plist')
                    add_file_to_project(project, google_plist_path, skip_if_exsit=False)
                elif channel_name == "facebook":
                    process_facebook_setting(project, info_plist_path)
                elif channel_name == "google":
                    process_google_setting(project, info_plist_path)
                elif channel_name == "line":
                    process_line_setting(project, info_plist_path)
                elif channel_name == "twitter":
                    process_twitter_setting(project, info_plist_path)
                elif channel_name == "rongheguiyin":
                    process_rongheguiyin_setting(project, info_plist_path)
                elif channel_name == "douyin":
                    douyin_config_path = os.path.join(project_path, 'UOPSDKConfig.json')
                    add_file_to_project(project, douyin_config_path, skip_if_exsit=False)
                elif channel_name == "aihelp":
                    process_aihelp_setting(project, info_plist_path)
                elif channel_name == "bugly":
                    process_bugly_setting(project, info_plist_path)
                elif channel_name == "firebasecrashlytics":
                    input_files = [
                        '${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}',
                        '$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)'
                    ]
                    shell_scrip = '"$SRCROOT/Pods/FirebaseCrashlyticsSDK/run"'
                    target_names = find_app_target_names(project)
                    print(f'✅ target_namestarget_namestarget_names = {target_names}')
                    add_shell_script(project, shell_scrip, target_name=target_names, input_files=input_files)
                    project.set_flags('DEBUG_INFORMATION_FORMAT', 'dwarf-with-dsym')
                elif channel_name == "firebaseodm":
                    add_bool_to_info_plist(info_plist_path, 'kFirebaseODM', True, False)
                elif channel_name == "idfa":
                    add_string_to_info_plist(info_plist_path, 'NSUserTrackingUsageDescription', channel_config.get('NSUserTrackingUsageDescription'), False)
                elif channel_name == "gdt":
                    add_string_to_info_plist(info_plist_path, 'TYGDTActionSetId', channel_config.get('TYGDTActionSetId'), False)
                    add_string_to_info_plist(info_plist_path, 'TYGDTActionSecretKey', channel_config.get('TYGDTActionSecretKey'), False)
    configs = project.objects.get_configurations_on_targets(target_name=target_name)
    for config in configs:
        config.buildSettings['ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES'] = 'NO'
    project.save()
    # 处理pod文件，并install
    podfile_path = os.path.join(project_path, "Podfile")
    add_dependencies_set(build_config.get('tuyooParam').get('podDependencies')) 
    process_podfile(podfile_path, app_target_name, target_name, miniversion, dependencies)
    install_podfile(project_path)
    if app_target_name and app_target_name != target_name:
        copy_bundle_to_main_projet(project, project_path)
        add_file_to_embed_frameworks(project, project_path)
        install_podfile(project_path)

if __name__ == "__main__":
    print(f'✅ input info {argparse}')

    parser = argparse.ArgumentParser(description="Process some integers.")
    parser.add_argument('--configFile', type=str, required=True, help='build config json path')
    parser.add_argument('--workspace', type=str, required=True, help='export ipa project path')
    parser.add_argument('--minos', type=str, required=True, help='miniversion')
    parser.add_argument('--targetName', type=str, help='target_name')
    args = parser.parse_args()
    print(f"args: {args}")
    if args.configFile:
        build_config_json_path = args.configFile
    else:
        print('❌ config json path not set')
        exit(1)

    if args.workspace:
        export_ipa_project_path = args.workspace
    if not export_ipa_project_path or not os.path.isdir(export_ipa_project_path):
        print('❌ project path not set')
        exit(1)
    xcodeproj_path = ''
    project_name = ''
    for file in os.listdir(export_ipa_project_path):
        xcodeproj_path = os.path.join(export_ipa_project_path, file)
        if os.path.exists(xcodeproj_path) and xcodeproj_path.endswith('.xcodeproj'):
            project_name = os.path.basename(xcodeproj_path)
            print(f"✅ project 文件存在: {project_name} - {xcodeproj_path}")
            break
    
    if args.minos:
        miniversion = args.minos
    if args.targetName:
        target_name = args.targetName

    if not miniversion:
        miniversion = IOSMINIVERSION
    if not target_name:
        target_name = target_name = os.path.splitext(project_name)[0]

    print(f"✅ 打包基础信息:\n build_config_json_path - {build_config_json_path} \n export_ipa_project_path - {export_ipa_project_path} \n project_name - {project_name} \n miniversion - {miniversion} \n target_name - {target_name}")
    if os.path.exists(xcodeproj_path) and project_name != '':
        # 解析配置信息
        costom_plist_file_path = os.path.join(export_ipa_project_path, 'EngineSdkBaseInfo.plist')
        parse_build_config_json(build_config_json_path, costom_plist_file_path)
        # SDK+渠道配置
        process_project_setting(export_ipa_project_path, project_name, miniversion, target_name)
    else:
        print('❌ xcodeproj file not exist')
        exit(1)