import logging
import os

import requests
from lxml import etree
from injector.models import TaskModel
from injector.models import MediaInfoSyncModel
from domain.lsp.generated.common_xsd import Adi
from domain.lsp.base_injector import LspInjectorC2
from domain.lsp.value_object import C2ApiUtil, CodeFactory
from domain.injector.value_object import TaskStatusValue, ContentTypeValue
from domain.injector.value_object import DefinitionValue

from utils.date_utils import datetime_now

logger = logging.getLogger(__file__)


class ShanXiDianXin(LspInjectorC2):

    def __init__(self, alias="SHANXIDIANXIN"):
        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.code_factory = CodeFactory(self.csp_id)
        self.cast_sep = " "
        self.genre_sep = " "
        # self.tmp_ftp_path = "ftp://iptv:iptv@10.197.129.4:21//opt/wacos/nas19/senyu/"
        self.tmp_ftp_path = "ftp://scmsftp:scmsftp_2023!@10.197.129.150:21//opt/scms/nas07/"
        # self.tmp_date = "20221125/"
        self.tmp_date = "20230721/"

    def get_content_type(self, content_type):
        return {
            ContentTypeValue.POSITIVE: "0",
            ContentTypeValue.CLIP: "1",
            ContentTypeValue.TRAILER: "1"
        }.get(content_type, "0")

    def _get_bit_rate_type(self, definition):
        BIT_RATE_TYPE_MAP = {
            DefinitionValue.P320: 3,  # 2/3 D1
            DefinitionValue.P480: 5,  # D1
            DefinitionValue.P720: 6,  # 720P
            DefinitionValue.P1080: 8,  # 1080P
            DefinitionValue.K4: 8,  # 4K 对方没有展示设置为1080P
        }
        return BIT_RATE_TYPE_MAP.get(definition, 3)

    def generate_code(self, _code, category):
        """
        安徽移动新发布code规范,规范{0}{1}{2}，不足补在{2}之前补0, 32位
        0：CMSID后5位
        1: 对象类型对应,见Object_map
        2: 自己平台编码
        """
        _OBJECT_MAP = {
            "Program": "001",
            "Movie": "002",
            "Cast": "003",
            "CastRoleMap": "004",
            "Channel": "005",
            "PhysicalChannel": "006",
            "Schedule": "007",
            "Picture": "008",
            "Category": "009",
            "Series": "010",
            "Package": "011"
        }
        after_list = _code.split("-")[:2]
        after = "".join(after_list).rjust(25, "0")
        in_the = _OBJECT_MAP[category]
        before = str(self.csp_id)
        return "{}{}{}{}".format(before, in_the, self.csp_id, after)

    def _get_bit_rate_type(self, definition):
        if definition == DefinitionValue.P480:
            return "2/3D1"
        elif definition == DefinitionValue.P720:
            return "720P"
        elif definition == DefinitionValue.P1080:
            return "1080i"
        elif definition == DefinitionValue.K4:
            return "3840i(4K)"
        return "1080i"

    def _get_v_code(self, v_code):
        if v_code == "h264":
            return "H.264"
        return v_code.upper()

    def gen_media_spec(self, storage):
        # TS-CBR-H.264-8008-1080i-25-MP2-128
        resolution = self._get_bit_rate_type(storage.definition)
        a_bit_rate = storage.a_bit_rate // 1000
        bit_rate = storage.bit_rate // 1000
        v_code = self._get_v_code(storage.v_codec)
        frame_rate = storage.frame_rate if storage.frame_rate else "25"
        return "TS-CBR-H.264-1080i-AAC"

    def property_common(self, media):
        search_name = self.transform_chinese_to_letter(self.get_media_name_by_alias(media))
        vst, vet = self.get_media_effective_time(media.id)
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        the_property = [{"LicensingWindowStart": vst},
                        {"LicensingWindowEnd": vet},
                        {"ContentProvider": self.csp_id},
                        {"SearchName": search_name},
                        {"Kpeople": actors},
                        {"ActorDisplay": actors},
                        {"Director": directors},
                        {"Description": media.storyline},
                        {"OriginalCountry": self.get_regions(media)},
                        {"Language": media.languages},
                        {"ReleaseYear": media.year},
                        {"OrgAirDate": "".join(media.date_published.split("-"))},
                        {"Status": 1},
                        {"Genre": self.get_genre(media)},
                        {"Reserve1": ""},
                        {"CopyRight": self.csp_id},
                        {"LabelType": "2"}]
        return the_property

    def to_picture_url_constant(self, name):
        name = self.transform_chinese_to_letter(name)
        suffix = ".jpg"
        return "{}{}hb/{}{}".format(self.tmp_ftp_path, self.tmp_date, name, suffix)

    def to_video_url_constant(self, name, num):
        name = self.transform_chinese_to_letter(name)
        suffix = ".ts"
        sub_path = "{}/".format(name)
        return "{}{}{}{}{}{}".format(self.tmp_ftp_path, self.tmp_date, sub_path, name, num, suffix)

    def property_movie(self, _id, media_storage, name, serial_num, _type=1, action="REGIST"):
        return {
            "element_type": "Movie",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Type": _type},
                {"FileURL": self.to_video_url_constant(name, serial_num)},
                {"MediaSpec": self.gen_media_spec(media_storage)}
            ]
        }

    def property_picture(self, _id, file_url, size, media_name, action="REGIST"):
        return {
            "element_type": "Picture",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"FileURL": self.to_picture_url_constant(media_name)},
            ]
        }

    def _album_movie(self, media_storage, media, media_ext, episode, action="REGIST"):
        media_verpic = media_ext.media_verpic
        size_verpic = self.image_resize.ANHUIYIDONG.get("verpic")

        _ppid = self.code_factory.gen_program_code(media.id)
        ppid = self.generate_code(_ppid, "Program")
        the_property = self.property_common(media)
        the_property.extend([
            {"Name": self.get_media_name_by_alias(media)},
            {"SeriesFlag": 0},
            {"Duration": int(media_storage.seconds / 60) + 1}
        ])
        p_program = {
            "element_type": "Program",
            "id": ppid,
            "code": ppid,
            "action": action,
            "property": the_property
        }

        _pmid = self.code_factory.gen_movie_code(media_storage.id)

        pmid = self.generate_code(_pmid, "Movie")
        serial_num = episode.serial_num
        serial_num = "0{}".format(serial_num) if len(str(serial_num)) == 1 else str(serial_num)
        p_movie = self.property_movie(pmid, media_storage, self.get_media_name_by_alias(media), serial_num, action=action)
        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, action=action)

        _pspcvid = self.code_factory.gen_media_vpic_code(media.id)
        pspcvid = self.generate_code(_pspcvid, "Picture")
        verpic_series_picture = self.property_picture(pspcvid, media_verpic, size_verpic, self.get_media_name_by_alias(media), action=action)
        mapping_series_ver_picture = self.property_mapping("Picture", "Program", pspcvid, ppid, _type=1,
                                                           action=action)

        return {
            "domain": "2",
            "object_list": [p_program, p_movie, verpic_series_picture],
            "mapping_list": [mapping_movie, mapping_series_ver_picture]
        }

    def _album_series_header(self, media, media_ext, info_sync):
        action = info_sync.action_description()
        volumn_count = int(media.episodes) if media.episodes else 0
        media_verpic = media_ext.media_verpic
        size_verpic = self.image_resize.ANHUIYIDONG.get("verpic")
        _psid = self.code_factory.gen_series_code(media.id)
        psid = self.generate_code(_psid, "Series")
        the_property = self.property_common(media)
        the_property.extend([
            {"Name": self.get_media_name_by_alias(media)},
            {"VolumnCount": volumn_count},
            {"SeriesType": "0"},  # 0：连续剧，1：系列片
        ])
        p_series = {
            "element_type": "Series",
            "id": psid,
            "code": psid,
            "action": action,
            "property": the_property
        }

        _pspcvid = self.code_factory.gen_media_vpic_code(media.id)
        pspcvid = self.generate_code(_pspcvid, "Picture")
        verpic_series_picture = self.property_picture(pspcvid, media_verpic, size_verpic, self.get_media_name_by_alias(media), action=action)
        mapping_series_ver_picture = self.property_mapping("Picture", "Series", pspcvid, psid, _type=1,
                                                           action=action)

        return {
            "domain": "2",
            "object_list": [p_series, verpic_series_picture],
            "mapping_list": [mapping_series_ver_picture]
        }

    def _album_sub_series(self, media_storage, media, episode, action="REGIST"):
        vst, vet = self.get_episode_effective_time(media.id, episode.serial_num)

        _psid = self.code_factory.gen_series_code(media.id)
        psid = self.generate_code(_psid, "Series")

        _ppid = self.code_factory.gen_program_code(media_storage.id)
        ppid = self.generate_code(_ppid, "Program")
        serial_num = episode.serial_num
        serial_num = "0{}".format(serial_num) if len(str(serial_num)) == 1 else str(serial_num)
        the_property = [
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
            {"Name": "{} {}".format(self.get_media_name_by_alias(media), serial_num)},
            {"SeriesFlag": 1},
            {"SourceType": "1"},
            {"Status": 1},
            {"OrgAirDate": "".join(media.date_published.split("-"))},
            {"EpisodeNum": media.episodes},
            {"EpisodeIndex": episode.serial_num},
            {"LabelType": "2"},
            {"Reserve1": ""},
            {"Duration": int(media_storage.seconds / 60) + 1}
        ]

        p_program = {
            "element_type": "Program",
            "id": ppid,
            "code": ppid,
            "action": action,
            "property": the_property
        }

        _pmid = self.code_factory.gen_movie_code(media_storage.id)
        pmid = self.generate_code(_pmid, "Movie")
        p_movie = self.property_movie(pmid, media_storage, self.get_media_name_by_alias(media), serial_num, action=action)
        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, action=action)

        mapping_series = self.property_mapping("Series", "Program", psid, ppid, sequence=episode.serial_num,
                                               action=action)

        return {
            "domain": "2",
            "object_list": [p_program, p_movie],
            "mapping_list": [mapping_series, mapping_movie],
        }

    def album(self, _header_id, action="REGIST"):
        header_id, _ = _header_id.split("_", 1)
        info_sync = MediaInfoSyncModel.objects.filter(pk=header_id).select_related("media").first()
        media = info_sync.media
        media_ext = media.mediaextfieldmodel
        data = self._album_series_header(media, media_ext, info_sync)
        xml = Adi.to_xml(data, flag="Nope")
        search_name = self.transform_chinese_to_letter(self.get_media_name_by_alias(media))
        return xml, search_name, ""

    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
        if _episodes > 1:
            data = self._album_sub_series(media_storage, media, episode, action)
        else:  # 单剧集
            data = self._album_movie(media_storage, media, media_ext, episode, action)
        xml = Adi.to_xml(data, flag="Nope")
        search_name = self.transform_chinese_to_letter(self.get_media_name_by_alias(media))
        serial_num = episode.serial_num
        serial_num = "0{}".format(serial_num) if len(str(serial_num)) == 1 else str(serial_num)
        return xml, search_name, serial_num

    def save_album_xml_file(self, csp_id, cmd_id, param="series", action="REGIST"):
        xml, search_name, serial_num = self.album(cmd_id, action=action) if param == "series" else self.program(cmd_id,
                                                                                                                action)

        print(xml)

        dir_path = "{}/{}/{}".format(self.XML_FILE_DIR, self.partner_alias, self.tmp_date)
        file_name = "{}{}.xml".format(search_name, serial_num)
        file_path = "{}/{}".format(dir_path, file_name)

        if not os.path.exists(dir_path):
            os.makedirs(dir_path, exist_ok=True)

        with open(file_path, "w") as f:
            f.write(xml)

    def exec_cmd(self, task_id):
        _task_id = self.generate_correlate_id(task_id)
        cmd_file_url = self.get_cmd_ftp_url(self.csp_id, _task_id)
        xml = C2ApiUtil.REQUESTXML.format(self.csp_id, self.lsp_id, "{}_{}".format(_task_id, datetime_now()),
                                          cmd_file_url)
        logger.warning("{} EXEC_CMD Request: {}".format(self.lsp_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_CMD Response: {}".format(self.lsp_id, response))
        tree = etree.fromstring(response)

        code = tree.findall(".//multiRef")[1].text
        desc = tree.find(".//ErrorDescription").text
        if int(code) == 0:
            status = TaskStatusValue.INFO_TRANSFERING
            remark = ""
        else:
            status = TaskStatusValue.INFO_FAILED
            remark = desc

        the_task = TaskModel.objects.filter(pk=task_id)
        if the_task.first().status == TaskStatusValue.FILE_PENDING:
            the_task.update(status=status, remark=remark)
        return status

    def exec_sync_header_cmd(self, sync):
        _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 = etree.fromstring(response)
        code = tree.findall(".//multiRef")[1].text
        if int(code) == 0:
            status = MediaInfoSyncModel.SYNCING
        else:
            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 injector(self, task):
        status, flag = 0, 0
        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")
            # self.exec_cmd(task.id)
            #  不去执行注入操作
            task.status = TaskStatusValue.INFO_COMPLETED
            task.remark = "工单已生成"
            task.save()
        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)
            #  不去执行注入操作
            task.status = TaskStatusValue.INFO_COMPLETED
            task.remark = "工单已生成"
            task.save()

    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")
            #  不去执行注入操作
            task.status = TaskStatusValue.INFO_COMPLETED
            task.remark = "工单已生成"
            task.save()

    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)
            sync.status = MediaInfoSyncModel.FINISH
            sync.remark = "工单已生成"
            sync.save()

    def series_header_update(self, sync):
        self.series_header_injector(sync)

    def series_header_remove(self, sync):
        self.series_header_injector(sync)
