# coding:utf-8
import json
from django.conf import settings
from functools import partial
from itertools import chain

from gm_dataquery.dict_mixin import to_dict
from gm_dataquery.dataquery import DataSQLQuery, DataBuilder
from gm_dataquery.db import DB
from gm_types.gaia import TOP_CONTENT_RELATED_TYPE

from group.models import Group, GroupTag, GroupTagV3, GroupTopicRelation, TopContent
from api.models import Tag
from group.tasks.tasks import sync_group_topic_extra_by_id


class GroupDB(DataBuilder):

    def getval_tags(self, obj):
        return list(GroupTag.objects.filter(group_id=obj.id, is_online=True).values_list("tag_id", flat=True))

    def getval_tags_name(self, obj):
        tag_ids = list(GroupTag.objects.filter(group_id=obj.id, is_online=True).values_list("tag_id", flat=True))
        return ", ".join([i.name for i in Tag.objects.filter(pk__in=tag_ids)])

    def getval_tag_v3_ids(self, obj):
        return list(GroupTagV3.objects.filter(
            group_id=obj.id,
            is_online=True
        ).values_list("tag_v3_id", flat=True))

    def punch_entrance(self, obj):

        try:
            punch_entrance = json.loads(obj.punch_entrance)
        except:
            punch_entrance = {}

        return punch_entrance

    def getval_punch_entrance_one(self, obj):
        return self.punch_entrance(obj).get("punch_entrance_one", '')

    def getval_punch_entrance_one_url(self, obj):
        return self.punch_entrance(obj).get("punch_entrance_one_url", '')

    def getval_punch_entrance_two(self, obj):
        return self.punch_entrance(obj).get("punch_entrance_two", '')

    def getval_punch_entrance_two_url(self, obj):
        return self.punch_entrance(obj).get("punch_entrance_two_url", '')

    def getval_topics_length(self, obj):
        return GroupTopicRelation.objects.filter(group_id=obj.id).count()

    def getval_top_contents_length(self, obj):
        return TopContent.objects.filter(
            related_id=obj.id,
            related_type=TOP_CONTENT_RELATED_TYPE.GROUP,
            is_online=True,
        ).count()


def add_relation_topic():

    def getval_topic(self, obj, i=0):

        try:
            return obj.topics[i-1]["topic_id"]
        except:
            return None

    def getval_topic_rank(self, obj, i=0):

        try:
            return obj.topics[i-1]["rank"]
        except:
            return None

    def f(callable, i):
        def wrap(*args, **kwargs):
            wrap_func = partial(callable, i=i)
            return wrap_func(*args, **kwargs)
        return wrap

    for i in range(1, 50):
        setattr(GroupDB, "getval_topic_" + str(i), f(getval_topic, i=i))
        setattr(GroupDB, "getval_topic_rank_" + str(i), f(getval_topic_rank, i=i))


def add_relation_top_content():
    def getval_top_id(self, obj, i=1):

        try:
            return obj.top_contents[i-1]["id"]
        except:
            return None

    def getval_top_theme(self, obj, i=1):

        try:
            return obj.top_contents[i-1]["theme"]
        except:
            return ""

    def getval_top_title(self, obj, i=1):

        try:
            return obj.top_contents[i-1]["title"]
        except:
            return ""

    def getval_top_content_type(self, obj, i=1):

        try:
            return obj.top_contents[i-1]["content_type"]
        except:
            return ""

    def getval_top_content_id(self, obj, i=1):

        try:
            return obj.top_contents[i - 1]["content_id"]
        except:
            return 0

    def f(callable, i):
        def wrap(*args, **kwargs):
            wrap_func = partial(callable, i=i)
            return wrap_func(*args, **kwargs)
        return wrap

    for i in range(1, 20):
        setattr(GroupDB, "getval_top_id_" + str(i), f(getval_top_id, i=i))
        setattr(GroupDB, "getval_top_theme_" + str(i), f(getval_top_theme, i=i))
        setattr(GroupDB, "getval_top_title_" + str(i), f(getval_top_title, i=i))
        setattr(GroupDB, "getval_top_content_type_" + str(i), f(getval_top_content_type, i=i))
        setattr(GroupDB, "getval_top_content_id_" + str(i), f(getval_top_content_id, i=i))


add_relation_topic()
add_relation_top_content()


@DB
class GroupDQ(DataSQLQuery):
    model = Group
    data_model = GroupDB

    def build_objs_data(self, objs, fields):
        data = super(GroupDQ.sqlquery, self).build_objs_data(objs, fields)

        topics_dict = {}
        for obj in data:
            _id = obj["pk"]
            topics = list(GroupTopicRelation.objects.filter(group_id=_id, is_online=True).order_by("rank").values("topic_id", "rank"))
            topics_dict[_id] = topics

        for item in data:
            item["topics"] = topics_dict.get(item["pk"], [])
            item["topics_length"] = len(topics_dict.get(item["pk"], []))

        return data

    def create(self, **kwargs):
        tags = kwargs.pop('tags')
        tag_v3_ids = kwargs.pop("tag_v3_ids", [])
        kwargs.pop('tags_name', None)
        kwargs.pop('topics_length', None)
        kwargs.pop('top_contents_length', None)
        topics = kwargs.pop('topics', [])
        top_contents = kwargs.pop('top_contents', [])

        top_card_id = kwargs.get('top_card_id', '')
        if not top_card_id:
            kwargs["top_card_id"] = None

        punch_entrance = {
            "punch_entrance_one": kwargs.pop('punch_entrance_one', ''),
            "punch_entrance_one_url": kwargs.pop('punch_entrance_one_url', ''),
            "punch_entrance_two": kwargs.pop('punch_entrance_two', ''),
            "punch_entrance_two_url": kwargs.pop('punch_entrance_two_url', ''),
        }
        kwargs["punch_entrance"] = json.dumps(punch_entrance)

        group = Group.objects.create(**kwargs)
        if tags:
            GroupTag.objects.bulk_create([
                GroupTag(group_id=group.id, tag_id=tag_id)
                for tag_id in tags
            ])

        if tag_v3_ids:
            GroupTagV3.objects.bulk_create([
                GroupTagV3(group_id=group.id, tag_v3_id=tag_id)
                for tag_id in tag_v3_ids
            ])

        if topics:
            self._update_topics(group.id, topics)
        self._update_top_contents(group.id, top_contents)
        sync_group_topic_extra_by_id.delay(group.id, _type=TOP_CONTENT_RELATED_TYPE.GROUP)
        return to_dict(group)

    def _update_tag(self, group_id, tags):

        if tags == [] or tags:
            GroupTag.objects.filter(group_id=group_id).delete()

        if tags:
            GroupTag.objects.bulk_create([
                GroupTag(group_id=group_id, tag_id=tag_id)
                for tag_id in tags
            ])

    def _update_tag_v3(self, group_id, tags):
        if tags == [] or tags:
            GroupTagV3.objects.filter(group_id=group_id).delete()

        if tags:
            GroupTagV3.objects.bulk_create([
                GroupTagV3(group_id=group_id, tag_v3_id=tag_id)
                for tag_id in tags
            ])

    def _update_topics(self, group_id, topics):

        ts = {}
        for _id, rank in topics.items():
            ts[int(_id)] = rank

        relations = GroupTopicRelation.objects.filter(group_id=group_id, topic_id__in=ts.keys())
        all_relations = GroupTopicRelation.objects.filter(group_id=group_id)

        exists_ids = [item.topic_id for item in relations if item.is_online]
        nedd_online_ids = [item.topic_id for item in relations if not item.is_online]

        need_delete = set([item.topic_id for item in all_relations if item.topic_id not in ts]) - set(ts.keys())
        if need_delete:
            GroupTopicRelation.objects.filter(group_id=group_id, topic_id__in=need_delete).update(is_online=False)

        for _id in nedd_online_ids:
            GroupTopicRelation.objects.filter(group_id=group_id, topic_id=_id).update(is_online=True, rank=ts[_id])

        for _id in exists_ids:
            GroupTopicRelation.objects.filter(group_id=group_id, topic_id=_id).update(rank=ts[_id])

        add_ids = set(ts.keys()) - set([item.topic_id for item in all_relations])
        rs = [
            GroupTopicRelation(group_id=group_id, topic_id=_id, rank=ts[_id])
            for _id in add_ids
        ]
        if ts:
            GroupTopicRelation.objects.bulk_create(rs)

    def _update_top_contents(self, group_id, top_contents):
        need_create_top_contents = []
        online_top_ids = set()
        for top_content in top_contents:
            top_id = int(top_content.get("top_id")) if top_content.get("top_id") else 0
            top_theme = top_content.get("top_theme", "")
            top_title = top_content.get("top_title", "")
            content_type = top_content.get("top_content_type", "")
            top_content_id = top_content.get("top_content_id", 0)
            if not top_id:
                need_create_top_contents.append(
                    TopContent(
                        theme=top_theme,
                        title=top_title,
                        content_type=content_type,
                        content_id=top_content_id,
                        related_type=TOP_CONTENT_RELATED_TYPE.GROUP,
                        related_id=group_id,
                    )
                )
            else:
                online_top_ids.add(top_id)
                TopContent.objects.update_or_create(
                    id=top_id,
                    defaults=dict(
                        theme=top_theme,
                        title=top_title,
                        content_type=content_type,
                        content_id=top_content_id,
                        related_type=TOP_CONTENT_RELATED_TYPE.GROUP,
                        related_id=group_id,
                        is_online=1,
                    )
                )

        all_top_ids = set(
            TopContent.objects.using(settings.SLAVE_DB_NAME).filter(
                related_id=group_id,
                related_type=TOP_CONTENT_RELATED_TYPE.GROUP,
            ).values_list('id', flat=True)
        )

        need_offline_top_ids = list(all_top_ids - online_top_ids)
        TopContent.objects.filter(id__in=need_offline_top_ids).update(is_online=False)

        if need_create_top_contents:
            TopContent.objects.bulk_create(need_create_top_contents)

    def update(self, updates, **kwargs):
        tags = updates.pop('tags', None)
        tag_v3_ids = updates.pop("tag_v3_ids", None)
        updates.pop('tags_name', None)
        updates.pop('topics_length', None)
        updates.pop('top_contents_length', None)
        topics = updates.pop('topics', [])
        top_contents = updates.pop('top_contents', [])

        group = Group.objects.get(**kwargs)

        top_card_id = updates.get('top_card_id', '')
        if not top_card_id:
            updates["top_card_id"] = None

        topic = Group.objects.get(**kwargs)

        try:
            punch_entrance = json.loads(topic.punch_entrance)
        except:
            punch_entrance = {}

        def get_punch_entrance_key(k):
            v = punch_entrance.get(k, '')
            if k in updates:
                v = updates.pop(k)
            return v

        punch_entrance_keys = [
            "punch_entrance_one",
            "punch_entrance_one_url",
            "punch_entrance_two",
            "punch_entrance_two_url",
        ]
        for k in punch_entrance_keys:
            punch_entrance[k] = get_punch_entrance_key(k)
        updates["punch_entrance"] = json.dumps(punch_entrance)

        Group.objects.filter(**kwargs).update(**updates)
        self._update_tag(group.id, tags)
        self._update_tag_v3(group.id, tag_v3_ids)
        self._update_topics(group.id, topics)
        self._update_top_contents(group.id, top_contents)

        return 1

