import logging
import requests
import pypinyin
from lxml import etree

from domain.lsp.base_injector import LspInjectorC2
from injector.models import TaskModel, MediaInfoSyncModel, MediaStorageModel
from domain.imdb.value_object import CategoryValue
from domain.lsp.value_object import C2ApiUtil, CodeFactory
from domain.lsp.generated.common_xsd import Adi
from domain.injector.value_object import TaskStatusValue, DefinitionValue, ContentTypeValue
from utils.date_utils import datetime_now
import math
import re

logger = logging.getLogger("django")


# 多个匹配结果(只针对两层，一层空一层真正)返回code
def return_code(codes):
    code = -1
    for c in codes:
        try:
            if int(c.text) != code:
                code = int(c.text)
        except TypeError:
            pass
        except ValueError:
            if c.text == "Success":
                code = 0
    return code


class HubeiPaLw(LspInjectorC2):

    def __init__(self, alias="HUBEIPALW"):
        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.code_factory = CodeFactory(self.csp_id)

        self.BIT_RATE_MAP = [1300, 2000, 2300, 4000, 6000, 8000, 14000, 25000]

    def _get_bit_rate_type(self, definition):
        if definition == DefinitionValue.P320:
            return 51
        elif definition == DefinitionValue.P480:
            return 52
        elif definition == DefinitionValue.P720:
            return 53
        elif definition == DefinitionValue.P1080:
            return 54
        elif definition == DefinitionValue.K4:
            return 56
        return 51

    def get_audio_type(self, a_codec):
        return 5 if a_codec == "ac3" else 2

    def _get_category_desc(self, category):
        if category == CategoryValue.CHILDREN:
            return "少儿"
        if category == CategoryValue.EDUCATION:
            return "教育"
        return CategoryValue.desc(category)

    def bit_rate_transform(self, bit_rate):
        BRM = self.BIT_RATE_MAP
        _new_bit_poor = [abs(bit_rate - i) for i in BRM]
        min_poor = min(_new_bit_poor)
        return BRM[_new_bit_poor.index(min_poor)]

    # 湖北目前用
    def _get_sub_category(self):
        sub_categorys = ["教育", "启蒙"]
        return self.genre_sep.join(sub_categorys)

    # 汉字转换成首字母小写拼音
    def transform_chinese_to_letter(self, the_str):
        _letter = pypinyin.pinyin(the_str, style=pypinyin.FIRST_LETTER)
        result = [str(_letter[index][0]).lower() for index, _ in enumerate(_letter)]
        return "".join(result)

    def _get_tags(self, media):
        return self.get_genre(media)

    def _nope_episode_title(self, media, episode):
        fmt = "第{}集"
        return fmt.format(episode.serial_num)

    def _get_positive(self, positive):
        return {
            ContentTypeValue.POSITIVE: 1,
            ContentTypeValue.CLIP: 3,
            ContentTypeValue.TRAILER: 2
        }.get(positive, 4)

    def _get_definition_flag(self, definition):
        if definition == DefinitionValue.P480:
            return 0
        elif definition == DefinitionValue.P720:
            return 1
        elif definition == DefinitionValue.P1080:
            return 2
        elif definition == DefinitionValue.K4:
            return 4
        return 0

    def _album_movie(self, storage, media, episode, media_ext, action, replace, limit=3):
        program_code = self.code_factory.gen_program_code(episode.id)
        movie_code = self.code_factory.gen_movie_code(storage.id)
        if replace:
            replace_storage = MediaStorageModel.objects.filter(id=replace).first()
            storage = replace_storage if replace_storage else storage
        series_picture_ver_code = self.code_factory.gen_media_vpic_code(media.id)
        series_picture_hor_code = self.code_factory.gen_media_hpic_code(media.id)
        vst, vet = self.get_media_effective_time(media.id)
        actors, directors, scriptwriters = self.get_media_person(media, actor=limit, director=limit, writer=limit)
        episode_name = self.get_episode_title(media, episode)
        btt = self._get_bit_rate_type(storage.definition)
        audio_type = self.get_audio_type(storage.a_codec)
        param_dict = {"category": media.category}
        _category = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)["category"]
        category = self._get_category_desc(_category)
        tags = self._get_tags(media)
        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)

        object_list = [

            {
                "property": [
                    {"Name": self.get_media_name_by_alias(media)},
                    {"LicensingWindowStart": vst},
                    {"LicensingWindowEnd": vet},
                    {"ReleaseYear": media.year},
                    {"OrgAirDate": media.date_published.replace("-", "")},
                    {"Status": 1},
                    {"Type": category},
                    {"length1": storage.seconds},
                    {"Duration": math.ceil(int(storage.seconds) / 60)},
                    {"ActorDisplay": actors},
                    {"DirectorDisplay": directors},
                    {"WriterDisplay": scriptwriters},
                    {"Description": "【来源于乐维】 {}".format(media.storyline)},
                    {"CpCode": self.csp_id},
                    {"Rating": media.douban_score},
                    {"Tags": tags},
                    {"PgmCategory": category},
                    {"PgmSndClass": tags},
                    {"SeriesFlag": 0 if media.episodes == 1 else 1},
                    {"Language": media.languages},
                    {"OriginalCountry": self.get_regions(media)},
                    {"SearchName": self.transform_chinese_to_letter(self.get_media_name_by_alias(media))},
                    {"IsPositive": self._get_positive(storage.content_type)},
                    {"DefinitionFlag": self._get_definition_flag(storage.definition)},
                    {"ReleaseLicenseNumber": media_ext.approval_num},
                    {"SarftRecordNumber": media_ext.approval_num},
                ],
                "element_type": "Program",
                "id": program_code,
                "code": program_code,
                "action": action
            },
            {
                "property": [
                    {"Type": 1},
                    {"BitRate": self.bit_rate_transform(int(storage.bit_rate / 1024))},
                    {"BitRateType": btt},
                    {"AudioType": audio_type},
                    {"FileURL": self.generate_file_url(storage)},  # http
                ],
                "element_type": "Movie",
                "id": movie_code,
                "code": movie_code,
                "action": action
            },
            {
                "property": [
                    {"FileURL": self.to_ip_url(media_verpic)},
                ],
                "element_type": "Picture",
                "id": series_picture_ver_code,
                "code": series_picture_ver_code,
                "action": action
            },
            {
                "property": [
                    {"FileURL": self.to_ip_url(media_horpic)},
                ],
                "element_type": "Picture",
                "id": series_picture_hor_code,
                "code": series_picture_hor_code,
                "action": action
            }
        ]

        mapping_list = [
            {
                "parent_type": "Program",
                "parent_id": program_code,
                "parent_code": program_code,
                "element_type": "Movie",
                "element_id": movie_code,
                "element_code": movie_code,
                "action": action,
            },
            {
                "property": [
                    {"Type": 1},
                ],
                "parent_type": "Picture",
                "parent_id": series_picture_ver_code,
                "parent_code": series_picture_ver_code,
                "element_type": "Program",
                "element_id": program_code,
                "element_code": program_code,
                "action": action,
            },
            {
                "property": [
                    {"Type": 2},
                ],
                "parent_type": "Picture",
                "parent_id": series_picture_hor_code,
                "parent_code": series_picture_hor_code,
                "element_type": "Program",
                "element_id": program_code,
                "element_code": program_code,
                "action": action,
            }
        ]

        data = {
            "object_list": object_list,
            "mapping_list": mapping_list,
            "reply": {
                "result": 0,
                "description": "成功",
            }
        }
        return data

    def get_storage_ftp_url(self, http_url):
        self.FTP_STORAGE_HOST = "112.16.171.210"
        file_path = re.split('/', http_url, 3)[-1]
        storage_ftp_url = "ftp://{}:{}@{}:{}/{}".format(
            self.FTP_STORAGE_USER, self.FTP_STORAGE_PASSWORD, self.FTP_STORAGE_HOST, self.FTP_STORAGE_PORT, file_path)
        return storage_ftp_url

    def generate_file_url(self, storage):
        if storage.size > 1024 * 1024 * 1024:
            return self.get_storage_ftp_url(storage.url)
        return self.change_storage_url(storage.url)

    def _album_sub_series(self, media_storage, media, media_ext, episode, _episodes, action, replace):

        series_code = self.code_factory.gen_series_code(media.id)
        program_code = self.code_factory.gen_program_code(episode.id)
        movie_code = self.code_factory.gen_movie_code(media_storage.id)
        if replace:
            replace_storage = MediaStorageModel.objects.filter(id=replace).first()
            media_storage = replace_storage if replace_storage else media_storage
        vst, vet = self.get_media_effective_time(media.id)
        param_dict = {"category": media.category}
        _category = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)["category"]
        category = self._get_category_desc(_category)
        tags = self._get_tags(media)
        episode_name = self.get_episode_title(media, episode)
        btt = self._get_bit_rate_type(media_storage.definition)
        audio_type = self.get_audio_type(media_storage.a_codec)

        object_list = [
            {
                "property": [
                    {"Name": episode_name},
                    {"Duration": math.ceil(int(media_storage.seconds) / 60)},
                    {"length1": media_storage.seconds},
                    {"LicensingWindowStart": vst},
                    {"LicensingWindowEnd": vet},
                    {"ReleaseYear": media.year},
                    {"OrgAirDate": media.date_published.replace("-", "")},
                    {"PgmCategory": category},
                    {"PgmSndClass": tags},
                    {"Tags": tags},
                    {"Status": 1},
                    {"Type": category},
                    {"OriginalCountry": self.get_regions(media)},
                    {"Language": media.languages},
                    {"SeriesFlag": 0 if media.episodes == 1 else 1},
                    {"SearchName": self.transform_chinese_to_letter(episode_name)},
                    {"Rating": media.douban_score},
                    {"IsPositive": self._get_positive(media_storage.content_type)},
                    {"DefinitionFlag": self._get_definition_flag(media_storage.definition)},
                    {"ReleaseLicenseNumber": media_ext.approval_num},
                    {"SarftRecordNumber": media_ext.approval_num},
                    {"Description": "【来源于乐维】{}".format(episode.storyline)},
                ],
                "element_type": "Program",
                "id": program_code,
                "code": program_code,
                "action": action
            },
            {
                "property": [
                    {"Type": 1},
                    {"BitRate": self.bit_rate_transform(int(media_storage.bit_rate / 1024))},
                    {"BitRateType": btt},
                    {"AudioType": audio_type},
                    {"FileURL": self.generate_file_url(media_storage)},  # http
                ],
                "element_type": "Movie",
                "id": movie_code,
                "code": movie_code,
                "action": action
            }
        ]

        mapping_list = [
            {
                "property": [
                    {"Sequence": episode.serial_num},
                ],
                "parent_type": "Series",
                "parent_id": series_code,
                "parent_code": series_code,
                "element_type": "Program",
                "element_id": program_code,
                "element_code": program_code,
                "action": action,
            },
            {
                "parent_type": "Program",
                "parent_id": program_code,
                "parent_code": program_code,
                "element_type": "Movie",
                "element_id": movie_code,
                "element_code": movie_code,
                "action": action,
            }

        ]
        data = {
            "object_list": object_list,
            "mapping_list": mapping_list,
            "reply": {
                "result": 0,
                "description": "成功",
            }
        }
        return data

    def get_episode_title(self, media, episode):
        if media.episodes > 1:
            return self.gen_episode_title_by_num(episode.serial_num)
        else:
            return super().get_episode_title(media, episode)

    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  # 媒体文件补充信息
        replace = task.replace

        _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, replace)
        else:  # 单剧集
            data = self._album_movie(media_storage, media, episode, media_ext, action, replace)
        xml = Adi.to_xml(data, flag="Nope")
        # print(xml)
        return 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
        action = info_sync.action_description()

        series_code = self.code_factory.gen_series_code(media.id)
        series_picture_ver_code = self.code_factory.gen_media_vpic_code(media.id)
        series_picture_hor_code = self.code_factory.gen_media_hpic_code(media.id)
        genre = self._get_sub_category()
        vst, vet = self.get_media_effective_time(media.id)
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        param_dict = {"category": media.category}
        _category = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)["category"]
        category = self._get_category_desc(_category)
        tags = self._get_tags(media)
        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)

        object_list = [
            {
                "property": [
                    {"Name": self.get_media_name_by_alias(media)},
                    {"LicensingWindowStart": vst},
                    {"LicensingWindowEnd": vet},
                    {"Status": 1},
                    {"ActorDisplay": actors},
                    {"DirectorDisplay": directors},
                    {"WriterDisplay": writers},
                    {"Type": category},
                    {"PgmCategory": category},
                    {"PgmSndClass": tags},
                    {"Tags": tags},
                    {"OriginalCountry": self.get_regions(media)},
                    {"Language": media.languages},
                    {"ReleaseYear": media.year},
                    {"SearchName": self.transform_chinese_to_letter(self.get_media_name_by_alias(media))},
                    {"Rating": media.douban_score},
                    {"SourceCode": self.csp_id},
                    {"Description": "【来源于乐维】 {}".format(media.storyline)},
                    {"VolumnCount": media.episodes},
                    {"ReleaseLicenseNumber": media_ext.approval_num},
                    {"SarftRecordNumber": media_ext.approval_num},
                ],
                "element_type": "Series",
                "id": series_code,
                "code": series_code,
                "action": action
            },
            {
                "property": [
                    {"FileURL": self.to_ip_url(media_verpic)}
                ],
                "element_type": "Picture",
                "id": series_picture_ver_code,
                "code": series_picture_ver_code,
                "action": action
            },

            {
                "property": [
                    {"FileURL": self.to_ip_url(media_horpic)},
                ],
                "element_type": "Picture",
                "id": series_picture_hor_code,
                "code": series_picture_hor_code,
                "action": action
            }
        ]

        mapping_list = [
            {
                "property": [
                    {"Type": 1},
                ],
                "parent_type": "Picture",
                "parent_id": series_picture_ver_code,
                "parent_code": series_picture_ver_code,
                "element_type": "Series",
                "element_id": series_code,
                "element_code": series_code,
                "action": action,
            },
            {
                "property": [
                    {"Type": 2},
                ],
                "parent_type": "Picture",
                "parent_id": series_picture_hor_code,
                "parent_code": series_picture_hor_code,
                "element_type": "Series",
                "element_id": series_code,
                "element_code": series_code,
                "action": action,
            }
        ]

        data = {
            "object_list": object_list,
            "mapping_list": mapping_list,
            "reply": {
                "result": 0,
                "description": "成功",
            }
        }
        xml = Adi.to_xml(data)
        return xml

    def exec_cmd(self, task_id):
        _task_id = self.generate_correlate_id(task_id)
        ftp_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()), ftp_url)
        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 = etree.XML(response)
        code = tree.findall(".//multiRef")[-1].text
        desc = tree.find(".//ErrorDescription").text

        if int(code) == 0:
            status = TaskStatusValue.FILE_TRANSFERING
            remark = ""
        else:
            status = TaskStatusValue.FILE_FAILED
            remark = desc

        TaskModel.objects.filter(pk=task_id).update(status=status, remark=remark)
        return status

    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):
        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 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
        desc = tree.find(".//ErrorDescription").text
        if int(code) == 0:
            status = MediaInfoSyncModel.SYNCING
            remark = ""
        else:
            status = MediaInfoSyncModel.FAILED
            remark = desc
        the_sync = MediaInfoSyncModel.objects.filter(pk=sync.id)
        if the_sync.first().status == MediaInfoSyncModel.PENDING:
            the_sync.update(status=status, remark=remark)
        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

    # 更新 剧头
    def series_header_update(self, sync):
        self.series_header_injector(sync)

    # 删除 剧头
    def series_header_remove(self, sync):
        self.series_header_injector(sync)
