import logging
import re

import requests

from domain.imdb.value_object import CelebrityRoleValue
from domain.injector.value_object import TaskStatusValue, DefinitionValue
from domain.lsp.base_injector import LspInjectorC2
from domain.lsp.value_object import C2ApiUtil
from imdb.models import CelebrityMediaRelationModel
from injector.models import TaskModel
import xml.etree.ElementTree as ET
from utils.date_utils import datetime_now
from domain.lsp.generated.common_xsd import Adi
import json

logger = logging.getLogger(__file__)


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 GanSuYiDong(LspInjectorC2):
    Video_Required_Args = ("url", "a_codec", "definition", "md5")

    def __init__(self, alias="GANSUYIDONG"):
        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 = "GS_SHSY"  # 这个找LSP确认
        self.code_factory = CodeFactory(self.cps_code)

    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 cdata_round(self, data):
        return "<![CDATA[{}]]>".format(data)

    def property_movie(self, _id, media_storage, _type=1, action="REGIST"):
        play_url = re.sub("\d+\.\d+\.\d+\.\d+:\d+", "xx.xx.xx.xx", media_storage.url)
        return {
            "element_type": "Movie",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"FileURL": self.cdata_round(self.change_storage_url(media_storage.url))},
                {"PlayURL": self.cdata_round("{}?Contentid={}".format(media_storage.url, self.get_stbld_nums()))},
                {"Definition": self._get_bit_rate_type(media_storage.definition)},
                {"CdnPlatform": 164},
                {"Md5": self.cdata_round(media_storage.md5)},
                {"Dispatch": 1},
                {"IsTranscode": 0}
            ]
        }

    def get_regions(self, media_obj):
        regions = media_obj.regions
        if regions and len(regions):
            names = ["内地" if region["id"] == 1 else region["name"] for region in regions]
            return self.region_sep.join(names)
        return ""

    def property_series(self, ps_id, media_storage, 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)

        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)

        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)
        definition = self._get_bit_rate_type(media_storage.definition)
        vst, vet = self.get_media_effective_time_line(media.id)

        the_property = [
            {'Code': ps_id},
            {"ActorDisplay": directors},  # 要写导演
            {"Definition": definition},
            {"PictureURL1": self.cdata_round(self.to_ip_url(media_verpic))},
            {"Name": self.get_media_name_by_alias(media)},
            {"HorizontalPoster": self.cdata_round(self.to_ip_url(media_horpic))},
            {"Pinyin": ""},
            {"Language": media.languages},
            {"WriterDisplay": actors},  # 这里是主演
            {"Description": 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)},
            {"IsCharge": str(change_fields["is_charge"])},
            {"Score": media.douban_score},
            {"Duration": int(media.total_seconds / 60) + 1},
            {"ContentType": "Video"},
            {"CopyRightBeginDate": vst},
            {"CopyRightEndDate": vet},
            {"IfPreview": 0},
            {"scriptWriter": writers},  # 这里要写编剧
        ]
        return {
            "element_type": "Series",
            "id": ps_id,
            "code": ps_id,
            "action": action,
            "property": the_property
        }

    def _album_sub_series(self, media_storage, media, episode, media_ext, _episodes, action="REGIST"):
        # 还需要添加媒资信息

        # 定义 media的 id
        psid = self.code_factory.gen_series_code(media.id)
        media_ext = media.mediaextfieldmodel
        p_series = self.property_series(psid, media_storage, media, media_ext, action=action)

        # 分集的 父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)

        episode_horpic = self.image_resize.get_oss_resize_params(self.partner_alias, episode.episode_horpic,
                                                                 size=episode.horpic_size, to_ip=True)
        # 定义 program 对象部分
        p_program = {
            "element_type": "Program",
            "id": ppid,
            "code": ppid,
            "action": action,
            "property": [
                {"Definition": definition},
                {"Name": episode_title},
                {"VolumnCount": episode.serial_num},
                {"ProgramType": self._get_category_desc(change_fields["category"])},
                {"ProgramType2": self.get_genre(media)},
                {"VideoType": self.get_content_type(media_storage.content_type)},
                {"Introduction": episode.storyline if episode.storyline else media.storyline},
                {'Seriesflag': 0 if media.episodes == 1 else 1},
            ]
        }

        # 定义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)

        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, sequence=episode.serial_num,
                                              action=action)

        return {
            "object_list": [p_series, p_program, p_movie],
            "mapping_list": [mapping_program, mapping_movie],
        }

    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  # 媒体文件补充信息
        _episodes = int(media.episodes) if media.episodes else 0
        data = self._album_sub_series(media_storage, media, episode, media_ext, _episodes, action)
        xml = Adi.to_xml(data, flag="Nope")
        return xml

    # 介质注入 向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 int(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 callback_cdn(self, body):
        logging.info("callback_cdn:{}".format(body))
        try:
            ss = json.loads(body)
            media_id, storage_id = str(ss["mediaId"]).split("_")[-2:]
            # 做cdn备注
            TaskModel.objects.filter(partner_id=self.conf.id, storage_id=storage_id, media_id=media_id).update(
                **{"remark": "cdn"})
        except Exception as e:
            print("cdn的回执 解析失败")
            print(e)
        return {"code": 1, "message": "成功"}

    # 介质注入
    def injector(self, task):
        status, flag = 0, 0
        # 如果工单状态 在 pending 在就绪状态，我们就继续
        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")
            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)
            logger.warning("{} 更新媒体: {}, task_id: {}".format(self.partner_alias, task.storage_id, 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))
