import logging
import math

import requests

from domain.injector.value_object import DefinitionValue, TaskModelActionValue
from domain.lsp.base_injector import LspInjectorC2, TaskStatusValue, C2ApiUtil, TaskModel
from utils.date_utils import datetime_now
from lxml import etree
from injector.models import MediaInfoSyncModel
from domain.lsp.generated.common_xsd import Adi
from domain.lsp.value_object import C2ApiUtil, CodeSampleFactory

logger = logging.getLogger(__file__)


class CHongQingYdWl(LspInjectorC2):
    Media_Required_Args = ("episodes", "name", "date_published", "category", "genres", "regions",
                           "storyline", "total_seconds", "languages", "year", "douban_score")
    MediaExt_Required_Args = ("media_horpic", "definition")

    Episode_Required_Args = ("serial_num",)

    def __init__(self, alias="CHONGQINGYDWL"):
        super().__init__(alias)
        self.csp_id = self.conf.csp_id
        self.lsp_id = self.conf.lsp_id
        self.cast_sep = "|"
        self.region_sep = "|"
        self.genre_sep = "|"
        self.tag_sep = "|"
        self.code_factory = CodeSampleFactory(30)

    def get_media_name(self, name):
        return str(name).strip()

    def gen_media_definition(self, definition):
        if definition == "1":
            return "LD"
        elif definition == "2":
            return "SD"
        elif definition == "3":
            return "HD"
        elif definition == "4":
            return "XD"
        elif definition == "5":
            return "4K"

    def gen_series_obj(self, _id, media, 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)
        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)

        media_horpic = self.image_resize.get_oss_resize_params(self.partner_alias, media_ext.media_horpic,
                                                               size=media_ext.horpic_size, to_ip=True)

        property_list = [
            {"ActorDisplay": directors},
            {"Definition": self.gen_media_definition(str(media_ext.definition))},
            {"PictureURL1": self.to_ip_url(media_verpic)},
            {"Name": self.get_media_name(self.get_media_name_by_alias(media))},
            {"Language": media.languages},
            {"WriterDisplay": actors},
            {"Description": media.storyline},
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"ProgramType2": self.get_genre(media)},
            {"ReleaseYear": media.year},
            {"VolumnCount": media.episodes},
            {"OriginalCountry": self.get_regions(media)},
            {"Score": media.douban_score},
            {"Duration": int(media.total_seconds / 60) + 1},
            {"ContentType": "Video"},
            {"IfPreview": 0},
            {"ScriptWriter": writers},
            {"IsCharge": change_fields["is_charge"]},
            {"HorizontalPoster": self.to_ip_url(media_horpic)},
            {"CopyRightBeginDate": vst},
            {"CopyRightEndDate": vet}
        ]

        data = {
            "element_type": "Series",
            "id": _id,
            "code": _id,
            "action": action,
            "property": property_list
        }
        return data

    def gen_series(self, media, media_ext, action):
        _psid = self.code_factory.gen_series_code(media.id)
        series_obj = self.gen_series_obj(_psid, media, media_ext, action)
        data = {
            "BizDomain": "2",
            "object_list": [series_obj],
            "mapping_list": []
        }
        return data

    def album(self, cmd_id, action):
        sync_id = cmd_id.split("_")[0]
        sync = MediaInfoSyncModel.objects.filter(pk=sync_id).select_related("media").first()
        media = sync.media
        media_ext = media.mediaextfieldmodel
        series_obj = self.gen_series(media, media_ext, action)
        xml = Adi.to_xml(series_obj, flag="Nope")
        return xml

    def gen_program_definition(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 in (DefinitionValue.K4, DefinitionValue.P3840, DefinitionValue.P4096):
            return "4K"
        return "SD"

    def gen_program_obj(self, _id, media, storage, episode, action):
        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)
        episode_title = self.get_episode_title(media, episode)

        property_list = [
            {"Definition": self.gen_program_definition(storage.definition)},
            {"Name": episode_title},
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"ProgramType2": self.get_genre(media)},
            {"VolumnCount": episode.serial_num},
            {"Duration": math.ceil(media.seconds / 60)},
            {"IsCharge": change_fields["is_charge"]},
            {"IfPreview": 0},
            {"Seriesflag": 1 if media.episodes > 1 else 0}
        ]

        data = {
            "element_type": "Program",
            "id": _id,
            "code": _id,
            "action": action,
            "property": property_list
        }
        return data

    def get_movie_codec(self, v_codec):
        if v_codec == "h264":
            return "1"
        elif v_codec == "hevc":
            return "7"

    def gen_movie_obj(self, _id, storage, action):

        property_list = [
            {"Definition": self.gen_program_definition(storage.definition)},
            {"FileURL": self.change_storage_url(storage.url)},
            {"CdnPlatform": 162},
            {"Dispatch": 1},
            {"VideoType": self.get_movie_codec(storage.v_codec)}
        ]

        data = {
            "element_type": "Movie",
            "id": _id,
            "code": _id,
            "action": action,
            "property": property_list
        }
        return data

    def gen_sub_program(self, media, storage, episode, action):
        psid = self.code_factory.gen_series_code(media.id)
        ppid = self.code_factory.gen_program_code(episode.id)
        pmid = self.code_factory.gen_movie_code(storage.id)
        program_obj = self.gen_program_obj(ppid, media, storage, episode, action)
        movie_obj = self.gen_movie_obj(pmid, storage, action)
        series_program_mapping = self.property_mapping("Series", "Program", psid, ppid, sequence=episode.serial_num,
                                                       action=action)
        program_movie_mapping = self.property_mapping("Program", "Movie", ppid, pmid, action=action)

        data = {
            "BizDomain": "514",
            "object_list": [program_obj, movie_obj],
            "mapping_list": [series_program_mapping, program_movie_mapping]
        }
        return data

    def gen_program_code_by_movie(self, media):
        if media.episodes == 1:
            ppid = self.code_factory.gen_series_code(media.id)
            return ppid
        return "暂无"

    def gen_series_code_by_media(self, media_id):
        psid = self.code_factory.gen_series_code(media_id)
        return psid

    def gen_movie_program(self, media, media_ext, storage, episode, action):
        psid = self.code_factory.gen_series_code(media.id)
        ppid = self.code_factory.gen_program_code(episode.id)
        pmid = self.code_factory.gen_movie_code(storage.id)
        series_obj = self.gen_series_obj(psid, media, media_ext, action)
        program_obj = self.gen_program_obj(ppid, media, storage, episode, action)
        movie_obj = self.gen_movie_obj(pmid, storage, action)
        series_program_mapping = self.property_mapping("Series", "Program", psid, ppid, sequence=episode.serial_num,
                                                       action=action)
        program_movie_mapping = self.property_mapping("Program", "Movie", ppid, pmid, action=action)

        data = {
            "BizDomain": "514",
            "object_list": [series_obj, program_obj, movie_obj],
            "mapping_list": [series_program_mapping, program_movie_mapping]
        }
        return data

    def program(self, cmd_id, action):
        task_id = cmd_id.split("_")[0]
        task = TaskModel.objects.filter(pk=task_id).select_related("media", "storage", "episode").first()
        media = task.media
        media_ext = media.mediaextfieldmodel
        storage = task.storage
        episode = task.episode
        if media.episodes > 1:
            data = self.gen_sub_program(media, storage, episode, action)
        else:
            data = self.gen_movie_program(media, media_ext, storage, episode, action)
        xml = Adi.to_xml(data, flag="Nope")
        return xml

    def post_api(self, correlate_id, cmd_id):
        headers = {"content-type": "text/xml", "SOAPAction": "application/soap+xml;charset=utf8"}
        ftp_url = self.get_cmd_ftp_url(self.csp_id, cmd_id)
        data = C2ApiUtil.REQUESTXML.format(self.csp_id, self.lsp_id, correlate_id, ftp_url)
        logger.warning("{} exec_sync_header_cmd HEADER_ID: {}, Request: {}".format(self.lsp_id, cmd_id, data))
        try:
            content = requests.post(self.conf.cms_api, headers=headers, data=data).content
            logger.warning("{} exec_sync_header_cmd HEADER_ID: {}, Response: {}".format(self.lsp_id, cmd_id, content))
            tree = etree.fromstring(content)
            code = tree.find(".//Result").text.strip()
            msg = tree.find(".//ErrorDescription").text.strip()
        except Exception as e:
            logger.error("exception:{}".format(e))
            code = -1
            msg = str(e)
        return code, msg

    def exec_cmd(self, task_id, cmd_id):
        correlate_id = self.generate_correlate_id(task_id, "common", datetime_now())
        code, msg = self.post_api(correlate_id, cmd_id)

        if str(code) == "0":
            status = TaskStatusValue.INFO_TRANSFERING
            msg = ""
        else:
            status = TaskStatusValue.INFO_FAILED

        TaskModel.objects.filter(pk=task_id).update(status=status, remark=msg)
        return status

    # 子集注入
    def injector(self, task, action="REGIST"):
        status, flag = 0, 0
        if task.status in (TaskStatusValue.INFO_PENDING, TaskStatusValue.FILE_PENDING):
            # 生成xml
            cmd_id = self.generate_correlate_id(task.id)
            self.save_album_xml_file(self.csp_id, cmd_id, param="program", action=action)
            # 执行请求
            status = self.exec_cmd(task.id, cmd_id)
            # 执行是否结束
            flag = self.get_continue_to_injector(status)
        return status, flag

    def update(self, task):
        # self.injector(task, action="UPDATE")
        # self.update_storage(task.partner_id, task.storage_id)
        pass

    def remove(self, task):
        self.injector(task, action="DELETE")
        self.off_line_storage(task.partner_id, task.storage_id)

    def exec_header_cmd(self, sync_id, cmd_id):
        correlate_id = self.generate_correlate_id(sync_id, "series", datetime_now())
        code, msg = self.post_api(correlate_id, cmd_id)
        if str(code) == "0":
            status = MediaInfoSyncModel.SYNCING
            msg = ""
        else:
            status = MediaInfoSyncModel.FAILED

        MediaInfoSyncModel.objects.filter(pk=sync_id).update(status=status, remark=msg)
        return status

    # 剧头注入
    def series_header_injector(self, sync, action="REGIST"):
        if sync.status == MediaInfoSyncModel.PENDING:
            cmd_id = self.generate_correlate_id(sync.id, stage="series")
            self.save_album_xml_file(self.csp_id, cmd_id, param="series", action=action)
            # 执行请求
            status = self.exec_header_cmd(sync.id, cmd_id)
            return status

    # 更新剧头
    def series_header_update(self, sync):
        # self.series_header_injector(sync, action="UPDATE")
        pass

    # 删除剧头
    def series_header_remove(self, sync):
        self.series_header_injector(sync, action="DELETE")
