import json
import os

import portalocker
from apps.core.services.django_sys import uWSGI_Reload  # 统一使用跨平台锁库
from django.apps import apps
from django.conf import settings
from django.db import close_old_connections
from django.db.models.fields.files import ImageFieldFile
from django.db.utils import OperationalError, ProgrammingError
from django.utils.functional import Promise

CONFIG_PATH = settings.BASE_DIR / "config/config.json"


def update_config(model_name: str, data: dict):
    CONFIG_PATH.parent.mkdir(exist_ok=True)

    # 处理不可JSON序列化的数据类型
    def serialize_value(value):
        if isinstance(value, Promise):  # 检测惰性翻译字符串
            return str(value)  # 转换为普通字符串
        elif isinstance(value, ImageFieldFile):  # 处理 ImageFieldFile 对象
            if value:  # 检查是否有文件
                return str(value)  # 或者使用 value.name 获取相对路径
            return None  # 如果没有文件，返回 None
        elif hasattr(value, "id"):
            return value.id
        # 添加其他需要特殊处理的类型
        return value

    processed_data = {}
    for key, value in data.items():
        processed_data[key] = serialize_value(value)

    try:
        with open(CONFIG_PATH, "r+", encoding="utf-8") as f:
            portalocker.lock(f, portalocker.LOCK_EX | portalocker.LOCK_NB)
            content = f.read().strip()
            config = json.loads(content) if content else {}
            config[model_name] = processed_data
            f.seek(0)
            json.dump(config, f, indent=2, ensure_ascii=False)  # 添加 ensure_ascii=False
            f.truncate()
    except (portalocker.LockException, FileNotFoundError, json.JSONDecodeError):
        with open(CONFIG_PATH, "w", encoding="utf-8") as f:
            json.dump({model_name: processed_data}, f, indent=2, ensure_ascii=False)


def is_config_valid():
    """
    检查config.json文件是否有效。
    有效性标准：检查通过post_save信号连接的特定模型配置是否存在。
    """
    config_path = settings.BASE_DIR / "config" / "config.json"

    if not config_path.exists():
        return False

    try:
        with open(config_path, "r", encoding="utf-8") as f:
            config_data = json.load(f)

        # 定义需要检查的模型名称列表（与post_save信号连接的模型一致）
        required_models = [
            "AuxiliaryConfig",
            "DebugSetting",
            "AdminSettings",
            "EmailSetting",
            "UrlStyle",
            "AiConfig",  # 确保这个在列表中
            "RateLimitConfig",
            "CacheConfig",
            "Theme",
            "SiteSettings",
        ]

        # 检查每个必需的模型是否都在配置数据中
        for model_name in required_models:
            if model_name not in config_data:
                print(f"配置有效性检查失败: 缺少 {model_name} 的配置")
                return False

        # 可选：检查每个模型配置是否不是空对象
        for model_name in required_models:
            if not config_data[model_name]:  # 检查是否为空字典或空值
                print(f"配置有效性检查警告: {model_name} 的配置为空")
                # 这里你可以决定空配置是否意味着无效，或者只是警告
                # return False  # 如果认为空配置也无效，则取消注释这行

        return True

    except (json.JSONDecodeError, IOError) as e:
        print(f"配置有效性检查失败: 文件读取或JSON解析错误 - {e}")
        return False


def initialize_configurations():
    """初始化所有单例配置模型"""
    """自动初始化core应用下所有单例配置模型"""
    try:
        # 获取core应用的所有模型
        core_app = apps.get_app_config("core")
        core_models = core_app.get_models()
        from apps.core.models.common import ConfigCommonModel

        # 过滤继承自ConfigCommonModel的模型（假设这是单例基类）
        config_models = [
            model
            for model in core_models
            if (
                issubclass(model, ConfigCommonModel)
                and not model._meta.abstract
                and getattr(model._meta, "managed", True) is not False
            )  # 关键检查：排除 managed=False 的模型 \ 排除基类本身
        ]
    except LookupError:
        # 处理core应用未找到的情况
        print("Core应用未注册，无法自动发现配置模型")
        config_models = []

    # 初始化日志输出
    print(f"发现{len(config_models)}个配置模型：{[m.__name__ for m in config_models]}")

    # 添加标志变量，用于控制错误提示频率
    db_not_ready_reported = False
    other_errors_reported = set()  # 用于记录其他类型的错误，避免重复提示

    for model in config_models:
        try:
            # 强制创建单例实例并加载到缓存
            obj = model.get_solo()  # 自动创建默认实例
            obj.save()  # 显式触发保存信号

            # 确保数据库连接释放
            close_old_connections()

        except (OperationalError, ProgrammingError) as e:
            # 处理数据库未就绪（首次迁移时可能发生）
            if not db_not_ready_reported:
                print(f"数据库未就绪，跳过所有配置模型初始化")
                db_not_ready_reported = True  # 标记已提示
            continue  # 跳过当前模型，继续下一个
        except Exception as e:
            # 对于其他异常，按类型去重提示
            error_type = type(e).__name__
            if error_type not in other_errors_reported:
                print(f"初始化配置模型时出现{error_type}错误: {str(e)}")
                other_errors_reported.add(error_type)
            continue

    # 可选：在循环结束后添加总结信息
    if db_not_ready_reported:
        print("提示：数据库连接失败，请检查数据库配置和服务状态")


# 初始化检测和补充uwsgi.ini配置的脚本


def check_and_patch_uwsgi_ini():
    """
    检测并自动修补uwsgi.ini配置文件
    确保包含py-autoreload和touch-reload配置项
    """
    # 环境检测：非Linux或非uWSGI运行时，直接跳过
    if not is_uwsgi_environment():
        print("ℹ️  当前非Linux或非uWSGI运行环境，跳过uwsgi.ini检查。")
        return True
    # 构建uwsgi.ini文件的完整路径
    uwsgi_ini_path = os.path.join(settings.BASE_DIR, "uwsgi.ini")

    # 如果uwsgi.ini文件不存在，则直接返回（或者你可以选择创建它）
    if not os.path.exists(uwsgi_ini_path):
        print(f"⚠️  uwsgi.ini文件不存在: {uwsgi_ini_path}")
        return False

    # 读取文件内容
    with open(uwsgi_ini_path, "r", encoding="utf-8") as f:
        content = f.read()

    modified = False
    new_content = content

    # 检查并添加py-autoreload配置
    if "py-autoreload" not in content:
        py_autoreload_str = "\n\n# 修改py文件时自动重启\npy-autoreload = 1"
        new_content += py_autoreload_str
        modified = True
        print("✅ 已添加py-autoreload配置")

    # 检查并添加touch-reload配置
    if "touch-reload" not in content:
        # 确保config目录存在
        config_dir = os.path.join(settings.BASE_DIR, "config")
        restart_file = os.path.join(config_dir, "restart.py")

        # 如果目录不存在则创建
        if not os.path.exists(config_dir):
            os.makedirs(config_dir)
            print(f"✅ 创建配置目录: {config_dir}")

        # 如果restart.py文件不存在则创建空文件
        if not os.path.exists(restart_file):
            with open(restart_file, "w") as f:
                f.write("# uWSGI touch-reload文件，修改此文件会触发uWSGI重启\n")
            print(f"✅ 创建touch-reload文件: {restart_file}")

        touch_reload_str = f"\n\n# 项目设置重启容灾文件\ntouch-reload = {restart_file}"
        new_content += touch_reload_str
        modified = True
        print("✅ 已添加touch-reload配置")
    # 检查并添加max-requests配置
    if "max-requests" not in content:
        max_requests_str = (
            "\n\n# 防止内存泄漏，每处理5000个请求后重启worker进程\nmax-requests = 5000"
        )
        new_content += max_requests_str
        modified = True
        print("✅ 已添加max-requests配置")

    # 检查并添加max-requests-delta配置
    if "max-requests-delta" not in content:
        max_requests_delta_str = (
            "\n\n# 防止所有worker进程同时重启，设置重启请求数浮动范围\nmax-requests-delta = 500"
        )
        new_content += max_requests_delta_str
        modified = True
        print("✅ 已添加max-requests-delta配置")
    if "lazy-apps" not in content:
        lazy_apps_str = (
            "\n\n# 启用惰性加载，每个工作进程独立加载应用以优化内存使用\nlazy-apps = true"
        )
        new_content += lazy_apps_str
        modified = True
        print("✅ 已添加lazy-apps配置（惰性加载应用）")
    print(
        "azy-apps配置（惰性加载应用）开始测试，启动项目后，每个进程首次进入项目会加载应用，比以前慢点，跑起来就和以前一样的快了。如果不喜欢可以在uSWGI中注销或删除lazy-apps = true"
    )
    # 如果内容有修改，则写回文件
    if modified:
        with open(uwsgi_ini_path, "w", encoding="utf-8") as f:
            f.write(new_content)
        print("✅ uwsgi.ini文件已更新，马上重项目项目，以确定文件正确加载")
        uWSGI_Reload()
    else:
        print("✅ uwsgi.ini配置检查完成，无需修改")

    return modified


def is_uwsgi_environment():
    """
    判断当前是否是需要检测uwsgi.ini的环境
    """
    # 条件1: 检查操作系统 - uWSGI通常用于类Unix环境[7,8](@ref)
    if os.name == "nt":  # Windows系统
        return False

    # 条件2: 检查是否在uWSGI运行时内（重要）[9](@ref)
    # 判断是否在uWSGI进程中运行，例如通过环境变量`UWSGI_ORIGINAL_PROC_NAME`或`UWSGI_PROCNAME`[9](@ref)
    if "UWSGI_" in os.environ.get("UWSGI_ORIGINAL_PROC_NAME", "") or "uwsgi" in os.environ.get(
        "_", ""
    ):
        return True

    # 条件3: 可选 - 检查是否存在uwsgi命令行工具（这表示系统可能配置了uWSGI）
    # 注意：这仅表示环境“可能”用于uWSGI部署，但不是绝对可靠的指标[1,6](@ref)
    if is_command_available("uwsgi"):
        return True

    # 如果以上uWSGI特有的条件都不满足，则认为不是目标环境
    return False


def is_command_available(command_name):
    """
    检查一个命令是否在系统PATH中存在
    """
    for path in os.environ.get("PATH", "").split(os.pathsep):
        cmd_path = os.path.join(path, command_name)
        if os.path.isfile(cmd_path) and os.access(cmd_path, os.X_OK):
            return True
    return False
