import os
from django.db import models
from django.conf import settings
import requests
from .common import ConfigCommonModel
from django.core.exceptions import ValidationError
from django.apps import apps
from apps.core.utils.get_models import autoload_user_apps


class UrlPush(ConfigCommonModel):

    baidu_is_active = models.BooleanField(default=False, verbose_name="是否开启推送")
    baidu_api = models.URLField(verbose_name="百度推送接口地址",
                                blank=True,
                                default="")
    # 文章改变时
    article_status = models.BooleanField(
        default=False,
        verbose_name="文章API发布时是否推送",
        help_text=
        '文章API发布指的不是用API上传文章,而是将文章的非发布状态改为发布的API(//host/api/update_status99).')
    # models save()时
    # 核心内容模型选择
    content_models = models.JSONField(verbose_name='内容模型',
                                      default=list,
                                      help_text='选择要包含的content应用模型')

    # 用户空间应用模型选择
    user_app_models = models.JSONField(verbose_name="用户空间模型",
                                       default=list,
                                       help_text='选择要包含的用户应用模型')

    class Meta:
        verbose_name = "URL推送配置"
        verbose_name_plural = verbose_name

    def __str__(self):
        return "URL推送配置"

    def save(self, *args, **kwargs):
        # 检查是否是新建对象且content_models字段为空列表
        if self.pk is None and self.content_models == []:
            self.content_models = ["content.article"]
        super().save(*args, **kwargs)

    def clean(self):
        """验证数据格式"""
        if self.baidu_is_active and self.baidu_api == '':
            raise ValidationError({'baidu_api': '当选择百度推送时,百度推送接口地址必须填写.'})

        # 验证模型选择格式
        for field in ['content_models', 'user_app_models']:
            value = getattr(self, field)
            if not isinstance(value, list):
                raise ValidationError({field: '必须为数组格式'})
            if any(not isinstance(item, str) for item in value):
                raise ValidationError({field: '包含非字符串元素'})

    @classmethod
    def push_baidu_url(cls, urls):
        """
        推送URL到百度搜索资源平台
        :param urls: 单个URL字符串或URL列表
        :return: (success: bool, result: dict)
        """
        instance = cls._instance or cls.get_solo()
        if not instance or not instance.baidu_is_active:
            return False, {"error": 403, "message": "推送功能未启用"}

        if isinstance(urls, str):
            urls = [urls]

        try:
            response = requests.post(
                instance.baidu_api,
                data="\n".join(urls),
                headers={"Content-Type": "text/plain"},
                timeout=5  # 默认5秒超时
            )

            if response.status_code == 200:
                result = response.json()
                return True, {
                    "success": result.get("success", 0),
                    "remain": result.get("remain", 0),
                    "invalid_urls": {
                        "not_same_site": result.get("not_same_site", []),
                        "not_valid": result.get("not_valid", [])
                    }
                }
            else:
                error_data = response.json()
                return False, {
                    "error": response.status_code,
                    "message": error_data.get("message", "未知错误")
                }

        except requests.exceptions.RequestException as e:
            return False, {"error": 500, "message": f"网络请求异常: {str(e)}"}
        except ValueError as e:
            return False, {"error": 500, "message": f"响应解析失败: {str(e)}"}

    @classmethod
    def push_url(cls, urls):
        '''
        urls:list
        '''
        data = {'baidu': cls.push_baidu_url(urls)}
        return data

    def check_baidu_push_status(self, push_status):
        """
        检查百度推送状态并生成报告
        参数: push_status(dict) - 包含推送状态的字典
        返回: 状态报告字符串
        """
        try:
            if not isinstance(push_status, dict) or 'baidu' not in push_status:
                return "未检测到百度推送状态信息"

            status, details = push_status['baidu']
            report = f"百度推送状态：{'成功' if status else '失败'};"

            # 错误状态处理
            if not status and isinstance(details, dict) and 'error' in details:
                error_map = {
                    400: "配额已用完",
                    401: "认证失败",
                    403: "权限不足",
                    404: "资源不存在"
                }
                error_code = details['error']
                return f"{report} 错误原因：{error_map.get(error_code, f'未知错误({error_code})')}"

            # 正常状态处理
            if isinstance(details, dict):
                report += f"- 成功推送数量：{details.get('success', 'N/A')};"
                report += f"- 剩余可推送额度：{details.get('remain', 'N/A')};"

                if 'invalid_urls' in details and isinstance(
                        details['invalid_urls'], dict):
                    invalid = details['invalid_urls']
                    report += "- 无效URL分析："
                    report += f"  - 非本站链接：{len(invalid.get('not_same_site', []))}条;"
                    report += f"  - 无效格式链接：{len(invalid.get('not_valid', []))}条"

            return report

        except Exception as e:
            return f"解析推送状态时出错：{str(e)}"

    @staticmethod
    def get_content_models_choices():
        """获取content应用的可用模型选项"""
        from django.apps import apps
        try:
            app_config = apps.get_app_config('content')
            return [(f"{model._meta.app_label}.{model._meta.model_name}",
                     model._meta.verbose_name)
                    for model in app_config.get_models()
                    if not model._meta.model_name == 'contentmanager']
        except LookupError:
            return []

    @staticmethod
    def get_user_space_choices():
        """获取用户空间应用的可用模型选项"""
        from django.apps import apps
        models = []
        for app_name in autoload_user_apps():
            try:
                app_config = apps.get_app_config(app_name.split('.')[-1])
                models.extend([
                    (f"{model._meta.app_label}.{model._meta.model_name}",
                     f"{app_config.verbose_name} - {model._meta.verbose_name}")
                    for model in app_config.get_models()
                ])
            except (LookupError, ImportError):
                continue
        return models

    def get_active_models(self):
        """
        获取合并后的模型列表
        返回: 包含所有选中模型类的列表
        """
        combined = []

        # 合并两个字段的模型标识
        all_models = self.content_models + self.user_app_models

        # 去重处理
        seen = set()
        unique_models = [
            x for x in all_models if not (x in seen or seen.add(x))
        ]

        # 转换为模型类
        for model_str in unique_models:
            try:
                app_label, model_name = model_str.split('.')
                model_class = apps.get_model(app_label, model_name)
                combined.append(model_class)
            except (ValueError, LookupError, AttributeError) as e:
                # 处理无效模型标识
                print(f"无效的模型标识 {model_str}: {str(e)}")
                continue

        return combined


# def autoload_user_apps():
#     """基于 settings.BASE_DIR 自动扫描用户应用"""
#     user_apps = []
#     base_dir = os.path.join(settings.BASE_DIR, "user_space")
#     apps_dir = os.path.join(base_dir, "apps")
#     os.makedirs(apps_dir, mode=0o755, exist_ok=True)

#     if not os.access(apps_dir, os.W_OK):
#         raise PermissionError(f"目录 {apps_dir} 不可写，请检查权限！")
#     print('os.listdir(apps_dir)=>', os.listdir(apps_dir))
#     for app_name in os.listdir(apps_dir):
#         app_path = os.path.join(apps_dir, app_name)
#         if (os.path.isdir(app_path)
#                 and os.path.exists(os.path.join(app_path, "__init__.py"))
#                 and os.path.exists(os.path.join(app_path, "apps.py"))):
#             user_apps.append(f"user_space.apps.{app_name}")
#     print(user_apps)
#     return user_apps
