# -*- coding: utf-8 -*-
from rpc.tool.error_code import CODES, gen
from gm_dataquery.db import DB
from gm_dataquery.dataquery import DataBuilder, DataSQLQuery
from api.models.face.cheek_style import CheekStyleClassify, CheekStyleClassifyMap, CheekStyle, CheekStyleExtra, \
    CheekStyleAppealExtra, UserPlasticHistory
from api.models.face.plastic import PlasticTemplate, AppealCard
from agile.models.tag import TagV3
from api.models.face.cheek_style import CheekStyle
from gm_types.gaia import PLASTICFACESWORD, TAG_V3_TYPE
from gm_types.face.enum import RatioCombination, PLASTIC_V4_TAGS
from django.db.models import Q
from gm_dataquery.dict_mixin import to_dict
from django.conf import settings
from collections import namedtuple


class PlasticTemplateDB(DataBuilder):

    def getval_fenggelian_name(self, obj):
        obj = CheekStyle.objects.filter(id=int(obj.fenggelian)).first()
        return obj.name if obj else ""


@DB
class PlasticTemplateDQ(DataSQLQuery):
    model = PlasticTemplate
    data_model = PlasticTemplateDB

    def filter_fenggelian_name(self, key, value, regex):
        fenggelians = CheekStyle.objects.filter(name__contains=value).values_list("id", flat=True)
        return Q(fenggelian__in=fenggelians)

    def create(self, **kwargs):
        if self.model.objects.filter(face_style=kwargs['face_style'], fenggelian=kwargs['fenggelian']).exists():
            return gen(CODES.PLASTIC_FACE_TYPE_UNIQ)

        obj = self.model.objects.create(**kwargs)
        return to_dict(obj)

    def update(self, updates, **kwargs):
        obj = self.model.objects.get(id=kwargs['id'])
        if updates.get('face_style') or updates.get('fenggelian'):
            face_style = updates.get('face_style') or obj.face_style
            fenggelian = updates.get('fenggelian') or obj.fenggelian
            if self.model.objects.filter(face_style=face_style, fenggelian=fenggelian).exclude(
                    id=kwargs['id']).exists():
                return gen(CODES.PLASTIC_FACE_TYPE_UNIQ)

        return super(PlasticTemplateDQ.sqlquery, self).update(updates, **kwargs)


class AppealCardDB(DataBuilder):
    def getval_appeal_tag_name(self, obj):
        obj = TagV3.objects.filter(id=obj.appeal_tag_id).first()
        return obj.name if obj else ""


ATTR_TAG_TYPE = [
    TAG_V3_TYPE.FIRST_SYMPTOM, TAG_V3_TYPE.SECOND_SYMPTOM, TAG_V3_TYPE.FIRST_BRAND, TAG_V3_TYPE.SECOND_BRAND,
    TAG_V3_TYPE.MACROSCOPIC_MODE, TAG_V3_TYPE.FIRST_APPEAL, TAG_V3_TYPE.SECOND_APPEAL, TAG_V3_TYPE.FIRST_POSITION,
    TAG_V3_TYPE.SECOND_POSITION, TAG_V3_TYPE.DRUG, TAG_V3_TYPE.INSTRUMENT, TAG_V3_TYPE.CONSUMABLES,
    TAG_V3_TYPE.MATERIAL,
]


@DB
class AppealCardDQ(DataSQLQuery):
    model = AppealCard
    data_model = AppealCardDB

    def filter_appeal_tag_name(self, key, value, regex):
        tag_ids = TagV3.objects.filter(name__contains=value, tag_type__in=ATTR_TAG_TYPE).values_list("id", flat=True)
        return Q(appeal_tag_id__in=tag_ids)


class CheekStyleDB(DataBuilder):

    DEFAULT_CLASSIFY_NAME = u'无类别'

    def getval_classify_name(self, obj):
        obj = CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter(
            cheek_style_id=obj.id,
        ).first()

        if not obj:
            return self.DEFAULT_CLASSIFY_NAME

        classify_obj = CheekStyleClassify.objects.using(settings.SLAVE_DB_NAME).filter(
            id=obj.cheek_style_classify_id,
        ).first()

        if not classify_obj:
            return self.DEFAULT_CLASSIFY_NAME

        return classify_obj.name

    def getval_desc(self, obj):
        try:
            desc = CheekStyleExtra.objects.using(settings.SLAVE_DB_NAME).get(cheek_style_id=obj.id).desc
        except:
            desc =  ''

        return desc

    def getval_analyze(self, obj):
        try:
            analyze = CheekStyleExtra.objects.using(settings.SLAVE_DB_NAME).get(cheek_style_id=obj.id).analyze
        except:
            analyze = ""

        return analyze

    def getval_appeal_tags(self, obj):
        tag_info_dic = dict(
            CheekStyleAppealExtra.objects.using(settings.SLAVE_DB_NAME).filter(
                cheek_style_id=obj.id
            ).values_list("appeal_tag_name", "desc")
        )
        res = []
        for _, tag_name in PLASTIC_V4_TAGS.choices:
            res.append({
                "appeal_tag_name": tag_name,
                "desc": tag_info_dic.get(tag_name, ''),
            })
        return res

    def getval_deleted(self, obj):
        try:
            deleted = CheekStyleExtra.objects.using(settings.SLAVE_DB_NAME).get(cheek_style_id=obj.id).deleted
        except:
            deleted = False

        return deleted

    def getval_extra_id(self, obj):
        try:
            _id = CheekStyleExtra.objects.using(settings.SLAVE_DB_NAME).get(cheek_style_id=obj.id).id
        except:
            _id = None

        return _id


@DB
class CheekStyleDQ(DataSQLQuery):
    model = CheekStyle
    data_model = CheekStyleDB
    CLASSIFY_STATUS = namedtuple("CLASSIFY_STATUS", ["ALL", "NOT_CLASSIFY", "CLASSIFIED"])(ALL=0, NOT_CLASSIFY=1, CLASSIFIED=2)

    def filter_classify_status(self, field, val, regex):
        val = int(val) if val else self.CLASSIFY_STATUS.ALL
        if val == self.CLASSIFY_STATUS.ALL:
            return Q()
        else:
            cheek_style_ids = list(set(
                CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter().values_list("cheek_style_id", flat=True)
            ))

            if val == self.CLASSIFY_STATUS.NOT_CLASSIFY:
                return ~Q(id__in=cheek_style_ids)
            else:
                return Q(id__in=cheek_style_ids)

    def filter_search_name(self, field, val, regex):
        classify_ids = list(CheekStyleClassify.objects.using(settings.SLAVE_DB_NAME).filter(
            name__contains=val,
        ).values_list("id", flat=True))
        cheek_style_ids = list(CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter(
            cheek_style_classify_id__in=classify_ids,
        ).values_list("cheek_style_id", flat=True))

        return (Q(id__in=cheek_style_ids) | Q(name__contains=val))

    def filter_classify_id(self, field, val, regex):
        val = int(val) if val else 0
        if not val:
            classify_ids = list(set(
                CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter().values_list("cheek_style_id", flat=True)
            ))
            return ~Q(id__in=classify_ids)
        else:
            cheek_style_ids = list(CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter(
                cheek_style_classify_id=val,
            ).values_list("cheek_style_id", flat=True))
            return Q(id__in=cheek_style_ids)

    def _update_appeal_tags(self, cheek_style_id, appeal_tags):
        new_appeal_tags = {(tag.get("appeal_tag_name", ""), tag.get("desc", "")) for tag in appeal_tags}
        old_appeal_tags = set(CheekStyleAppealExtra.objects.using(settings.SLAVE_DB_NAME).filter(
            cheek_style_id=cheek_style_id
        ).values_list("appeal_tag_name", "desc"))

        need_save_appeal_tags = new_appeal_tags - old_appeal_tags
        need_delete_appeal_tags = old_appeal_tags - new_appeal_tags

        if need_save_appeal_tags:
            tag_names = {tag_name for _, tag_name in PLASTIC_V4_TAGS.choices}
            CheekStyleAppealExtra.objects.bulk_create(
                [CheekStyleAppealExtra(
                        cheek_style_id=cheek_style_id,
                        appeal_tag_name=item[0],
                        desc=item[1],
                        is_online=True,
                        deleted=False,
                    ) for item in need_save_appeal_tags if item[0] in tag_names]
            )

        for item in need_delete_appeal_tags:
            CheekStyleAppealExtra.objects.filter(
                cheek_style_id=cheek_style_id,
                appeal_tag_name=item[0],
                desc=item[1],
            ).delete()

    def update(self, updates, **kwargs):
        cheek_style_id = kwargs.get("id", 0)
        defaults = {
            "is_online": True,
        }
        need_update = False
        if "desc" in updates:
            defaults["desc"] = updates.pop("desc", "")
            need_update = True

        if "analyze" in updates:
            defaults["analyze"] = updates.pop("analyze", "")
            need_update = True

        if "deleted" in updates:
            defaults["deleted"] = updates.pop("deleted", False)
            need_update = True

        if need_update:
            CheekStyleExtra.objects.update_or_create(
                cheek_style_id=cheek_style_id,
                defaults=defaults,
            )

        self._update_appeal_tags(cheek_style_id, updates.pop("appeal_tags", []))

        return 1


class CheekStyleClassifyDB(DataBuilder):
    def getval_cheek_styles(self, obj):
        cheekstyle_classify_map = CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter(
            cheek_style_classify_id=obj.id,
            deleted=False,
        ).order_by('id').values("cheek_style_id", "sort_type")

        cheek_style_ids = []
        cheek_styles_info_dic = {}
        for item in cheekstyle_classify_map:
            cheek_style_id = item['cheek_style_id']
            cheek_styles_info_dic[cheek_style_id] = item['sort_type']
            cheek_style_ids.append(cheek_style_id)

        cheek_style_name_dic = dict(CheekStyle.objects.using(settings.SLAVE_DB_NAME).filter(
            id__in=cheek_style_ids,
        ).values_list("id", "name"))

        cheek_styles = []
        for cheek_style_id in cheek_style_ids:
            cheek_styles.append({
                "cheek_style_id": cheek_style_id,
                "name": cheek_style_name_dic.get(cheek_style_id, ''),
                "sort_type": cheek_styles_info_dic[cheek_style_id],
            })

        return cheek_styles


@DB
class CheekStyleClassifyDQ(DataSQLQuery):
    model = CheekStyleClassify
    data_model = CheekStyleClassifyDB
    ONLINE_STATUS = namedtuple("ONLINE_STATUS", ["ALL", "ONLINE", "NOT_ONLINE"])(ALL=0, ONLINE=1, NOT_ONLINE=2)

    def _update_cheek_styles(self, classify_id, cheek_styles, is_online):
        CheekStyleClassifyMap.objects.filter(cheek_style_classify_id=classify_id).delete()

        if cheek_styles:
            CheekStyleClassifyMap.objects.bulk_create([
                CheekStyleClassifyMap(
                    cheek_style_classify_id=classify_id,
                    cheek_style_id=item.get("cheek_style_id", 0),
                    sort_type=item.get("sort_type", 0),
                    is_online=is_online,
                    deleted=False,
                )
                for item in cheek_styles
            ])

    def update(self, updates, **kwargs):
        cheek_styles = updates.pop("cheek_styles", [])
        classify_obj = CheekStyleClassify.objects.get(**kwargs)
        is_online = updates['is_online'] if "is_online" in updates else classify_obj.is_online
        self._update_cheek_styles(classify_obj.id, cheek_styles, is_online)

        return super(CheekStyleClassifyDQ.sqlquery, self).update(updates, **kwargs)

    def create(self, **kwargs):
        cheek_styles = kwargs.pop("cheek_styles", [])
        obj = CheekStyleClassify.objects.create(
            name=kwargs.get('name', ''),
            rank=kwargs.get('rank', 0),
            is_online=kwargs.get('is_online', True),
            deleted=kwargs.get('deleted', False),
        )

        if cheek_styles:
            CheekStyleClassifyMap.objects.bulk_create([
                CheekStyleClassifyMap(
                    cheek_style_classify_id=obj.id,
                    cheek_style_id=item.get("cheek_style_id", 0),
                    sort_type=item.get("sort_type", 0),
                    is_online=kwargs.get('is_online', False),
                    deleted=False,
                )
                for item in cheek_styles
            ])

        return to_dict(obj)

    def filter_search_name(self, field, val, regex):
        cheek_style_ids = list(CheekStyle.objects.using(settings.SLAVE_DB_NAME).filter(
            name__contains=val,
        ).values_list('id', flat=True))

        classify_ids = list(CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter(
            cheek_style_id__in=cheek_style_ids,
        ).values_list("cheek_style_classify_id", flat=True))
        return (Q(name__contains=val) | Q(id__in=classify_ids))

    def filter_classify_id(self, field, val, regex):
        return Q(id=val)

    def filter_cheeck_style_id(self, field, val, regex):
        classify_ids = list(CheekStyleClassifyMap.objects.using(settings.SLAVE_DB_NAME).filter(
            cheek_style_id=val,
        ).values_list("cheek_style_classify_id", flat=True))
        return Q(id__in=classify_ids)

    def filter_online_status(self, field, val, regex):
        if val == self.ONLINE_STATUS.ALL:
            return Q()
        elif val == self.ONLINE_STATUS.ONLINE:
            return Q(is_online=True)
        else:
            return Q(is_online=False)

    def filter_need_no_classify(self, field, val, regex):
        """用来获取无分类"""
        return Q()

    def filter_deleted(self, field, val, regex):
        if val in ['false', 'False', 0]:
            val = False
        else:
            val = True
        return Q(deleted=val)

class UserPlasticHistoryDB(DataBuilder):

    def getval_proportion(self, obj):
        return RatioCombination.getDesc(obj.proportion)

@DB
class UserPlasticHistoryDQ(DataSQLQuery):
    model = UserPlasticHistory
    data_model = UserPlasticHistoryDB

    def filter_start_time(self, field, val, regex):
        return Q(create_time__gte=val)

    def filter_end_time(self, field, val, regex):
        return Q(create_time__lte=val)
