import logging
import random

import requests
import xml.etree.ElementTree as ET
from domain.imdb.value_object import CelebrityRoleValue
from domain.lsp.generated.common_xsd import Adi
from domain.lsp.value_object import CodeFactory, C2ApiUtil
from imdb.models import CelebrityMediaRelationModel
from injector.models import MediaInfoSyncModel, TaskModel
from .base_injector import LspInjectorC2
from domain.injector.value_object import TaskStatusValue, DefinitionValue, ContentTypeValue

from utils.date_utils import datetime_now

logger = logging.getLogger(__file__)


# 定义 广西移动的 id、 code 模式
class CodeFactory:
    Series = "ps"  # 基础媒资信息id类别，供剧头使用
    Program = "p"  # 基础媒资信息id类别
    Movie = "m"  # 基础媒资分集id类别

    def __init__(self, csp_code=""):
        self.csp_code = csp_code

    def gen_series_code(self, series_id):
        flag_0 = (32 - len(str(series_id)) - len(self.Series) - len(self.csp_code) - 2) * "0"
        return "{}_{}{}_{}".format(self.Series, self.csp_code, flag_0, series_id)

    def gen_program_code(self, series_id, movie_id):
        flag_0 = (32 - len(str(series_id)) - len(str(movie_id)) - len(self.Program) - len(self.csp_code) - 3) * "0"
        return "{}_{}{}_{}_{}".format(self.Program, self.csp_code, flag_0, series_id, movie_id)

    def gen_movie_code(self, series_id, movie_id):
        flag_0 = (32 - len(str(series_id)) - len(str(movie_id)) - len(self.Program) - len(self.csp_code) - 3) * "0"
        return "{}_{}{}_{}_{}".format(self.Movie, self.csp_code, flag_0, series_id, movie_id)


# 定义广西移动注入类
class GuangXiYiDong(LspInjectorC2):

    def __init__(self, alias="GUANGXIYIDONG"):
        super().__init__(alias)
        self.lsp_id = self.conf.lsp_id
        self.csp_id = self.conf.csp_id
        self.cms_api = self.conf.cms_api
        self.csp_name = self.conf.csp_name
        self.cast_sep = "|"
        self.region_sep = "|"
        self.genre_sep = "|"
        self.tag_sep = ","
        self.cps_code = "77777111"  # 这个找LSP确认
        self.code_factory = CodeFactory(self.cps_code)
        self.desc_prefix = "（来源：森宇文化）"
        self.try_count = 3
        self.type_num_mapper = {"H.264": 1, "MPEG4": 2, "AVS": 3, "MPEG2": 4, "MP3": 5, "H.265": 7, "AVS2": 8}
        self.resolution_num_mapper = {"QCIF": 1, "QVGA": 2, "2/3 D1": 3, "3/4 D1": 4, "D1": 5, "720P": 6, "1080i": 7,
                                      "1080P": 8, "4K(3840*2160)": 9, "4K(4096*2160)": 10}

    def get_stbld_nums(self):
        the_num = TaskModel.objects.filter(partner_id=self.lsp_pk).count()
        if len(str(the_num)) <= 5:
            return str(the_num).rjust(5, "0")
        elif len(str(the_num)) <= 7:
            return str(the_num).rjust(7, "0")
        else:
            return "{}{}".format(random.randint(10, 99), str(the_num)[-5:])

    def get_content_type(self, content_type):
        return {
            ContentTypeValue.POSITIVE: "正片",
            ContentTypeValue.CLIP: "花絮",
            ContentTypeValue.TRAILER: "预告片"
        }.get(content_type, "正片")

    # 清晰度（1:高清HD、2:流畅LD、3:标清SD、4：XD，默认SD）
    def _get_bit_rate_type(self, definition):
        if definition == DefinitionValue.P320:
            return "LD"
        elif definition == DefinitionValue.P480:
            return "SD"
        elif definition == DefinitionValue.P720:
            return "HD"
        elif definition == DefinitionValue.P1080:
            return "XD"
        elif definition == DefinitionValue.K4:
            return "XD"
        return "SD"

    def _get_video_type(self, type):

        type_mapper = {"h264": "H.264", "hevc": "H.265", "mpeg4": "MPEG4", "mpeg2": "MPEG2", "avs": "AVS",
                       "avs2": "AVS2"}

        return self.type_num_mapper[type_mapper[type]]

    def _get_resolution(self, definition, width):
        def_mapper = {DefinitionValue.P320: "2/3 D1", DefinitionValue.P480: "D1", DefinitionValue.P720: "720P",
                      DefinitionValue.P1080: "1080P",
                      DefinitionValue.K4: "4K(3840*2160)" if width == 3840 else "4K(4096*2160)"}
        return self.resolution_num_mapper[def_mapper[definition]]

    def get_tags(self, media_obj):
        genres = media_obj.genres
        if genres and len(genres):
            names = [genre["name"] for genre in genres]
            return self.tag_sep.join(names)
        return ""

    def property_series(self, ps_id, media, media_ext, action="REGIST"):
        _actors = CelebrityMediaRelationModel.objects.filter(
            media_id=media.id, role=CelebrityRoleValue.ACTOR).select_related("celebrity")[:4]
        _directors = CelebrityMediaRelationModel.objects.filter(
            media_id=media.id, role=CelebrityRoleValue.DIRECTOR).select_related("celebrity")[:4]
        _writers = CelebrityMediaRelationModel.objects.filter(
            media_id=media.id, role=CelebrityRoleValue.AUTHOR).select_related("celebrity")[:2]
        actors, directors, writers = self.join_names(_actors), self.join_names(_directors), self.join_names(
            _writers)

        # 这个需要value_object.py文件中 LspExtendConfig类中配置 GUANGXIYIDONG属性 定义横图大小、竖图大小、分类
        media_verpic = self.image_resize.get_oss_resize_params(self.partner_alias, media_ext.media_verpic,
                                                               size=media_ext.verpic_size, to_ip=True)

        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)

        the_property = [
            {"Code": ps_id},
            {"ActorDisplay": directors},  # 要写导演 一会改动
            {"Definition": ""},
            {"PictureURL1": self.to_ip_url(media_verpic)},
            {"Name": self.get_media_name_by_alias(media)},
            {"Pinyin": ""},
            {"Language": media.languages},
            {"WriterDisplay": actors},  # 这里是主演
            {"Description": self.make_desc_prefix(media.storyline)},
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"ProgramType2": self.get_genre(media)},
            {"EnName": ""},
            {"ReleaseYear": media.year},
            {"VolumnCount": media.episodes},
            {"OrgAirDate": ""},
            {"Tag": ""},
            {"OriginalCountry": self.get_regions(media)},
            {"Score": media.douban_score},
            {"Duration": int(media.total_seconds / 60) + 1},
            {"ContentType": "Video"},
            {"IfPreview": 0},
            {"scriptWriter": writers},  # 这里要写编剧
            {"Recommendation": media_ext.focus},
            {"MediaCodeFormat": media_ext.format}
        ]
        return {
            "element_type": "Series",
            "id": ps_id,
            "code": ps_id,
            "action": action,
            "property": the_property
        }

    # 广西移动，要求简介 有前缀
    def make_desc_prefix(self, content):
        return self.desc_prefix + content

    # 广西移动只支持一个国家 目前处理是取第一个国家
    def get_regions(self, media_obj):
        regions = media_obj.regions
        if regions and len(regions):
            names = [region["name"] for region in regions]
            # 目前个别平台 需要改成内地，先分开处理,平台统一后在 统一处理
            if names[0] == "中国大陆":
                return "内地"
            return names[0]
        return ""

    # 定义剧头信息
    def _album_series_header(self, ps_id, media, media_ext, info_sync):
        # 定义ID、Action、Code
        action = info_sync.action_description()
        # 获取必要信息
        p_series = self.property_series(ps_id, media, media_ext, action)

        return {
            "object_list": [p_series],
            "mapping_list": []
        }

    # 定义剧头xml信息
    def album(self, _task_id, action="REGIST"):

        # 获取taskId
        task_id = str(_task_id).split("_")[0]

        # 从MediaInfo中获取media信息
        info_sync = MediaInfoSyncModel.objects.filter(pk=task_id).select_related("media").first()
        media = info_sync.media
        media_ext = media.mediaextfieldmodel
        ps_id = self.code_factory.gen_series_code(media.id)
        data = self._album_series_header(ps_id, media, media_ext, info_sync)
        logger.warning(data)
        xml = Adi.to_xml(data, flag="Nope")
        logger.warning(xml)
        return xml

    def property_movie(self, _id, media_storage, _type=1, action="REGIST"):
        return {
            "element_type": "Movie",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Code": _id},
                {"FileURL": self.change_storage_url(media_storage.url)},
                {"BitRateId": int(media_storage.bit_rate)},
                {"Definition": self._get_bit_rate_type(media_storage.definition)},
                {"Type": 1},
                {"CdnPlatform": 18},
                {"Dispatch": 0},
                {"IsTranscode": 0},
                {"VideoType": self._get_video_type(media_storage.v_codec)},
                {"BitRate": media_storage.bit_rate // 1024},
                {"Resolution": self._get_resolution(media_storage.definition, media_storage.width)},
                {"FrameRate": media_storage.frame_rate}
            ]
        }

    # 定义分集信息
    def _album_sub_series(self, media_storage, media, episode, media_ext, _episodes, action="REGIST"):

        # 还需要添加媒资信息
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        vst, vet = self.get_media_effective_time(media.id)
        media_verpic = self.image_resize.get_oss_resize_params(self.partner_alias, media_ext.media_verpic,
                                                               size=media_ext.verpic_size, to_ip=True)
        media_horpic = self.image_resize.get_oss_resize_params(self.partner_alias, media_ext.media_horpic,
                                                               size=media_ext.horpic_size, to_ip=True)
        definition = self._get_bit_rate_type(media_storage.definition)
        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)

        # 定义 media的 id
        psid = self.code_factory.gen_series_code(media.id)
        media_ext = media.mediaextfieldmodel
        p_series = self.property_series(psid, media, media_ext, action=action)
        p_series["property"].extend([
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"ActorDisplay": directors},
            {"WriterDisplay": actors},
            {"scriptWriter": writers},
            {"Definition": definition},
            {"IsCharge": str(change_fields["is_charge"])},
            {"PictureURL1": self.to_ip_url(media_verpic)},
            {"HorizontalPoster": self.to_ip_url(media_horpic)},
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet}
        ])

        # 分集的 父id  也就是分集所在媒体id  然后拼接成 一个字符串
        psid = self.code_factory.gen_series_code(media.id)
        # 返回 清晰度 转成需要的LD  SD  HD  XD
        definition = self._get_bit_rate_type(media_storage.definition)
        episode_title = self.get_episode_title(media, episode)
        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)

        ppid = self.code_factory.gen_program_code(media.id, media_storage.id)

        point_s = '[{"timePoint":"0:03:54","timePointEnd":"0:04:43","poster":"%s","tags":"%s","description":"%s"}]' % (
            self.to_ip_url(media_verpic), self.get_tags(media), media_ext.focus)
        # 定义 program 对象部分
        p_program = {
            "element_type": "Program",
            "id": ppid,
            "code": ppid,
            "action": action,
            "property": [
                {"Code": ppid},
                {"Name": episode_title},
                {"Description": ""},
                {"ProgramType2": self.get_genre(media)},
                {"IfPreview": 0},
                {"Definition": definition},
                {"Duration": int(media_storage.seconds / 60) + 1},
                {"VolumnCount": episode.serial_num},
                {"ProgramType": self._get_category_desc(change_fields["category"])},
                {"IsCharge": str(change_fields["is_charge"])},
                {"OriginalCountry": self.get_regions(media)},
                {'Seriesflag': 1},
                {"VideoType": self.get_content_type(media_storage.content_type)},
                {
                    "Points": point_s
                }
            ]
        }

        # 定义program与series的关系
        mapping_program = {
            "parent_type": "Series",
            "parent_id": psid,
            "element_type": "Program",
            "element_id": ppid,
            "parent_code": psid,
            "element_code": ppid,
            "action": action
        }
        sub_property = list()
        if episode.serial_num is not None:
            sub_property.extend([{"Sequence": episode.serial_num}, ])
        if "Series" == "Picture":
            sub_property.extend([{"Type": "Series"}, ])
        if sub_property:
            mapping_program["property"] = sub_property

        # 定义 program 与movie的关系
        pmid = self.code_factory.gen_movie_code(media.id, media_storage.id)
        p_movie = self.property_movie(pmid, media_storage, action=action)
        p_movie["property"].extend([{"PlayURL": "{}?stbld={}".format(media_storage.url, self.get_stbld_nums())}])
        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, sequence=episode.serial_num,
                                              action=action)
        mapping_package1 = self.property_mapping("Package", "Program", "77720239831", ppid, 1, action=action)
        mapping_package2 = self.property_mapping("Package", "Program", "77720239832", ppid, 2, action=action)

        return {
            "object_list": [p_series, p_program, p_movie],
            "mapping_list": [mapping_program, mapping_movie, mapping_package1, mapping_package2],
        }

    def property_program(self, _id, name, media, action="REGIST"):
        return {
            "element_type": "Program",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Code": _id},
                {"Name": name},
                {"Description": ""},
                {"ProgramType2": self.get_genre(media)},
                {"IfPreview": 0}
            ]
        }

    # 定义电影
    def _album_movie(self, media_storage, media, episode, media_ext, action):
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        vst, vet = self.get_media_effective_time(media.id)
        media_verpic = self.image_resize.get_oss_resize_params(self.partner_alias, media_ext.media_verpic,
                                                               size=media_ext.verpic_size, to_ip=True)
        media_horpic = self.image_resize.get_oss_resize_params(self.partner_alias, media_ext.media_horpic,
                                                               size=media_ext.horpic_size, to_ip=True)
        definition = self._get_bit_rate_type(media_storage.definition)
        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)

        # 定义 media的 id
        psid = self.code_factory.gen_series_code(media.id)
        media_ext = media.mediaextfieldmodel
        p_series = self.property_series(psid, media, media_ext, action=action)
        p_series["property"].extend([
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"IsCharge": str(change_fields["is_charge"])},
            {"HorizontalPoster": self.to_ip_url(media_horpic)},
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet}
        ])

        ppid = self.code_factory.gen_program_code(media.id, media_storage.id)
        p_program = self.property_program(ppid, self.get_media_name_by_alias(media), media, action)
        point_s = '[{"timePoint":"0:43:00","timePointEnd":"0:44:15","poster":"%s","tags":"%s","description":"%s"}]' % (
            self.to_ip_url(media_verpic), self.get_tags(media), media_ext.focus)
        p_program["property"].extend([
            {"Definition": definition},
            {"Duration": int(media_storage.seconds / 60) + 1},
            {"VolumnCount": episode.serial_num},
            {"isCharge": str(change_fields["is_charge"])},
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"OriginalCountry": self.get_regions(media)},
            {"videoType": self.get_content_type(media_storage.content_type)},
            {
                "Points": point_s
            }
        ])

        mapping_program = self.property_mapping("Series", "Program", psid, ppid, sequence=episode.serial_num,
                                                action=action)

        pmid = self.code_factory.gen_movie_code(media.id, media_storage.id)
        p_movie = self.property_movie(pmid, media_storage, action=action)
        p_movie["property"].extend([{"PlayURL": "{}?stbld={}".format(media_storage.url, self.get_stbld_nums())}])
        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, sequence=episode.serial_num,

                                              action=action)

        mapping_package1 = self.property_mapping("Package", "Program", "77720239831", ppid, 1, action=action)
        mapping_package2 = self.property_mapping("Package", "Program", "77720239832", ppid, 2, action=action)

        return {
            "object_list": [p_series, p_program, p_movie],
            "mapping_list": [mapping_program, mapping_movie, mapping_package1, mapping_package2]
        }

    # 定义节目xml信息
    def program(self, _task_id, action="REGIST"):
        task_id, _ = _task_id.split("_", 1)
        task = TaskModel.objects.filter(pk=task_id).select_related("storage").first()
        media_storage = task.storage  # 媒介
        media = media_storage.media  # 媒体文件主体
        episode = media_storage.episode  # (分集)扩展信息
        media_ext = media.mediaextfieldmodel  # 媒体文件补充信息

        # 目前的设计是 任何媒介都要有1集  电影也要设置为1集
        _episodes = int(media.episodes) if media.episodes else 0
        if _episodes > 1:  # 如果大于1 表示 连续剧 则该介质是某集电视剧
            data = self._album_sub_series(media_storage, media, episode, media_ext, _episodes, action)
        else:  # 电影
            data = self._album_movie(media_storage, media, episode, media_ext, action)
        xml = Adi.to_xml(data, flag="Nope")
        logger.warning(xml)
        return xml

    # 剧头注入  向LSP发起xml内容
    def exec_sync_header_cmd(self, sync, count=0):
        count += 1
        _sync_id = self.generate_correlate_id(sync.id, stage="series")
        cmd_file_url = self.get_cmd_ftp_url(self.csp_id, _sync_id)
        sync_id = "{}_{}".format(_sync_id, datetime_now())

        xml = C2ApiUtil.REQUESTXML.format(self.csp_id, self.lsp_id, sync_id, cmd_file_url)
        logger.warning("{} exec_sync_header_cmd HEADER_ID: {}, Request: {}".format(self.lsp_id, sync.id, xml))
        headers = {"content-type": "text/xml", "SOAPAction": "application/soap+xml;charset=utf8"}
        response = requests.post(self.cms_api, data=xml, headers=headers).content
        logger.warning("{} exec_sync_header_cmd HEADER_ID: {}, Response: {}".format(self.lsp_id, sync.id, response))
        tree = ET.fromstring(response)
        msg = tree.find(".//ErrorDescription").text
        if msg.lower() == "success":
            status = MediaInfoSyncModel.SYNCING
        else:
            if str(msg).lower().__contains__("could not connect") and count < self.try_count:
                return self.exec_sync_header_cmd(sync, count)
            status = MediaInfoSyncModel.FAILED
        the_sync = MediaInfoSyncModel.objects.filter(pk=sync.id)
        if the_sync.first().status == MediaInfoSyncModel.PENDING:
            the_sync.update(status=status)
        return

    # 剧头注入处理
    def series_header_injector(self, sync):
        if sync.status == MediaInfoSyncModel.PENDING:
            header_id = self.generate_correlate_id(sync.id, stage="series")
            self.save_album_xml_file(self.csp_id, header_id, param="series")
            self.exec_sync_header_cmd(sync)
        return

    # 介质注入 向LSP发起xml内容
    def exec_cmd(self, task_id, count=0):
        count += 1
        _task_id = self.generate_correlate_id(task_id)
        cmdfileUrl = self.get_cmd_ftp_url(self.csp_id, _task_id)  # ftp协议
        xml = C2ApiUtil.REQUESTXML.format(self.csp_id, self.lsp_id, "{}_{}".format(_task_id, datetime_now()),
                                          cmdfileUrl)
        logger.warning("EXEC_CMD Request: {}".format(xml))
        headers = {"content-type": "text/xml", "SOAPAction": "application/soap+xml;charset=utf8"}
        response = requests.post(self.cms_api, data=xml, headers=headers).content
        logger.warning("EXEC_CMD Response: {}".format(response))
        tree = ET.fromstring(response)
        # code = tree.find(".//Result").text
        msg = tree.find(".//ErrorDescription").text

        if msg.lower() == "success":
            status = TaskStatusValue.FILE_TRANSFERING
            remark = ""
        else:
            if str(msg).lower().__contains__("could not connect") and count < self.try_count:
                return self.exec_cmd(task_id, count)
            status = TaskStatusValue.FILE_FAILED
            remark = msg

        TaskModel.objects.filter(pk=task_id).update(status=status, remark=remark)
        return status

    # 介质注入
    def injector(self, task):
        status, flag = 0, 0
        # 如果工单状态 在 pending 在就绪状态，我们就继续
        if task.status in (TaskStatusValue.FILE_PENDING, TaskStatusValue.INFO_PENDING):
            # 组成一新的task_id  在全局中唯一 taskId_00
            _task_id = self.generate_correlate_id(task.id)

            # 生成新的xml
            self.save_album_xml_file(self.csp_id, _task_id, param="program")
            status = self.exec_cmd(task.id)
            flag = self.get_continue_to_injector(status)
        return status, flag

    # 介质注入更新
    def update(self, task):
        if task.status in (TaskStatusValue.FILE_PENDING, TaskStatusValue.INFO_PENDING):
            _task_id = self.generate_correlate_id(task.id)
            self.save_album_xml_file(self.csp_id, _task_id, param="program", action="UPDATE")
            self.exec_cmd(task.id)

    # 介质注入删除
    def remove(self, task):
        if task.status in (TaskStatusValue.FILE_PENDING, TaskStatusValue.INFO_PENDING):
            _task_id = self.generate_correlate_id(task.id)
            self.save_album_xml_file(self.csp_id, _task_id, param="program", action="DELETE")
            self.exec_cmd(task.id)
            self.off_line_storage(task.partner_id, task.storage_id)
            logger.warning("{} 下线媒体: {}, task_id: {}".format(self.partner_alias, task.storage_id, task.id))

    # 更新 剧头
    def series_header_update(self, sync):
        self.series_header_injector(sync)

    # 删除 剧头  目前 广西移动 没有给cp 删除权限
    def series_header_remove(self, sync):
        self.series_header_injector(sync)
