"""
替换指定文件中的特定字符串
支持JSON文件、plist文件等多种格式
"""
import os
import re
import json
import plistlib
from ObjectiveC import oc_util
from ObjectiveC import oc_yaml


def remove_trailing_commas(json_string):
    """
    移除JSON字符串中的尾随逗号，使其符合标准JSON格式
    支持处理对象和数组中的尾随逗号
    """
    # 移除对象中的尾随逗号: ,\s*}
    json_string = re.sub(r',\s*}', '}', json_string)
    # 移除数组中的尾随逗号: ,\s*]
    json_string = re.sub(r',\s*]', ']', json_string)
    return json_string


def load_json_with_trailing_comma(file_path):
    """
    读取JSON文件，容忍尾随逗号
    """
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()
    # 先尝试直接解析
    try:
        return json.loads(content)
    except json.JSONDecodeError:
        # 如果失败，尝试移除尾随逗号后再解析
        cleaned_content = remove_trailing_commas(content)
        return json.loads(cleaned_content)


def replace_in_file(file_path, search_key, old_value, new_value):
    """
    在指定文件中查找并替换特定字符串

    参数:
        file_path (str): 文件路径
        search_key (str): 要查找的键名，如 'xxpk_startid'
        old_value (str): 要替换的旧值
        new_value (str): 新的值

    返回:
        bool: 操作成功返回 True，否则返回 False
    """
    try:
        # 获取文件扩展名
        _, ext = os.path.splitext(file_path)
        ext = ext.lower()

        # 根据文件类型选择不同的处理方式
        if ext == '.json':
            return replace_in_json_file(file_path, search_key, old_value, new_value)
        elif ext == '.plist':
            return replace_in_plist_file(file_path, search_key, old_value, new_value)
        else:
            # 对于其他文件类型，使用通用的文本替换方法
            return replace_in_text_file(file_path, search_key, old_value, new_value)

    except Exception as e:
        print(f"处理文件 {file_path} 时出错: {e}")
        return False


def replace_in_json_file(file_path, search_key, old_value, new_value):
    """
    在JSON文件中查找并替换特定键的值
    """
    try:
        # 读取JSON文件（容忍尾随逗号）
        data = load_json_with_trailing_comma(file_path)

        # 递归查找并替换键值
        modified = replace_in_json_object(
            data, search_key, old_value, new_value)

        # 如果有修改，写回文件
        if modified:
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=4)
            print(f"已更新JSON文件: {file_path}")
            return True

        return False

    except FileNotFoundError:
        print(f"错误: 找不到文件 {file_path}")
        print(f"请检查文件路径是否正确，以及文件是否存在")
        return False
    except Exception as e:
        print(f"处理JSON文件 {file_path} 时出错: {e}")
        return False


def replace_in_json_object(obj, search_key, old_value, new_value):
    """
    递归查找并替换JSON对象中的值
    """
    modified = False

    if isinstance(obj, dict):
        for key, value in obj.items():
            # 如果找到目标键且值匹配
            if key == search_key and value == old_value:
                obj[key] = new_value
                modified = True
                print(f"已替换: {key}: {old_value} -> {new_value}")
            # 递归处理嵌套对象
            elif isinstance(value, (dict, list)):
                if replace_in_json_object(value, search_key, old_value, new_value):
                    modified = True

    elif isinstance(obj, list):
        for i, item in enumerate(obj):
            if isinstance(item, (dict, list)):
                if replace_in_json_object(item, search_key, old_value, new_value):
                    modified = True

    return modified


def replace_in_plist_file(file_path, search_key, old_value, new_value):
    """
    在plist文件中查找并替换特定键的值
    """
    try:
        # 读取plist文件
        with open(file_path, 'rb') as f:
            data = plistlib.load(f)

        # 递归查找并替换键值
        modified = replace_in_plist_object(
            data, search_key, old_value, new_value)

        # 如果有修改，写回文件
        if modified:
            with open(file_path, 'wb') as f:
                plistlib.dump(data, f)
            print(f"已更新plist文件: {file_path}")
            return True

        return False

    except Exception as e:
        print(f"处理plist文件 {file_path} 时出错: {e}")
        return False


def replace_in_plist_object(obj, search_key, old_value, new_value):
    """
    递归查找并替换plist对象中的值
    """
    modified = False

    if isinstance(obj, dict):
        for key, value in obj.items():
            # 如果找到目标键且值匹配
            if key == search_key and value == old_value:
                obj[key] = new_value
                modified = True
                print(f"已替换: {key}: {old_value} -> {new_value}")
            # 递归处理嵌套对象
            elif isinstance(value, (dict, list)):
                if replace_in_plist_object(value, search_key, old_value, new_value):
                    modified = True

    elif isinstance(obj, list):
        for i, item in enumerate(obj):
            if isinstance(item, (dict, list)):
                if replace_in_plist_object(item, search_key, old_value, new_value):
                    modified = True

    return modified


def replace_in_text_file(file_path, search_key, old_value, new_value):
    """
    在文本文件中查找并替换特定模式的字符串
    """
    try:
        # 读取文件内容
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()

        # 构建搜索模式
        # 例如: "xxpk_startid"\s*:\s*"352e8711bcca025e07230a8402f03d09"
        pattern = rf'"{re.escape(search_key)}"\s*:\s*"{re.escape(old_value)}"'
        replacement = f'"{search_key}": "{new_value}"'

        # 执行替换
        new_content, count = re.subn(pattern, replacement, content)

        # 如果有替换，写回文件
        if count > 0:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(new_content)
            print(f"已在文件 {file_path} 中替换 {count} 处匹配项")
            return True

        return False

    except Exception as e:
        print(f"处理文本文件 {file_path} 时出错: {e}")
        return False


def is_valid_file(file_path):
    """
    检查文件是否应该被处理
    """
    # 获取文件扩展名
    _, ext = os.path.splitext(file_path)
    ext = ext.lower()

    # 检查是否在支持的文件类型列表中
    return (ext in oc_yaml.list_support_open_file_type) or (ext in ['.json', '.plist'])


def is_valid_dir(dir_name):
    """
    检查目录是否应该被处理
    """
    return (not dir_name.endswith('.xcodeproj')) and \
           (not dir_name.endswith('.framework')) and \
           (dir_name not in oc_yaml.list_folder_ignore_all)


def replace_in_project(search_key, old_value, new_value, project_path=None):
    """
    在整个项目中查找并替换特定字符串

    参数:
        search_key (str): 要查找的键名，如 'xxpk_startid'
        old_value (str): 要替换的旧值
        new_value (str): 新的值
        project_path (str, optional): 项目路径，默认使用 oc_util.path_mix_project

    返回:
        int: 成功替换的文件数量
    """
    if not project_path:
        project_path = oc_util.path_mix_project

    print(f"开始在项目中查找并替换: {search_key}: {old_value} -> {new_value}")

    # 记录成功替换的文件数量
    success_count = 0

    # 遍历项目文件
    for root, dirs, files in os.walk(project_path, topdown=True):
        # 过滤目录
        dirs[:] = [d for d in dirs if is_valid_dir(d)]

        # 过滤文件
        files[:] = [f for f in files if is_valid_file(os.path.join(root, f))]

        for name in files:
            file_path = os.path.join(root, name)
            if replace_in_file(file_path, search_key, old_value, new_value):
                success_count += 1

    print(f"替换完成，共处理 {success_count} 个文件")
    return success_count


def replace_text_directly(file_path, old_text, new_text):
    """
    直接替换文件中的指定文本，不需要键名

    参数:
        file_path (str): 文件路径
        old_text (str): 要替换的旧文本
        new_text (str): 新的文本

    返回:
        bool: 操作成功返回 True，否则返回 False
    """
    try:
        # 读取文件内容
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()

        # 检查文件是否包含旧文本
        if old_text in content:
            # 执行替换
            new_content = content.replace(old_text, new_text)

            # 写回文件
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(new_content)

            print(f"已在文件 {file_path} 中直接替换文本: {old_text} -> {new_text}")
            return True

        return False

    except Exception as e:
        print(f"处理文件 {file_path} 时出错: {e}")
        return False


def replace_text_in_project(old_text, new_text, project_path=None):
    """
    在整个项目中直接替换指定文本

    参数:
        old_text (str): 要替换的旧文本
        new_text (str): 新的文本
        project_path (str, optional): 项目路径，默认使用 oc_util.path_mix_project

    返回:
        int: 成功替换的文件数量
    """
    if not project_path:
        project_path = oc_util.path_mix_project

    print(f"开始在项目中直接替换文本: {old_text} -> {new_text}")

    # 记录成功替换的文件数量
    success_count = 0

    # 遍历项目文件
    for root, dirs, files in os.walk(project_path, topdown=True):
        # 过滤目录
        dirs[:] = [d for d in dirs if is_valid_dir(d)]

        # 过滤文件
        files[:] = [f for f in files if is_valid_file(os.path.join(root, f))]

        for name in files:
            file_path = os.path.join(root, name)
            if replace_text_directly(file_path, old_text, new_text):
                success_count += 1

    print(f"替换完成，共处理 {success_count} 个文件")
    return success_count


def replace_key_in_file(file_path, search_key, new_value):
    """
    在指定文件中查找键名并替换其值

    参数:
        file_path (str): 文件路径
        search_key (str): 要查找的键名，如 'xxpk_startid'
        new_value (str): 新的值

    返回:
        bool: 操作成功返回 True，否则返回 False
    """
    try:
        # 获取文件扩展名
        _, ext = os.path.splitext(file_path)
        ext = ext.lower()

        # 根据文件类型选择不同的处理方式
        if ext == '.json':
            return replace_key_in_json_file(file_path, search_key, new_value)
        elif ext == '.plist':
            return replace_key_in_plist_file(file_path, search_key, new_value)
        else:
            # 对于其他文件类型，使用通用的文本替换方法
            return replace_key_in_text_file(file_path, search_key, new_value)

    except Exception as e:
        print(f"处理文件 {file_path} 时出错: {e}")
        return False


def replace_key_in_json_file(file_path, search_key, new_value):
    """
    在JSON文件中查找键名并替换其值
    """
    try:
        # 读取JSON文件（容忍尾随逗号）
        data = load_json_with_trailing_comma(file_path)

        # 递归查找并替换键值
        modified, _ = replace_key_in_json_object(data, search_key, new_value)

        # 如果有修改，写回文件
        if modified:
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=4)
            print(f"已更新JSON文件: {file_path}")
            return True

        return False

    except Exception as e:
        print(f"处理JSON文件 {file_path} 时出错: {e}")
        return False


def replace_key_in_json_object(obj, search_key, new_value):
    """
    递归查找并替换JSON对象中的键值

    返回:
        (bool, str): (是否修改, 旧值)
    """
    modified = False
    old_value = None

    if isinstance(obj, dict):
        for key, value in obj.items():
            # 如果找到目标键
            if key == search_key:
                old_value = value
                obj[key] = new_value
                modified = True
                print(f"已替换: {key}: {old_value} -> {new_value}")
            # 递归处理嵌套对象
            elif isinstance(value, (dict, list)):
                sub_modified, sub_old_value = replace_key_in_json_object(
                    value, search_key, new_value)
                if sub_modified:
                    modified = True
                    old_value = sub_old_value

    elif isinstance(obj, list):
        for i, item in enumerate(obj):
            if isinstance(item, (dict, list)):
                sub_modified, sub_old_value = replace_key_in_json_object(
                    item, search_key, new_value)
                if sub_modified:
                    modified = True
                    old_value = sub_old_value

    return modified, old_value


def replace_key_in_plist_file(file_path, search_key, new_value):
    """
    在plist文件中查找键名并替换其值
    """
    try:
        # 读取plist文件
        with open(file_path, 'rb') as f:
            data = plistlib.load(f)

        # 递归查找并替换键值
        modified, _ = replace_key_in_plist_object(data, search_key, new_value)

        # 如果有修改，写回文件
        if modified:
            with open(file_path, 'wb') as f:
                plistlib.dump(data, f)
            print(f"已更新plist文件: {file_path}")
            return True

        return False

    except Exception as e:
        print(f"处理plist文件 {file_path} 时出错: {e}")
        return False


def replace_key_in_plist_object(obj, search_key, new_value):
    """
    递归查找并替换plist对象中的键值

    返回:
        (bool, str): (是否修改, 旧值)
    """
    modified = False
    old_value = None

    if isinstance(obj, dict):
        for key, value in obj.items():
            # 如果找到目标键
            if key == search_key:
                old_value = value
                obj[key] = new_value
                modified = True
                print(f"已替换: {key}: {old_value} -> {new_value}")
            # 递归处理嵌套对象
            elif isinstance(value, (dict, list)):
                sub_modified, sub_old_value = replace_key_in_plist_object(
                    value, search_key, new_value)
                if sub_modified:
                    modified = True
                    old_value = sub_old_value

    elif isinstance(obj, list):
        for i, item in enumerate(obj):
            if isinstance(item, (dict, list)):
                sub_modified, sub_old_value = replace_key_in_plist_object(
                    item, search_key, new_value)
                if sub_modified:
                    modified = True
                    old_value = sub_old_value

    return modified, old_value


def replace_key_in_text_file(file_path, search_key, new_value):
    """
    在文本文件中查找键名并替换其值
    """
    try:
        # 读取文件内容
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()

        # 构建搜索模式，匹配 "xxpk_startid": "任何值"
        pattern = rf'("{re.escape(search_key)}"\s*:\s*")([^"]*)(")'

        # 定义替换函数，保留匹配的第一部分和第三部分，替换第二部分
        def replace_func(match):
            return f'{match.group(1)}{new_value}{match.group(3)}'

        # 执行替换
        new_content, count = re.subn(pattern, replace_func, content)

        # 如果有替换，写回文件
        if count > 0:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(new_content)
            print(f"已在文件 {file_path} 中替换 {count} 处匹配项")
            return True

        return False

    except Exception as e:
        print(f"处理文本文件 {file_path} 时出错: {e}")
        return False


def replace_key_in_project(search_key, new_value, project_path=None):
    """
    在整个项目中查找键名并替换其值

    参数:
        search_key (str): 要查找的键名，如 'xxpk_startid'
        new_value (str): 新的值
        project_path (str, optional): 项目路径，默认使用 oc_util.path_mix_project

    返回:
        int: 成功替换的文件数量
    """
    if not project_path:
        project_path = oc_util.path_mix_project

    print(f"开始在项目中查找键 {search_key} 并替换为新值: {new_value}")

    # 记录成功替换的文件数量
    success_count = 0

    # 遍历项目文件
    for root, dirs, files in os.walk(project_path, topdown=True):
        # 过滤目录
        dirs[:] = [d for d in dirs if is_valid_dir(d)]

        # 过滤文件
        files[:] = [f for f in files if is_valid_file(os.path.join(root, f))]

        for name in files:
            file_path = os.path.join(root, name)
            if replace_key_in_file(file_path, search_key, new_value):
                success_count += 1

    print(f"替换完成，共处理 {success_count} 个文件")
    return success_count


def main():
    """
    主函数，用于命令行调用

    用法示例:
    1. 替换指定文件中的特定字符串:
       python -m ObjectiveC.oc_custom.custom_replace file /path/to/file.json xxpk_startid 352e8711bcca025e07230a8402f03d09 new_value

    2. 在整个项目中查找并替换:
       python -m ObjectiveC.oc_custom.custom_replace project xxpk_startid 352e8711bcca025e07230a8402f03d09 new_value

    3. 直接替换指定文件中的文本:
       python -m ObjectiveC.oc_custom.custom_replace text_file /path/to/file.txt 352e8711bcca025e07230a8402f03d09 new_value

    4. 在整个项目中直接替换文本:
       python -m ObjectiveC.oc_custom.custom_replace text_project 352e8711bcca025e07230a8402f03d09 new_value

    5. 替换指定文件中的键值(不需要提供旧值):
       python -m ObjectiveC.oc_custom.custom_replace key_file /path/to/file.json xxpk_startid new_value

    6. 在整个项目中查找键并替换其值(不需要提供旧值):
       python -m ObjectiveC.oc_custom.custom_replace key_project xxpk_startid new_value
    """
    import sys

    if len(sys.argv) < 2:
        print("用法:")
        print("1. 替换指定文件中的特定字符串:")
        print("   python -m ObjectiveC.oc_custom.custom_replace file <文件路径> <键名> <旧值> <新值>")
        print("2. 在整个项目中查找并替换:")
        print("   python -m ObjectiveC.oc_custom.custom_replace project <键名> <旧值> <新值>")
        print("3. 直接替换指定文件中的文本:")
        print(
            "   python -m ObjectiveC.oc_custom.custom_replace text_file <文件路径> <旧文本> <新文本>")
        print("4. 在整个项目中直接替换文本:")
        print("   python -m ObjectiveC.oc_custom.custom_replace text_project <旧文本> <新文本>")
        print("5. 替换指定文件中的键值(不需要提供旧值):")
        print("   python -m ObjectiveC.oc_custom.custom_replace key_file <文件路径> <键名> <新值>")
        print("6. 在整个项目中查找键并替换其值(不需要提供旧值):")
        print("   python -m ObjectiveC.oc_custom.custom_replace key_project <键名> <新值>")
        return

    mode = sys.argv[1]

    if mode == "file":
        if len(sys.argv) < 6:
            print("替换指定文件需要提供: <文件路径> <键名> <旧值> <新值>")
            return

        file_path = sys.argv[2]
        search_key = sys.argv[3]
        old_value = sys.argv[4]
        new_value = sys.argv[5]

        replace_in_file(file_path, search_key, old_value, new_value)

    elif mode == "project":
        if len(sys.argv) < 5:
            print("项目替换需要提供: <键名> <旧值> <新值>")
            return

        search_key = sys.argv[2]
        old_value = sys.argv[3]
        new_value = sys.argv[4]

        replace_in_project(search_key, old_value, new_value)

    elif mode == "text_file":
        if len(sys.argv) < 5:
            print("直接替换文本需要提供: <文件路径> <旧文本> <新文本>")
            return

        file_path = sys.argv[2]
        old_text = sys.argv[3]
        new_text = sys.argv[4]

        replace_text_directly(file_path, old_text, new_text)

    elif mode == "text_project":
        if len(sys.argv) < 4:
            print("项目中直接替换文本需要提供: <旧文本> <新文本>")
            return

        old_text = sys.argv[2]
        new_text = sys.argv[3]

        replace_text_in_project(old_text, new_text)

    elif mode == "key_file":
        if len(sys.argv) < 5:
            print("替换指定文件中的键值需要提供: <文件路径> <键名> <新值>")
            return

        file_path = sys.argv[2]
        search_key = sys.argv[3]
        new_value = sys.argv[4]

        replace_key_in_file(file_path, search_key, new_value)

    elif mode == "key_project":
        if len(sys.argv) < 4:
            print("项目中查找键并替换其值需要提供: <键名> <新值>")
            return

        search_key = sys.argv[2]
        new_value = sys.argv[3]

        replace_key_in_project(search_key, new_value)

    else:
        print(f"未知模式: {mode}")
        print("可用模式: file, project, text_file, text_project, key_file, key_project")


def replace_pattern_in_content(content, pattern, replacement):
    """
    在内容中使用正则表达式替换匹配的模式

    参数:
        content (str): 要处理的内容
        pattern (str): 正则表达式模式
        replacement (str): 替换的内容

    返回:
        str: 替换后的内容
    """
    try:
        # 使用正则表达式进行替换
        new_content = re.sub(pattern, replacement, content)
        return new_content
    except Exception as e:
        print(f"正则表达式替换时出错: {e}")
        return content


def replace_multiple_patterns_in_content(content, pattern_replacements):
    """
    在内容中使用多个正则表达式模式进行替换

    参数:
        content (str): 要处理的内容
        pattern_replacements (list): 包含 (pattern, replacement) 元组的列表

    返回:
        str: 替换后的内容
    """
    try:
        result_content = content
        for pattern, replacement in pattern_replacements:
            result_content = re.sub(pattern, replacement, result_content)
        return result_content
    except Exception as e:
        print(f"多模式正则表达式替换时出错: {e}")
        return content


def get_key_value_from_file(file_path, search_key):
    """
    从文件中获取键值
    """
    try:
        # 获取文件扩展名
        _, ext = os.path.splitext(file_path)
        ext = ext.lower()

        # 根据文件类型选择不同的处理方式
        if ext == '.json':
            return get_key_value_from_json_file(file_path, search_key)
        elif ext == '.plist':
            return get_key_value_from_plist_file(file_path, search_key)
        else:
            # 对于其他文件类型，使用通用的文本处理方法
            return get_key_value_from_text_file(file_path, search_key)

    except Exception as e:
        print(f"处理文件 {file_path} 时出错: {e}")
        return None


def get_key_value_from_json_file(file_path, search_key):
    """
    从JSON文件中获取键值
    """
    try:
        # 读取JSON文件（容忍尾随逗号）
        data = load_json_with_trailing_comma(file_path)

        # 递归查找键值
        return get_key_value_from_json_object(data, search_key)

    except Exception as e:
        print(f"处理JSON文件 {file_path} 时出错: {e}")
        return None


def get_key_value_from_json_object(obj, search_key):
    """
    递归查找JSON对象中的键值
    """
    if isinstance(obj, dict):
        for key, value in obj.items():
            if key == search_key:
                return value
            elif isinstance(value, (dict, list)):
                result = get_key_value_from_json_object(value, search_key)
                if result is not None:
                    return result
    elif isinstance(obj, list):
        for item in obj:
            if isinstance(item, (dict, list)):
                result = get_key_value_from_json_object(item, search_key)
                if result is not None:
                    return result
    return None


def get_key_value_from_plist_file(file_path, search_key):
    """
    从plist文件中获取键值
    """
    try:
        # 读取plist文件
        with open(file_path, 'rb') as f:
            data = plistlib.load(f)

        # 递归查找键值
        return get_key_value_from_plist_object(data, search_key)

    except Exception as e:
        print(f"处理plist文件 {file_path} 时出错: {e}")
        return None


def get_key_value_from_plist_object(obj, search_key):
    """
    递归查找plist对象中的键值
    """
    if isinstance(obj, dict):
        for key, value in obj.items():
            if key == search_key:
                return value
            elif isinstance(value, (dict, list)):
                result = get_key_value_from_plist_object(value, search_key)
                if result is not None:
                    return result
    elif isinstance(obj, list):
        for item in obj:
            if isinstance(item, (dict, list)):
                result = get_key_value_from_plist_object(item, search_key)
                if result is not None:
                    return result
    return None


def get_key_value_from_text_file(file_path, search_key):
    """
    从文本文件中获取键值
    """
    try:
        # 读取文件内容
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()

        # 构建搜索模式，匹配 "xxpk_startid": "任何值"
        pattern = rf'"{re.escape(search_key)}"\s*:\s*"(.*?)"'

        # 查找匹配项
        match = re.search(pattern, content)
        if match:
            return match.group(1)

        return None

    except Exception as e:
        print(f"处理文本文件 {file_path} 时出错: {e}")
        return None


if __name__ == "__main__":
    main()
