import time

import elasticsearch.client
from django.conf import settings
from elasticsearch_dsl import Document, InnerDoc, Date, Integer, Long, Text, Object, GeoPoint, Keyword, Boolean
from elasticsearch_dsl.connections import connections

from blog.models import Article

# 检查是否启用了Elasticsearch配置
ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL')

if ELASTICSEARCH_ENABLED:
    # 创建Elasticsearch连接
    connections.create_connection(
        hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
    from elasticsearch import Elasticsearch

    # 初始化Elasticsearch客户端
    es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
    from elasticsearch.client import IngestClient

    # 创建Ingest管道客户端，用于数据处理管道
    c = IngestClient(es)
    try:
        # 检查是否已存在geoip管道
        c.get_pipeline('geoip')
    except elasticsearch.exceptions.NotFoundError:
        # 如果不存在，创建geoip管道用于IP地理位置解析
        c.put_pipeline('geoip', body='''{
              "description" : "Add geoip info",
              "processors" : [
                {
                  "geoip" : {
                    "field" : "ip"
                  }
                }
              ]
            }''')


class GeoIp(InnerDoc):
    """IP地理位置信息内嵌文档"""
    continent_name = Keyword()  # 大洲名称
    country_iso_code = Keyword()  # 国家ISO代码
    country_name = Keyword()  # 国家名称
    location = GeoPoint()  # 地理位置坐标


class UserAgentBrowser(InnerDoc):
    """用户代理浏览器信息"""
    Family = Keyword()  # 浏览器家族
    Version = Keyword()  # 浏览器版本


class UserAgentOS(UserAgentBrowser):
    """用户代理操作系统信息"""
    pass  # 继承自UserAgentBrowser，具有相同的字段结构


class UserAgentDevice(InnerDoc):
    """用户代理设备信息"""
    Family = Keyword()  # 设备家族
    Brand = Keyword()  # 设备品牌
    Model = Keyword()  # 设备型号


class UserAgent(InnerDoc):
    """完整的用户代理信息"""
    browser = Object(UserAgentBrowser, required=False)  # 浏览器信息对象
    os = Object(UserAgentOS, required=False)  # 操作系统信息对象
    device = Object(UserAgentDevice, required=False)  # 设备信息对象
    string = Text()  # 原始用户代理字符串
    is_bot = Boolean()  # 是否为爬虫/机器人


class ElapsedTimeDocument(Document):
    """
    性能监控文档 - 用于记录请求响应时间等性能数据

    这个文档类型用于存储网站性能监控数据，包括：
    - 请求URL和响应时间
    - 用户IP和地理位置
    - 用户代理信息
    """
    url = Keyword()  # 请求的URL
    time_taken = Long()  # 请求耗时（毫秒）
    log_datetime = Date()  # 日志时间
    ip = Keyword()  # 用户IP地址
    geoip = Object(GeoIp, required=False)  # IP地理位置信息
    useragent = Object(UserAgent, required=False)  # 用户代理信息

    class Index:
        """索引配置"""
        name = 'performance'  # 索引名称
        settings = {
            "number_of_shards": 1,  # 分片数量
            "number_of_replicas": 0  # 副本数量
        }

    class Meta:
        doc_type = 'ElapsedTime'  # 文档类型


class ElaspedTimeDocumentManager:
    """性能监控文档管理器"""

    @staticmethod
    def build_index():
        """创建性能监控索引"""
        from elasticsearch import Elasticsearch
        client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
        res = client.indices.exists(index="performance")
        if not res:
            ElapsedTimeDocument.init()  # 初始化索引映射

    @staticmethod
    def delete_index():
        """删除性能监控索引"""
        from elasticsearch import Elasticsearch
        es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
        es.indices.delete(index='performance', ignore=[400, 404])  # 忽略404错误

    @staticmethod
    def create(url, time_taken, log_datetime, useragent, ip):
        """
        创建性能监控记录

        Args:
            url: 请求URL
            time_taken: 请求耗时
            log_datetime: 日志时间
            useragent: 用户代理对象
            ip: 用户IP地址
        """
        ElaspedTimeDocumentManager.build_index()

        # 构建用户代理信息
        ua = UserAgent()
        ua.browser = UserAgentBrowser()
        ua.browser.Family = useragent.browser.family  # 浏览器家族
        ua.browser.Version = useragent.browser.version_string  # 浏览器版本

        ua.os = UserAgentOS()
        ua.os.Family = useragent.os.family  # 操作系统家族
        ua.os.Version = useragent.os.version_string  # 操作系统版本

        ua.device = UserAgentDevice()
        ua.device.Family = useragent.device.family  # 设备家族
        ua.device.Brand = useragent.device.brand  # 设备品牌
        ua.device.Model = useragent.device.model  # 设备型号
        ua.string = useragent.ua_string  # 原始UA字符串
        ua.is_bot = useragent.is_bot  # 是否为机器人

        # 创建文档并使用geoip管道处理IP地理位置
        doc = ElapsedTimeDocument(
            meta={
                'id': int(round(time.time() * 1000))  # 使用时间戳作为文档ID
            },
            url=url,
            time_taken=time_taken,
            log_datetime=log_datetime,
            useragent=ua,
            ip=ip
        )
        doc.save(pipeline="geoip")  # 使用geoip管道自动添加地理位置信息


class ArticleDocument(Document):
    """
    文章搜索文档 - 用于Elasticsearch全文搜索

    这个文档类型定义了文章在Elasticsearch中的索引结构，
    支持对文章标题、内容、作者、分类、标签等进行全文搜索。
    """
    body = Text(analyzer='ik_max_word', search_analyzer='ik_smart')  # 文章内容，使用IK中文分词器
    title = Text(analyzer='ik_max_word', search_analyzer='ik_smart')  # 文章标题，使用IK中文分词器
    author = Object(properties={
        'nickname': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),  # 作者昵称
        'id': Integer()  # 作者ID
    })
    category = Object(properties={
        'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),  # 分类名称
        'id': Integer()  # 分类ID
    })
    tags = Object(properties={
        'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),  # 标签名称
        'id': Integer()  # 标签ID
    })

    # 文章元数据字段
    pub_time = Date()  # 发布时间
    status = Text()  # 文章状态（发布/草稿）
    comment_status = Text()  # 评论状态（开启/关闭）
    type = Text()  # 文章类型（文章/页面）
    views = Integer()  # 浏览次数
    article_order = Integer()  # 文章排序

    class Index:
        """索引配置"""
        name = 'blog'  # 索引名称
        settings = {
            "number_of_shards": 1,  # 分片数量
            "number_of_replicas": 0  # 副本数量
        }

    class Meta:
        doc_type = 'Article'  # 文档类型


class ArticleDocumentManager():
    """文章文档管理器 - 负责文章搜索索引的创建、更新和管理"""

    def __init__(self):
        self.create_index()

    def create_index(self):
        """创建文章搜索索引"""
        ArticleDocument.init()

    def delete_index(self):
        """删除文章搜索索引"""
        from elasticsearch import Elasticsearch
        es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
        es.indices.delete(index='blog', ignore=[400, 404])  # 忽略404错误

    def convert_to_doc(self, articles):
        """
        将Django文章对象转换为Elasticsearch文档对象

        Args:
            articles: Django文章查询集

        Returns:
            list: Elasticsearch文档对象列表
        """
        return [
            ArticleDocument(
                meta={'id': article.id},  # 使用文章ID作为文档ID
                body=article.body,
                title=article.title,
                author={
                    'nickname': article.author.username,
                    'id': article.author.id
                },
                category={
                    'name': article.category.name,
                    'id': article.category.id
                },
                tags=[
                    {'name': t.name, 'id': t.id} for t in article.tags.all()  # 转换标签列表
                ],
                pub_time=article.pub_time,
                status=article.status,
                comment_status=article.comment_status,
                type=article.type,
                views=article.views,
                article_order=article.article_order
            ) for article in articles
        ]

    def rebuild(self, articles=None):
        """
        重建文章搜索索引

        Args:
            articles: 要索引的文章列表，如果为None则索引所有文章
        """
        ArticleDocument.init()  # 重新初始化索引
        articles = articles if articles else Article.objects.all()  # 获取所有文章或指定文章
        docs = self.convert_to_doc(articles)  # 转换为文档对象
        for doc in docs:
            doc.save()  # 保存到Elasticsearch

    def update_docs(self, docs):
        """
        更新文档索引

        Args:
            docs: 要更新的文档列表
        """
        for doc in docs:
            doc.save()  # 保存更新到Elasticsearch
