import functools
import logging
import traceback
import redis
import json
from gm_rpcd.all import bind
from libs.cache import redis_client
from libs.algorithms import variousness, region_division
from libs.debug import pretty_json
from libs.es import es_query, get_highlight_query_analyzer, get_es, get_highlight_query
from django.conf import settings
from search.views.wikitab import es_indices_analyze, get_wordrel_set
from libs.error import logging_exception

logger = logging.getLogger(__name__)

MAX_LOAD = 200
GROUP_SIZE = 10
variousness_per_10 = functools.partial(variousness, variety_size=GROUP_SIZE)


def query_filter_comprehensive_tractate(query=None, have_read_tractate_list=[], use_fresh_tag=False,
                                        correct_term_list=[], other_key_word_term_list=[], offset=0, size=10,
                                        have_read_id_list=[], closure_tags=[]):
    try:
        multi_fields = {
            "content": 6,
            "author": 2
        }
        fields_term_list = ["content", "author"]

        ####获取优质作者的ID
        good_user_ids = []
        key = "good_tractate_user_id"
        redis_user_ids = redis_client.get(key)
        if redis_user_ids:
            good_user_ids = json.loads(redis_user_ids)

        if use_fresh_tag:
            multi_fields["fresh_tractate_tag_name"] = 3
            multi_fields["fresh_tractate_tag_name_content"] = 4
            fields_term_list.append("fresh_tractate_tag_name")
            fields_term_list.append("fresh_tractate_tag_name_content")
        else:
            multi_fields["tractate_tag_name"] = 3
            multi_fields["tractate_tag_name_content"] = 4
            fields_term_list.append("tractate_tag_name")
            fields_term_list.append("tractate_tag_name_content")

        fields_weight_list = ['^'.join((k, str(v))) for (k, v) in multi_fields.items()]

        total_match_query = {
            "multi_match": {
                "query": query,
                "fields": fields_weight_list,
                "analyzer": "gm_default_index",
                "operator": "and",
                "type": "best_fields"
            }
        }
        total_match_query_list = list()
        for field_name in fields_term_list:
            term_dict = {
                "terms": {
                    field_name: correct_term_list
                }
            }
            total_match_query_list.append(term_dict)

        total_match_query_list.append(total_match_query)

        if len(closure_tags) > 0 and use_fresh_tag:
            total_match_query_list.append({
                "terms": {
                    "fresh_tractate_tag_name": closure_tags
                }
            })
            total_match_query_list.append({
                "terms": {
                    "fresh_tractate_tag_name_content": closure_tags
                }
            })
        if len(closure_tags) > 0 and use_fresh_tag == False:
            total_match_query_list.append({
                "terms": {
                    "tractate_tag_name": closure_tags
                }
            })
            total_match_query_list.append({
                "terms": {
                    "tractate_tag_name_content": closure_tags
                }
            })
        other_key_query_list = list()
        if len(other_key_word_term_list) > 0:
            for field_name in fields_term_list:
                term_dict = {
                    "terms": {
                        field_name: other_key_word_term_list
                    }
                }
                other_key_query_list.append(term_dict)

            other_key_match_query = {
                "multi_match": {
                    "query": other_key_word_term_list[0],
                    "fields": fields_weight_list,
                    "analyzer": "gm_default_index",
                    "operator": "and",
                    "type": "best_fields"
                }
            }
            other_key_query_list.append(other_key_match_query)

        q = {
            "from": 0,
            "size": size,
            "query": {
                "function_score": {
                    "functions": [
                        {
                            "filter": {
                                "bool": {
                                    "should": total_match_query_list,
                                    "minimum_should_match": 1
                                }
                            },
                            "weight": 1000
                        }
                    ],
                    "boost_mode": "replace",
                    "score_mode": "max",
                    "query": {
                        "bool": {
                            "must": [{
                                "term": {
                                    "is_online": True
                                }
                            }],
                            "must_not": [{
                                "term": {
                                    "status": 4
                                }
                            }],
                            "should": [{
                                "multi_match": {
                                    "query": query,
                                    "fields": fields_weight_list,
                                    "analyzer": "gm_default_index",
                                    "operator": "or",
                                    "type": "best_fields"
                                }
                            }],
                            "minimum_should_match": 1,
                        }
                    }
                }
            },
            "sort": [
                {'_score': {'order': 'desc'}},
                {'tractate_score': {'order': 'desc'}}
            ],
            "highlight": get_highlight_query(["content"], query)
        }
        if len(good_user_ids) > 0:
            q["query"]["function_score"]["functions"].append({
                "filter": {
                    "bool": {
                        "must": [
                            {"terms": {"user_id": good_user_ids}},
                            {
                                "match_phrase": {
                                    "tractate_tag_name": {
                                        "query": query,
                                        "analyzer": "gm_default_index"
                                    }
                                }
                            }
                        ]
                    }
                },
                "weight": 100000
            })

            q["query"]["function_score"]["functions"].append({
                "filter": {
                    "bool": {
                        "must": [
                            {"terms": {"user_id": good_user_ids}},
                            {
                                "match_phrase": {
                                    "fresh_tractate_tag_name": {
                                        "query": query,
                                        "analyzer": "gm_default_index"
                                    }
                                }
                            }
                        ]
                    }
                },
                "weight": 100000
            })

        if len(other_key_query_list) > 0:
            q["query"]["function_score"]["functions"].append({
                "filter": {
                    "bool": {
                        "should": other_key_query_list,
                        "minimum_should_match": 1
                    }
                },
                "weight": 500
            })
        if len(have_read_id_list) > 0:
            q["query"]["function_score"]["query"]["bool"]["must_not"].append(
                {
                    "terms": {
                        "id": have_read_id_list
                    }
                }
            )

        if len(closure_tags) > 0 and use_fresh_tag:
            q["query"]["function_score"]["query"]["bool"]["should"].append(
                {
                    "terms": {
                        "fresh_tractate_tag_name": closure_tags
                    }
                }
            )
            q["query"]["function_score"]["query"]["bool"]["should"].append(
                {
                    "terms": {
                        "fresh_tractate_tag_name_content": closure_tags
                    }
                }
            )
        if len(closure_tags) > 0 and use_fresh_tag == False:
            q["query"]["function_score"]["query"]["bool"]["should"].append(
                {
                    "terms": {
                        "tractate_tag_name": closure_tags
                    }
                }
            )
            q["query"]["function_score"]["query"]["bool"]["should"].append(
                {
                    "terms": {
                        "tractate_tag_name_content": closure_tags
                    }
                }
            )
        logging.info("get query_filter_comprehensive_tractate_query:%s" % q)
        return q
    except:
        logging.error("catch exception,logins:%s" % traceback.format_exc())
        return dict()


@bind('doris/search/query_filter_tractate')
def query_filter_tractate(query=None, offset=0, size=10, have_read_tractate_list=[], use_fresh_tag=False,
                          device_id=""):
    try:
        multi_fields = {
            "content": 6,
            # "tractate_tag_name": 3,
            # "tractate_tag_name_content": 4,
            "author": 2,
        }

        if use_fresh_tag:
            multi_fields["fresh_tractate_tag_name"] = 3
            multi_fields["fresh_tractate_tag_name_content"] = 4
        else:
            multi_fields["tractate_tag_name"] = 3
            multi_fields["tractate_tag_name_content"] = 4

        fields = ['^'.join((k, str(v))) for (k, v) in multi_fields.items()]

        multi_match = {
            'query': query,
            'type': 'cross_fields',
            'operator': 'and',
            'fields': fields,
        }

        q = {}

        q["query"] = {"function_score":
            {"functions": [
                {
                    "filter": {
                        "term": {
                            "content": query
                        }
                    },
                    "weight": 10

                }, {
                    "filter": {
                        "term": {
                            "tractate_tag_name": query
                        }
                    },
                    "weight": 5
                }, {
                    "filter": {
                        "term": {
                            "tractate_tag_name_content": query
                        }
                    },
                    "weight": 4
                }, {
                    "filter": {
                        "term": {
                            "author": query
                        }
                    },
                    "weight": 2
                }
            ]}}

        q["query"]["function_score"]["boost_mode"] = "sum"
        q["query"]["function_score"]["score_mode"] = "max"

        q["query"]["function_score"]["query"] = {
            "bool": {
                "must": [
                    {
                        "term": {
                            "is_online": True
                        }
                    },
                    {
                        "term": {
                            "status": "3"
                        }
                    }
                ],
                # "must_not": [{
                #     "term": {
                #         "status": "4"
                #     }
                # }],
                "should": {
                    "multi_match": multi_match
                },
                "minimum_should_match": 1,
            }
        }
        if len(have_read_tractate_list) > 0:
            q["query"]["function_score"]["query"]["bool"]["must_not"] = [{"terms": {"id": have_read_tractate_list}}]

        key = "search_topic_device:{}".format(device_id)
        have_read_topic = list()
        if offset > 0 and device_id != "":
            redis_have_data = redis_client.get(key)
            have_read_topic = json.loads(redis_have_data) if redis_have_data else []
            if have_read_topic:
                if "must_not" in q["query"]["function_score"]["query"]["bool"]:
                    q["query"]["function_score"]["query"]["bool"]["must_not"].append({"terms": {"id": have_read_topic}})
                else:
                    q["query"]["function_score"]["query"]["bool"]["must_not"] = [{"terms": {"id": have_read_topic}}]
        q["highlight"] = get_highlight_query(["content"], query)

        q["sort"] = [{
            "_script": {
                "order": "desc",
                "script": {
                    "inline": "_score*factor+doc['tractate_score'].value*factors",
                    "params": {
                        "factor": 0.7,
                        "factors": 0.3
                    }
                },
                "type": "number"
            }

        },
            "_score"
        ]

        tractate_id_list = []
        tractate_extra = []
        logging.info('[%s] query_filter_tractate:%s' % (str(device_id), str(q).encode('utf-8')))
        res = es_query('tractate', q, 0, size)
        res_hit = res["hits"]["hits"]
        for item in res_hit:
            if '_source' in item:
                id = item['_source']['id']
                highlight = item.get('highlight')
                tractate_id_list.append(id)
                if highlight != None:
                    tractate_extra.append({"id": id, "highlight": highlight})

        if tractate_id_list:
            have_read_topic = have_read_topic + tractate_id_list
            redis_client.set(key, json.dumps(have_read_topic), ex=6 * 60)

        return {'tractate_id_list': tractate_id_list, "tractate_extra": tractate_extra}
    except:
        logging.error("catch exception,logins:%s" % traceback.format_exc())
        return {'tractate_id_list': [], "tractate_extra": []}


def scatter(data, size):
    for item in data:
        item['id'] = item['_id']
        item['group'] = item.get('_source', {}).get('user_id', None)
    data = variousness(data, size)
    return data[:size]


@bind('doris/search/tractate_sort')
def tractate_sort(filters=None, offset=0, size=10, query=None, use_fresh_tag=False, device_id='', source_type=0,
                  get_query=False, have_read_tractate=[], noarea_tags=[], all_tags=[], content_star_keyword=[]):
    try:
        have_read_tractate_id_list = []
        if source_type == 1:
            """
            增加已读过滤
            """
            if not isinstance(device_id, str):
                device_id = ""
            redis_key = ""
            if device_id:
                redis_key = "doris_feed:home_recommend_tractate" + ":device_id:" + str(device_id)
                redis_question_val_list = redis_client.hmget(redis_key, source_type)
                # 获取已读question
                if redis_question_val_list[0]:
                    have_read_tractate_id_list = list(json.loads(redis_question_val_list[0]))

        filters = filters or {}

        tag_ids = []

        for k, v in filters.items():

            if k == "tag_ids":
                if isinstance(v, int):
                    tag_ids.append(v)
                if isinstance(v, list):
                    tag_ids = v
        q = {}

        if len(tag_ids) > 0:
            q["query"] = {
                "bool": {
                    "must": [
                        {"term": {"is_online": True}},
                        {"terms": {"content_level": ["3", "4", "5"]}}
                    ]
                }
            }
            if use_fresh_tag:
                q["query"]["bool"]["must"] += [{"terms": {"fresh_tractate_tag_list": tag_ids}}]
            else:
                q["query"]["bool"]["must"] += [{"terms": {"tractate_tag_list": tag_ids}}]

            if have_read_tractate_id_list:
                q["query"]["bool"]["must_not"] = [{"terms": {"id": have_read_tractate_id_list}}]
        else:
            q["query"] = {
                "bool": {
                    "must": [
                        {"term": {"is_online": True}},
                        {"terms": {"content_level": ["3", "4", "5"]}}
                    ]
                }
            }
            if have_read_tractate_id_list:
                q["query"]["bool"]["must_not"] = [{"terms": {"id": have_read_tractate_id_list}}]

        q["sort"] = [
            {"tractate_score": {"order": "desc"}},
            {"post_time": {"order": "desc"}}

        ]

        tractate_id_list = []

        if get_query is True:
            query_dict = {}
            new_q = {}

            new_q["from"] = offset
            new_q["size"] = size
            new_q["sort"] = [
                {"tractate_score": {"order": "desc"}},
                {"post_time": {"order": "desc"}}
            ]

            query_dict = {
                "bool": {
                    "must": [
                        {"term": {"is_online": True}},
                        {"terms": {"content_level": ["3", "4", "5"]}}
                    ],
                    "should": [],
                    "minimum_should_match": 1
                }
            }

            if len(have_read_tractate) > 0:
                query_dict["bool"]["must_not"] = [{"terms": {"id": list(set(have_read_tractate))}}]

            if len(noarea_tags) > 0:
                query_dict["bool"]["should"].append({
                    "terms": {
                        "tractate_tag_name": noarea_tags
                    }
                })
                query_dict["bool"]["should"].append({
                    "terms": {
                        "fresh_tractate_tag_name": noarea_tags
                    }
                })

            if len(all_tags) > 0:
                query_dict["bool"]["should"].append({
                    "terms": {
                        "fresh_tractate_tag_list": all_tags
                    }
                })

            if len(content_star_keyword) > 0:
                content_star_first_keyword = content_star_keyword[0]
                function_list = []

                query_dict["bool"]["should"].append({
                    "terms": {
                        "content_star_keyword": content_star_keyword
                    }
                })

                # "明星婚恋", "明星颜值", "明星整形", "明星抗衰", "网红婚恋", "网红整形", "网红颜值", "网红抗衰", "明星颜值打call", "明星娱乐"
                gossip_tag_list = [21788, 21790, 18665, 21789, 21791, 21794, 21793, 21792, 18760, 99]

                function_list.append({
                    "filter": {
                        "bool": {
                            "must": [
                                {"term": {"content_star_first_keyword": content_star_first_keyword}},
                                {"terms": {"fresh_tractate_tag_list": gossip_tag_list}}
                            ]
                        }
                    },
                    "weight": 1000
                })

                function_list.append({
                    "filter": {
                        "term": {"content_star_first_keyword": content_star_first_keyword}
                    },
                    "weight": 800
                })

                star_length = len(content_star_keyword)
                for key, val in enumerate(content_star_keyword):
                    weight = (star_length - key) * 100
                    function_list.append({
                        "filter": {
                            "bool": {
                                "must": [
                                    {"term": {"content_star_keyword": val}},
                                    {"terms": {"fresh_tractate_tag_list": gossip_tag_list}}
                                ]
                            }
                        },
                        "weight": weight
                    })

                function_list.append({
                    "filter": {
                        "terms": {"content_star_keyword": content_star_keyword}
                    },
                    "weight": 100
                })

                new_q["sort"].insert(0, {"_score": {"order": "desc"}})

                new_q["query"] = {
                    "function_score": {
                        "functions": function_list,
                        "query": query_dict,
                        "boost_mode": "replace",
                        "score_mode": "sum",
                    }
                }

                logger.info("tractate detail recommend with star, query:%s" % str(new_q))
                return new_q

            new_q["query"] = query_dict
            logger.info("tractate detail recommend, query:%s" % str(new_q))
            return new_q
        hits = []
        if source_type == 1:
            res = es_query('tractate', q, offset, 100)
            hits = res["hits"]
            hits["hits"] = scatter(hits['hits'], size)

        else:
            res = es_query('tractate', q, offset, size)
            hits = res["hits"]

        for item in hits["hits"]:
            if '_source' in item:
                id = item['_source']['id']
                have_read_tractate_id_list.append(id)
                tractate_id_list.append(id)

        """
        保存已读的问答数据
        """
        if source_type == 1:
            if tractate_id_list == []:
                if len(have_read_tractate_id_list) >= size:
                    redis_client.delete(redis_key)
                    redis_client.hset(redis_key, source_type, json.dumps(have_read_tractate_id_list[0:size]))
                    redis_client.expire(redis_key, 60 * 60 * 24 * 15)
                    tractate_id_list = have_read_tractate_id_list[0:size]
                else:
                    redis_client.hset(redis_key, source_type, json.dumps(have_read_tractate_id_list))
                    redis_client.expire(redis_key, 60 * 60 * 24 * 15)
                    tractate_id_list = have_read_tractate_id_list
                    #
            else:
                if redis_key:
                    redis_client.hset(redis_key, source_type, json.dumps(have_read_tractate_id_list))
                    redis_client.expire(redis_key, 60 * 60 * 24 * 15)

        return {'tractate_id_list': tractate_id_list}

    except:
        logging_exception()
        logging.error("catch exception,logins:%s" % traceback.format_exc())

        return {'tractate_id_list': []}


@bind('doris/search/doctor_tractate_sort')
def doctor_tractate_sort(filters=None, offset=0, size=10, query=None):
    try:
        filters = filters or {}

        tag_ids = []

        for k, v in filters.items():

            if k == "tag_ids":
                tag_ids = v
        q = {}
        if len(tag_ids) > 0:
            q["query"] = {
                "bool": {
                    "must": [
                        {"terms": {"tractate_tag_list": tag_ids}},
                        {"term": {"is_online": True}}
                    ]
                }
            }

        else:
            q["query"] = {
                "bool": {
                    "must": {"term": {"is_online": True}}

                }
            }

        tractate_id_list = []
        logging.info('test query es :%s' % str(q).encode('utf-8'))
        res = es_query('doctortractate', q, offset, size)
        res_hit = res["hits"]["hits"]
        for item in res_hit:
            if '_source' in item:
                id = item['_source']['id']
                tractate_id_list.append(id)
        return {'tractate_id_list': tractate_id_list}

    except:

        logging.error("catch exception,logins:%s" % traceback.format_exc())

        return {'tractate_id_list': []}


@bind('doris/search/tractate_hotword')
def tractate_hotword(query='', size=10):
    try:
        redis_key = "gaia:hot_tractate_keyword:city_id:" + str(query)
        redis_field_val_list = redis_client.get(redis_key)
        result = json.loads(str(redis_field_val_list, encoding="utf-8"))
        have_read_id_list = result if result else []
        tractate_hot_keyword = list()
        for item in have_read_id_list:
            tractate_hot_keyword.append(item[0])
        return {"hot_recommends": tractate_hot_keyword}
    except:
        logging.error("catch exception,err_msg:%s" % traceback.format_exc())
        return {"hot_recommends": []}


@bind('doris/search/tractate_check_in')
def tractate_check_in(device_id, size):
    try:
        key = 'device_register_tractate_queue:' + str(device_id)
        if redis_client.exists(key):
            tractate_dict = redis_client.hgetall(key)
            cursor = int(str(tractate_dict[b'cursor'], encoding='utf-8'))
            tractate_queue = json.loads(tractate_dict[b'tractate_queue'].decode())
            if cursor >= len(tractate_queue):
                cursor = 0
            data = tractate_queue[cursor:cursor + size]
            redis_client.hset(key, 'cursor', cursor + size)
            if len(data) > 0:
                read_tractate_key = "TS:recommend_tractate_set:device_id:" + str(device_id)
                redis_client.sadd(read_tractate_key, *data)
                read_register_tractate_key = "device_register_tractate_read_set:" + str(device_id)
                redis_client.sadd(read_register_tractate_key, *data)
                redis_client.expire(read_tractate_key, time=15 * 24 * 60 * 60)
            return {'tractate_id_list': data}
        else:
            return {'tractate_id_list': []}
    except:

        logging.error("catch exception,logins:%s" % traceback.format_exc())

        return {'tractate_id_list': []}
