import json
import logging
from operator import contains
from collections import defaultdict
from django.core.paginator import Paginator
from utils import util
from utils.exception import ParamError
from domain.dataset import DataSet
from imdb.models import MediaModel, ProductMedia, EpisodeModel
from domain.imdb.domain_model import Genre, Region
from domain.imdb.celebrity_domain import Celebrity
from domain.imdb.value_object import CelebrityRoleValue, CategoryValue
from domain.imdb.product_media import MetaMedia
from imdb.models import CelebrityMediaRelationModel
from injector.models import MediaExtFieldModel
from django.forms.models import model_to_dict


class Media(DataSet):
    """media在数据库中允许重复"""
    model = MediaModel

    def __init__(self, media_id=0):
        self.genre = Genre()
        self.region = Region()
        self.celebrity = Celebrity()
        self.media_id = media_id
        if media_id:
            self.inst = MediaModel.objects.get(pk=media_id)

    def get_by_id_list(self, media_id_list):
        return self.model.objects.filter(pk__in=media_id_list)

    @classmethod
    def parse_data(cls, data):
        params = {
            'category': data.get('type', None),
            'name': data.get('name', None),
            'year': data.get('production_time', None),
            'genres': data.get('theme', None),
            'date_published': data.get('', None),
            'douban_score': data.get('rater', None),
            'episodes': int(data.get('episodes')) if data.get('episodes') else None,
            'seconds': float(data.get('episodes_time')) if data.get('episodes') else None,
            'total_seconds': float(data.get('total_time')) if data.get('total_time') else None,
            'regions': data.get('area', None),
            'languages': data.get('languages', None),
            'alias': data.get('alias', None),
            'imdb_id': data.get('imdb_id', None),
            'img': data.get('image', None),
            'storyline': data.get('plots', None),
            "hidden": data.get("hidden", None),
        }
        return {key: val for key, val in params.items() if val is not None}

    @classmethod
    def is_exists(cls, media_name, genre_list, year):
        query_data = {
            "name__startswith": media_name,
            "year": year
        }
        if genre_list:
            # 取一个值进行判断；为解决列表中元素相同，但顺序不同的情况
            query_data["genres__contains"] = genre_list[0]

        return cls.model.objects.filter(**query_data).exists()

    @classmethod
    def add(cls, info):
        """
        添加media
        :param dict info: MediaForm 中类型声明
        """
        info.pop('media_id')
        minutes = info.pop("minutes")
        total_minutes = info.pop("total_minutes")

        genre_ids = info.pop('genre_ids')
        director_ids = info.pop('director_ids')
        author_ids = info.pop('author_ids')
        actor_ids = info.pop('actor_ids')
        region_ids = info.pop('region_ids')

        genre_list = Genre().get_obj_by_id_list(util.str_to_list(genre_ids, ","))
        is_exists = cls.is_exists(info["name"], genre_list, info["year"])
        if is_exists:
            raise ParamError("该媒体已存在！")

        region_list = Region().get_obj_by_id_list(util.str_to_list(region_ids, ","))

        # 插入media基础信息
        info.update({
            "seconds": float(minutes * 60),
            "total_seconds": float(total_minutes * 60),
            "genres": genre_list,
            "regions": region_list
        })
        obj = cls.model.objects.create(**info)

        # 插入media人物对应表
        cls.bulk_set_celebrity_relation(obj, director_ids, author_ids, actor_ids)
        return obj

    @classmethod
    def deep_copy_media(cls, media_id):
        # 媒资信息 根据media_id 查询媒资 得到对应的媒资信息、扩展信息

        media = MediaModel.objects.filter(id=media_id).first()

        media_dict = model_to_dict(media, exclude=["id", "create_time", "update_time"])
        media_dict.update({"is_divided": True})
        actor_list = media_dict.pop("casting")
        new_media = MediaModel.objects.create(**media_dict)
        media_ext = MediaExtFieldModel.objects.filter(media_id=media_id).first()

        # 艺术家对象
        if actor_list:
            celebrity_role_dict = dict(
                CelebrityMediaRelationModel.objects.filter(media_id=media_id).values_list("celebrity_id", "role"))

            for actor in actor_list:
                role = celebrity_role_dict[actor.id]
                new_media.casting.add(actor, through_defaults={"role": role})
        # 扩展信息
        if media_ext:
            media_ext_dict = model_to_dict(media_ext, exclude=["id", "media"])
            MediaExtFieldModel.objects.create(media=new_media, **media_ext_dict)
        # 分集信息
        episode_queryset = EpisodeModel.objects.filter(media_id=media_id).values("title", "serial_num",
                                                                                 "storyline",
                                                                                 "episode_horpic", "horpic_size",
                                                                                 "begin", "end")
        episode_list = [EpisodeModel(media_id=new_media.id, **dict(episode)) for episode in episode_queryset]
        EpisodeModel.objects.bulk_create(episode_list)
        return 0, "success"

    @classmethod
    def edit(cls, info):
        minutes = info.pop("minutes")
        total_minutes = info.pop("total_minutes")

        media_id = info.pop("media_id")
        genre_ids = info.pop('genre_ids')
        director_ids = info.pop('director_ids')
        author_ids = info.pop('author_ids')
        actor_ids = info.pop('actor_ids')
        region_ids = info.pop('region_ids')
        info.pop("img")
        genre_list = Genre().get_obj_by_id_list(util.str_to_list(genre_ids, ","))
        region_list = Region().get_obj_by_id_list(util.str_to_list(region_ids, ","))

        # 更新media基础信息
        info.update({
            "seconds": float(minutes * 60),
            "total_seconds": float(total_minutes * 60),
            "genres": genre_list,
            "regions": region_list
        })
        cls.model.objects.filter(pk=media_id).update(**info)

        # 更新media人物
        cls.update_celetrity_relation(media_id, director_ids, author_ids, actor_ids)
        return

    @classmethod
    def bulk_set_celebrity_relation(cls, media, director_ids, author_ids, actor_ids):
        def set_relations(celebrity_ids, role):
            relation_list = []
            if isinstance(celebrity_ids, str):
                celebrity_id_list = util.str_to_list(celebrity_ids, ",")
            else:
                celebrity_id_list = celebrity_ids
            for i in celebrity_id_list:
                relation_list.append(CelebrityMediaRelationModel(**{
                    "celebrity": Celebrity.model(id=i),
                    "media": media,
                    "role": role
                }))

            return relation_list

        media.celebritymodel_set.clear()
        obj_list = []
        obj_list.extend(set_relations(director_ids, "director"))
        obj_list.extend(set_relations(author_ids, "author"))
        obj_list.extend(set_relations(actor_ids, "actor"))
        CelebrityMediaRelationModel.objects.bulk_create(obj_list)
        return

    @classmethod
    def update_celetrity_relation(cls, media_id, director_ids, author_ids, actor_ids):
        media = cls.model.objects.get(id=media_id)
        return cls.bulk_set_celebrity_relation(media, director_ids, author_ids, actor_ids)

    def detail(self, flag=False):
        director_obj_list, actor_obj_list, author_obj_list = self.get_media_celebrity(self.inst)
        media = self.to_dict(self.inst)
        media["director_list"] = self.to_list(director_obj_list)
        media["author_list"] = self.to_list(author_obj_list)
        media["actor_list"] = self.to_list(actor_obj_list)
        if flag and self.inst.program_id:
            related_media_ids = self.inst.program_id
            media['related_medias'] = MetaMedia.search_name_by_id(related_media_ids)
        return media

    @staticmethod
    def to_dict(obj, with_celebrity=False):
        partner_alias = obj.partner_alias
        results = {
            'id': obj.id,
            'name': obj.name,
            'alias': obj.alias,
            'category': obj.category,
            'date_published': obj.date_published,
            'douban_score': obj.douban_score,
            'episodes': obj.episodes,
            'img': obj.img,
            'storyline': obj.storyline,
            'year': obj.year,
            'create_time': obj.create_time,
            'genre_list': obj.genres,
            'region_list': obj.regions,
            'minutes': obj.seconds / 60,
            'total_minutes': obj.total_seconds / 60,
            "languages": obj.languages,
            "is_divided": obj.is_divided,
            "partner_alias": partner_alias,

        }

        if with_celebrity:
            results['director'] = []
            results['actor'] = []
            results['author'] = []

        return results

    @staticmethod
    def get_media_celebrity(media):
        result = defaultdict(list)
        relations = media.celebritymediarelationmodel_set.all().select_related('celebrity')
        for relation in relations:
            role = relation.role
            result[role].append(relation.celebrity)

        director, actor = CelebrityRoleValue.DIRECTOR, CelebrityRoleValue.ACTOR
        author, host = CelebrityRoleValue.AUTHOR, CelebrityRoleValue.HOST
        # 将主持人划分为演员
        actor_obj_list, host_obj_list = result[actor], result[host]
        actor_obj_list.extend(host_obj_list)
        return result[director], actor_obj_list, result[author]

    @classmethod
    def _get_list(cls, query_set, page=1, page_size=20):
        pager = Paginator(query_set, page_size)
        media_obj_list = pager.get_page(page)
        media_list = []
        for i in media_obj_list:
            _media = Media.to_dict(i)
            try:
                ext = MediaExtFieldModel.objects.get(media_id=i.id)
            except MediaExtFieldModel.DoesNotExist:
                ext = None
            media_ext = {
                'id': ext.id if ext else "",
                "media_id": i.id,
                'rating': ext.rating if ext else "",
                'focus': ext.focus if ext else "",
                'approval_num': ext.approval_num if ext else "",
                'format': ext.format if ext else ""
            }

            if ext and ext.media_verpic:
                media_ext["media_verpic"] = ext.media_verpic if ext else ""
                media_ext["media_horpic"] = ext.media_horpic if ext else ""
                media_ext["verpic_size"] = ext.verpic_size if ext else ""
                media_ext["horpic_size"] = ext.horpic_size if ext else ""
            else:
                media_ext["media_verpic"] = ext.media_verpic1 if ext else ""
                media_ext["media_horpic"] = ext.media_horpic1 if ext else ""
                media_ext["verpic_size"] = ext.verpic_size1 if ext else ""
                media_ext["horpic_size"] = ext.horpic_size1 if ext else ""

            _media["media_ext"] = media_ext
            media_list.append(_media)

        data = {
            "total_page": pager.num_pages,
            "media_list": media_list
        }
        return data

    @classmethod
    def get_list(cls, page=1, page_size=20):
        query = cls.model.objects.all().order_by("-id")
        return cls._get_list(query, page, page_size)

    @classmethod
    def search(cls, name="", start_year="", end_year="", region_id=0, category="", page=1, page_size=20):
        if not (name or start_year or end_year or region_id or category):
            return cls.get_list(page, page_size)

        query_data = {}
        if name:
            query_data["name__icontains"] = name

        if region_id:
            query_data["regions__contains"] = {"id": region_id}

        if category:
            query_data["category"] = category

        if start_year and end_year:
            if start_year == end_year:
                query_data.update({"year": int(start_year)})
            else:
                query_data.update({
                    "year__gte": int(start_year),
                    "year__lte": int(end_year)
                })

        if query_data:
            query = cls.model.objects.filter(**query_data).order_by("-id")
        else:
            query = cls.model.objects.all().order_by("-id")

        return cls._get_list(query, page, page_size)

    @classmethod
    def search_name_list(cls, name):
        """
        根据media名搜索，返回media名列表;已限制最多返回20条
        """
        if not name:
            return []

        query = cls.model.objects.values("name").distinct().filter(name__icontains=name)[0:20]
        name_list = [i["name"] for i in query] if query else []
        return name_list

    @classmethod
    def __process_data(cls, detail):
        genres = detail.pop("genres", "")
        if contains(genres, "，"):
            delimiter = "，"
        elif contains(genres, ","):
            delimiter = ","
        elif contains(genres, "、"):
            delimiter = "、"
        elif contains(genres, "/"):
            delimiter = "/"
        else:
            delimiter = " "

        genre_name_list = cls.str_to_list(genres, delimiter) if genres else []
        genre_list = Genre().get_json_by_name_list(genre_name_list) if genre_name_list else []
        detail["genres"] = genre_list

        regions = detail.pop("regions", "")
        region_name_list = cls.str_to_list(regions) if regions else []
        region_list = Region().get_json_by_name_list(region_name_list) if region_name_list else []
        detail["regions"] = region_list

        # 获取人物id
        detail['director_ids'] = Celebrity.get_ids_by_name(detail.pop("director"))
        detail['author_ids'] = Celebrity.get_ids_by_name(detail.pop('author'))
        detail['actor_ids'] = Celebrity.get_ids_by_name(detail.pop('actor'))

        return detail

    @classmethod
    def detail_product_media(cls, xid):
        pm = ProductMedia.objects.get(id=xid)
        if not pm.episodes_time and pm.episodes and pm.total_time:
            pm.episodes_time = round(pm.total_time / pm.episodes, 2)

        if not pm.total_time or not pm.episodes:
            pm.total_time = 1
            pm.episodes = 1
            pm.episodes_time = 1

        data = {
            'name': pm.name,
            'director': pm.director or [],
            'author': pm.writers or [],
            'actor': pm.actor or [],
            'genres': pm.theme,
            'seconds': pm.episodes_time * 60,
            'total_seconds': pm.total_time * 60,
            'category': CategoryValue.to_category(pm.get_channel()),
            'episodes': pm.episodes,
            'year': pm.production_time.replace('年', '') if pm.production_time.replace('年', '').isdigit() else 0,
            'date_published': pm.play_time or '',
            'regions': pm.area or "",
            'alias': pm.alias or "",
            'storyline': pm.plots or "",
            'languages': "",
            'douban_score': pm.rater,
            'img': pm.image

        }

        return cls.__process_data(data)

    @classmethod
    def rsync(cls, media_list):
        """

        :param list media_list: [
            {
                "name": "媒体名",
                "category": "类别",
                "year": "发行年份，int 默认 0",
                "date_published": "上映日期，%Y-%m-%d",
                "episodes": "集数 int",
                "seconds": "单集时长，单位秒，float类型",
                "total_seconds": "总时长，单位秒，float类型",
                "storyline": "剧情简介，默认空字符串",
                "program_id": "节目id  int"
            }
        ]
        :return:
        """
        obj_list = [cls.model(**media) for media in media_list]
        cls().bulk_create(obj_list)

    @classmethod
    def synchronous(cls, uid, data):
        """
        同步资产系统媒体到运营系统
        :param data: 资产系统媒体数据
        :param uid:
        :return:
        """
        logging.warning("synchronous_data:{}".format(data))
        for item in data['info']:
            data = Media.detail_product_media(item['id'])
            director_ids = data.pop('director_ids')
            author_ids = data.pop('author_ids')
            actor_ids = data.pop('actor_ids')

            data['user_id'] = uid
            data['program_id'] = str(item['id'])

            obj, created = cls.model.objects.filter(hidden=0, is_divided=False).update_or_create(
                program_id=item['id'], category=data['category'], defaults=data)
            # 插入media人物对应表
            if created:
                cls.bulk_set_celebrity_relation(obj, director_ids, author_ids, actor_ids)
            else:
                cls.update_celetrity_relation(obj.id, director_ids, author_ids, actor_ids)

            # 修改product_media中的字段
            ProductMedia.objects.filter(id=item['id']).update(synchronous=True)
        return

