import re
import time
import logging
import requests
import datetime
import os

from PIL import Image
from lxml import etree
from injector.models import TaskModel
from injector.models import MediaInfoSyncModel
from domain.lsp.generated.anhuizx_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 AnHuiYiDong(LspInjectorC2):

    def __init__(self, alias="ANHUIYIDONG"):
        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.cms_id = "120000000094"
        self.code_factory = CodeFactory(self.csp_id)
        self.image_file_dir = "/home/sftp-user/vdb/xml_home"
        self.cast_sep = "|"
        self.genre_sep = "|"

    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 zoom_picture(self, file_path, pic_url, size):
        tmp_file_name = file_path.split("/")[-1]
        relative_file_dir = file_path.split(tmp_file_name)[0]

        name, suffix = re.split("\.", tmp_file_name)
        width, height = size

        tmp_file_path = os.path.join(self.image_file_dir, file_path)
        file_dir = tmp_file_path.split(tmp_file_name)[0]

        file_name = "{}_{}x{}.{}".format(name, width, height, suffix)
        file_path = os.path.join(file_dir, file_name)

        if not os.path.exists(file_path):
            os.makedirs(file_dir, exist_ok=True)
            r = requests.get(pic_url)
            with open(tmp_file_path, "wb") as f:
                f.write(r.content)
            image = Image.open(tmp_file_path)
            # 打开指定路径的jpg图像文件
            out = image.resize((width, height))
            out.save(file_path)
            os.remove(tmp_file_path)
        return "{}{}".format(relative_file_dir, file_name)

    # 将图片地址转成ftp
    def to_picture_ftp_url(self, pic_url, size):
        file_path = re.split('/', pic_url, 3)[-1]
        # 判断图片是否存在，存在直接引用，不存在就创建
        file_path = self.zoom_picture(file_path, pic_url, size)
        storage_ftp_url = "ftp://{}:{}@{}:{}/{}".format("partner", "partner", "47.110.47.81",
                                                        "2121", file_path)
        return storage_ftp_url

    def bit_rate_transform(self, bit_rate):
        BIT_RATE_MAP = {1634.8: 1, 2150.4: 2, 8192: 3, 60: 4, 150: 5, 420: 6, 750: 7}
        BRM = [60, 150, 420, 750, 1634.8, 2150.4, 8192]
        _new_bit_poor = [abs(bit_rate - i) for i in BRM]
        min_poor = min(_new_bit_poor)
        min_bit = BRM[_new_bit_poor.index(min_poor)]
        return BIT_RATE_MAP.get(min_bit)

    def change_duration(self, seconds):

        return datetime.timedelta(seconds=seconds)

    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(24 - len(self.csp_id), "0")
        in_the = _OBJECT_MAP[category]
        before = str(self.cms_id)[-5:]
        return "{}{}{}{}".format(before, in_the, self.csp_id, after)

    def v_codec_transform(self, v_codec):
        V_CODEC_MAP = {
            "h264": 1,  # H.264
            "mpeg4": 2,  # MPEG4
            "mpeg2video": 3  # MPEG2
        }
        return V_CODEC_MAP.get(v_codec, 2)

    def property_series(self, _id, volumn_count, media, action="REGIST"):
        search_name = self.transform_chinese_to_letter(self.get_media_name_by_alias(media))
        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 = [
            {"Name": self.get_media_name_by_alias(media)},
            {"SearchName": search_name},
            {"OrgAirDate": "".join(media.date_published.split("-"))},
            {"VolumnCount": volumn_count},
            {"Status": 1},
            {"Description": media.storyline},
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"Reserve1": change_fields["is_charge"]},
            {"Bcharging": change_fields["is_charge"]},
            {"TitleSC": self.get_media_name_by_alias(media)},
            {"ScoreSC": media.douban_score}
        ]
        return {
            "element_type": "Series",
            "id": _id,
            "code": _id,
            "action": action,
            "property": the_property
        }

    def property_program(self, _id, name, media, action="REGIST"):
        search_name = self.transform_chinese_to_letter(name.replace(" ", ""))
        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)
        return {
            "element_type": "Program",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Name": name},
                {"SearchName": search_name},
                {"OriginalCountry": self.get_regions(media)},
                {"Language": media.languages},
                {"ReleaseYear": media.year},
                {"Description": media.storyline},
                {"Status": 1},
                {"ProgramType": self._get_category_desc(change_fields["category"])},
                {"Bcharging": change_fields["is_charge"]},  # 0 免费 1 收费
                {"Reserve1": change_fields["is_charge"]},
                {"Chargingmode": "1"},
                {"Tags": self.get_genre(media)},
                {"ProgramType2": self.get_genre(media)},
                {"VolumnCount": media.episodes}
            ]
        }

    def format_duration_rate(self, seconds, frame):
        duration = time.strftime("%H%M%S", time.gmtime(seconds))
        return "{}{}".format(duration, frame)

    def property_movie(self, _id, media_storage, _type=1, action="REGIST"):
        return {
            "element_type": "Movie",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Type": _type},
                {"FileURL": self.get_storage_ftp_url(media_storage.url)},
                {"AudioType": self.get_audio_type(media_storage.a_codec)},
                {"ClosedCaptioning": 1},
                {"Duration": self.format_duration_rate(media_storage.seconds, media_storage.frame_rate)},
                {"BitRateType": self.bit_rate_transform(media_storage.bit_rate)},
                {"VideoType": self.v_codec_transform(media_storage.v_codec)},
                {"AudioFormat": 2},
                {"Resolution": self._get_bit_rate_type(media_storage.definition)},
                {"SystemLayer": 1},
                {"FileSize": str(media_storage.size)}
            ]
        }

    def property_picture(self, _id, file_url, size, action="REGIST"):
        return {
            "element_type": "Picture",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"FileURL": self.to_picture_ftp_url(file_url, size)},
            ]
        }

    def _album_movie(self, media_storage, media, episode, media_ext, action="REGIST"):
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        vst, vet = self.get_episode_effective_time(media.id, episode.serial_num)

        media_verpic = media_ext.media_verpic
        media_horpic = media_ext.media_horpic
        size_verpic = self.image_resize.ANHUIYIDONG.get("verpic")
        size_horpic = self.image_resize.ANHUIYIDONG.get("horpic")

        _ppid = self.code_factory.gen_program_code(media.id)
        ppid = self.generate_code(_ppid, "Program")
        p_program = self.property_program(ppid, self.get_media_name_by_alias(media), media, action=action)
        p_program["property"].extend([
            {"ActorDisplay": actors},
            {"WriterDisplay": writers},
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
            {"SeriesFlag": 0},
            {"Kpeople": actors},
            {"Director": directors},
            {"Bpreview": self.get_content_type(media_storage.content_type)},
        ])

        _pmid = self.code_factory.gen_movie_code(media_storage.id)
        pmid = self.generate_code(_pmid, "Movie")
        p_movie = self.property_movie(pmid, media_storage, 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")
        _pspchid = self.code_factory.gen_media_hpic_code(media.id)
        pspchid = self.generate_code(_pspchid, "Picture")
        verpic_series_picture = self.property_picture(pspcvid, media_verpic, size_verpic, action=action)
        horpic_series_picture = self.property_picture(pspchid, media_horpic, size_horpic, action=action)
        mapping_series_ver_picture = self.property_mapping("Picture", "Program", pspcvid, ppid, _type=1,
                                                           action=action)
        mapping_series_hor_picture = self.property_mapping("Picture", "Program", pspchid, ppid, _type=99,
                                                           action=action)

        property_list = [{"ValidStart": vst}, {"ValidEnd": vet}]
        mapping_package_series = self.property_mapping_free("Package", "Program", self.csp_name, ppid, property_list,
                                                            action=action)

        return {
            "domain": "2",
            "object_list": [p_program, p_movie, verpic_series_picture, horpic_series_picture],
            "mapping_list": [mapping_movie, mapping_series_ver_picture, mapping_series_hor_picture,
                             mapping_package_series],
        }

    def property_mapping_free(self, p_type, e_type, pid, eid, property_list=None, action="REGIST"):
        the_map = {
            "parent_type": p_type,
            "parent_id": pid,
            "element_type": e_type,
            "element_id": eid,
            "parent_code": pid,
            "element_code": eid,
            "action": action
        }
        if property_list:
            the_map["property"] = property_list
        return the_map

    def _album_series_header(self, media, media_ext, info_sync):
        action = info_sync.action_description()
        _episodes = int(media.episodes) if media.episodes else 0
        vst, vet = self.get_media_effective_time(media.id)
        media_verpic = media_ext.media_verpic
        media_horpic = media_ext.media_horpic
        size_verpic = self.image_resize.ANHUIYIDONG.get("verpic")
        size_horpic = self.image_resize.ANHUIYIDONG.get("horpic")
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)

        _psid = self.code_factory.gen_series_code(media.id)
        psid = self.generate_code(_psid, "Series")
        p_series = self.property_series(psid, _episodes, media, action=action)
        p_series["property"].extend([
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
            {"Kpeople": actors},
            {"Director": directors},
            {"SeriesType": "0"},  # 0：连续剧，1：系列片
            {"Chargingmode": "1"},  # 0：单点 1：产品包  2：单点和产品包
            {"Tags": self.get_genre(media)},
            {"ProgramType2": self.get_genre(media)},
            {"ActorDisplay": directors},
            {"WriterDisplay": actors},
            {"ReleaseYear": media.year}
        ])

        _pspcvid = self.code_factory.gen_media_vpic_code(media.id)
        pspcvid = self.generate_code(_pspcvid, "Picture")
        _pspchid = self.code_factory.gen_media_hpic_code(media.id)
        pspchid = self.generate_code(_pspchid, "Picture")
        verpic_series_picture = self.property_picture(pspcvid, media_verpic, size_verpic, action=action)
        horpic_series_picture = self.property_picture(pspchid, media_horpic, size_horpic, action=action)
        mapping_series_ver_picture = self.property_mapping("Picture", "Series", pspcvid, psid, _type=1,
                                                           action=action)
        mapping_series_hor_picture = self.property_mapping("Picture", "Series", pspchid, psid, _type=99,
                                                           action=action)

        property_list = [{"ValidStart": vst}, {"ValidEnd": vet}]
        mapping_package_series = self.property_mapping_free("Package", "Series", self.csp_name, psid, property_list,
                                                            action=action)

        return {
            "domain": "2",
            "object_list": [p_series, verpic_series_picture, horpic_series_picture],
            "mapping_list": [mapping_series_ver_picture, mapping_series_hor_picture, mapping_package_series]
        }

    def _album_sub_series(self, media_storage, media, media_ext, episode, _episodes, action="REGIST"):
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        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")
        episode_title = self.get_episode_title(media, episode)

        _ppid = self.code_factory.gen_program_code(media_storage.id)
        ppid = self.generate_code(_ppid, "Program")
        p_program = self.property_program(ppid, episode_title, media, action=action)
        p_program["property"].extend([
            {"ActorDisplay": actors},
            {"WriterDisplay": writers},
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
            {"SeriesFlag": 1},
            {"Director": directors},
            {"Bpreview": self.get_content_type(media_storage.content_type)},
        ])

        _pmid = self.code_factory.gen_movie_code(media_storage.id)
        pmid = self.generate_code(_pmid, "Movie")
        p_movie = self.property_movie(pmid, media_storage, 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")
        return 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  # 媒体文件补充信息

        _episodes = int(media.episodes) if media.episodes else 0
        if _episodes > 1:
            data = self._album_sub_series(media_storage, media, media_ext, episode, _episodes, action)
        else:  # 单剧集
            data = self._album_movie(media_storage, media, episode, media_ext, action)
        xml = Adi.to_xml(data, flag="Nope")
        return 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")
            status = self.exec_cmd(task.id)
            flag = self.get_continue_to_injector(status)
        return status, flag

    def update(self, task):  # 要求 子集、单集 更新 也是 REGIST
        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="REGIST")
            self.exec_cmd(task.id)

    # 不接受删除工单
    def remove(self, task):
        pass

    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)

    def series_header_update(self, sync):
        self.series_header_injector(sync)

    # 不接受删除工单
    def series_header_remove(self, sync):
        pass
