import logging
import random

import requests
import xml.etree.ElementTree as ET

from domain import domain_except
from domain.imdb.value_object import CelebrityRoleValue
from domain.lsp.generated.common_xsd import Adi
from domain.lsp.value_object import CodeSampleFactory, C2ApiUtil
from imdb.models import CelebrityMediaRelationModel
from injector.models import MediaInfoSyncModel, TaskModel, MediaExtFieldModel
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__)


class ShanXiWl(LspInjectorC2):
    Episode_Required_Args = ("serial_num",)

    def __init__(self, alias="SHANXIWL"):
        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.code_factory = CodeSampleFactory(self.csp_id)
        self.desc_prefix = "(来源于森宇)"
        # 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 2
        elif definition == DefinitionValue.P480:
            return 3
        elif definition == DefinitionValue.P720:
            return 1
        elif definition == DefinitionValue.P1080:
            return 4
        elif definition in (DefinitionValue.K4, DefinitionValue.P3840, DefinitionValue.P4096):
            return 5
        return 1

    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 gen_media_definition(self, definition):
        if definition == "1":
            return 2
        elif definition == "2":
            return 3
        elif definition == "3":
            return 1
        elif definition in ("4", "5"):
            return 4
        return 4

    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)
        if not actors:
            actors = "佚名"
        if not writers:
            writers = "佚名"
        if not directors:
            directors = "佚名"

        vst, vet = self.get_media_effective_time(media.id)

        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)

        # 获取必要信息
        media_verpic = self.image_resize.get_oss_resize_params(self.partner_alias, media_ext.media_verpic,
                                                               size=media_ext.verpic_size, to_ip=True)
        the_property = [
            {"ActorDisplay": directors},  # 要写导演 一会改动
            {"Definition": self.gen_media_definition(media_ext.definition)},
            {"PictureURL1": self.to_ip_url(media_verpic)},
            {"Name": self.get_media_name_by_alias(media)},
            {"Pinyin": self.transform_chinese_to_letter(self.get_media_name_by_alias(media))},
            {"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)},
            {"ReleaseYear": media.year},
            {"OriginalCountry": self.get_regions(media)},
            {"Score": media.douban_score},
            {"Duration": int(media.total_seconds / 60) + 1},
            {"ContentType": "Video"},
            {"IfPreview": 0},
            {"MediaCodeFormat": media_ext.format},
            {"VolumnCount": media.episodes},
            {"IsCharge": str(change_fields["is_charge"])},
            {"scriptWriter": writers},
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
        ]
        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 filter_cast(self, casts):
        pass

    def property_picture(self, _id, url, action="REGIST"):

        return {
            "element_type": "Picture",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"FileURL": self.to_ip_url(url)},
            ]
        }

    # 定义剧头信息
    def _album_series_header(self, media, media_ext, info_sync):
        # 定义ID、Action、Code
        action = info_sync.action_description()
        if action == "UPDATE":
            action = "REGIST"

        ps_id = self.code_factory.gen_series_code(media.id)
        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

        data = self._album_series_header(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)},
                {"Definition": self._get_bit_rate_type(media_storage.definition)},
                {"CdnPlatform": 163},
                {"Dispatch": 1},
                {"Md5": media_storage.md5}
            ]
        }

    # 定义分集信息
    def _album_sub_series(self, media_storage, media, episode, media_ext, _episodes, action="REGIST"):

        # 还需要添加媒资信息
        pmid = self.code_factory.gen_movie_code(media_storage.id)
        psid = self.code_factory.gen_series_code(media.id)

        ppid = self.code_factory.gen_program_code(media_storage.id)

        # 定义 program 对象部分
        p_program = self.property_program(ppid, media_storage, media, episode, action)
        p_movie = self.property_movie(pmid, media_storage, action=action)
        mapping_program = self.property_mapping("Series", "Program", psid, ppid, sequence=episode.serial_num,
                                                action=action)
        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, action=action)

        return {
            "object_list": [p_program, p_movie],
            "mapping_list": [mapping_program, mapping_movie]
        }

    def property_program(self, _id, media_storage, media, episode, action="REGIST"):
        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)
        p_program = {
            "element_type": "Program",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Definition": self._get_bit_rate_type(media_storage.definition)},
                {"Name": episode_title},
                {"VolumnCount": episode.serial_num},
                {"IsCharge": str(change_fields["is_charge"])},
                {"ProgramType": self._get_category_desc(change_fields["category"])},
                {"SeriesFlag": 0 if media.episodes == 1 else 1},
                {"ProgramType2": self.get_genre(media)}
            ]
        }
        return p_program

    # 定义电影
    def _album_movie(self, media_storage, media, episode, media_ext, action):

        ppid = self.code_factory.gen_program_code(media_storage.id)
        pmid = self.code_factory.gen_movie_code(media_storage.id)
        ps_id = self.code_factory.gen_series_code(media.id)
        p_series = self.property_series(ps_id, media, media_ext, action=action)
        p_program = self.property_program(ppid, media_storage, media, episode, action)

        p_movie = self.property_movie(pmid, media_storage, action=action)

        mapping_series_program = self.property_mapping("Series", "Program", ps_id, ppid, sequence=episode.serial_num,
                                                       action=action)
        mapping_program_movie = self.property_mapping("Program", "Movie", ppid, pmid, action=action)

        return {
            "object_list": [p_series, p_program, p_movie],
            "mapping_list": [mapping_series_program, mapping_program_movie]
        }

    # 定义节目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(".//Result").text
        if msg.lower() == "0":
            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).first()
        if the_sync.status == MediaInfoSyncModel.PENDING:
            the_sync.status = status
            the_sync.remark = msg
            the_sync.save()

        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 code == "0":
            status = TaskStatusValue.FILE_TRANSFERING
            remark = ""
        else:
            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)
