#
# This file is part of SickGear.
#
# SickGear is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickGear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickGear.  If not, see <http://www.gnu.org/licenses/>.
import datetime

from . import mediabrowser
from .. import logger
import sg_helpers
from lib.tvinfo_base.exceptions import *
import sickgear
import exceptions_helper
from exceptions_helper import ex
from lxml_etree import etree

# noinspection PyUnreachableCode
if False:
    from typing import AnyStr, Dict, Optional, Union


class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
    """
    Metadata generation class for Mede8er based on the MediaBrowser.

    The following file structure is used:

    show_root/series.xml                    (show metadata)
    show_root/folder.jpg                    (poster)
    show_root/fanart.jpg                    (fanart)
    show_root/Season ##/folder.jpg          (season thumb)
    show_root/Season ##/filename.ext        (*)
    show_root/Season ##/filename.xml        (episode metadata)
    show_root/Season ##/filename.jpg        (episode thumb)
    """

    def __init__(self,
                 show_metadata=False,  # type: bool
                 episode_metadata=False,  # type: bool
                 use_fanart=False,  # type: bool
                 use_poster=False,  # type: bool
                 use_banner=False,  # type: bool
                 episode_thumbnails=False,  # type: bool
                 season_posters=False,  # type: bool
                 season_banners=False,  # type: bool
                 season_all_poster=False,  # type: bool
                 season_all_banner=False  # type: bool
                 ):

        mediabrowser.MediaBrowserMetadata.__init__(
            self,
            show_metadata,
            episode_metadata,
            use_fanart,
            use_poster,
            use_banner,
            episode_thumbnails,
            season_posters,
            season_banners,
            season_all_poster,
            season_all_banner)

        self.name = 'Mede8er'  # type: AnyStr

        self.fanart_name = 'fanart.jpg'  # type: AnyStr

        # web-ui metadata template
        # self.eg_show_metadata = 'series.xml'
        self.eg_episode_metadata = 'Season##\\<i>filename</i>.xml'  # type: AnyStr
        self.eg_fanart = 'fanart.jpg'  # type: AnyStr
        # self.eg_poster = 'folder.jpg'
        # self.eg_banner = 'banner.jpg'
        self.eg_episode_thumbnails = 'Season##\\<i>filename</i>.jpg'  # type: AnyStr
        # self.eg_season_posters = 'Season##\\folder.jpg'
        # self.eg_season_banners = 'Season##\\banner.jpg'
        # self.eg_season_all_poster = '<i>not supported</i>'
        # self.eg_season_all_banner = '<i>not supported</i>'

    def get_episode_file_path(self, ep_obj):
        # type: (sickgear.tv.TVEpisode) -> AnyStr
        return sg_helpers.replace_extension(ep_obj.location, self._ep_nfo_extension)

    def get_episode_thumb_path(self, ep_obj):
        # type: (sickgear.tv.TVEpisode) -> AnyStr
        return sg_helpers.replace_extension(ep_obj.location, 'jpg')

    def _show_data(self, show_obj):
        # type: (sickgear.tv.TVShow) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a MediaBrowser-style series.xml
        returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        show_lang = show_obj.lang
        tvinfo_config = sickgear.TVInfoAPI(show_obj.tvid).api_params.copy()

        tvinfo_config['actors'] = True

        if show_lang and not 'en' == show_lang:
            tvinfo_config['language'] = show_lang

        if 0 != show_obj.dvdorder:
            tvinfo_config['dvdorder'] = True

        t = sickgear.TVInfoAPI(show_obj.tvid).setup(**tvinfo_config)

        rootNode = etree.Element('details')
        tv_node = etree.SubElement(rootNode, 'movie')
        tv_node.attrib['isExtra'] = 'false'
        tv_node.attrib['isSet'] = 'false'
        tv_node.attrib['isTV'] = 'true'

        try:
            show_info = t.get_show(show_obj.prodid, language=show_obj.lang)
        except BaseTVinfoShownotfound as e:
            logger.error(f'Unable to find show with id {show_obj.prodid} on tvdb, skipping it')
            raise e
        except BaseTVinfoError as e:
            logger.error(f'TVDB is down, can\'t use its data to make the NFO')
            raise e

        if not self._valid_show(show_info, show_obj):
            return

        # check for title and id
        try:
            if None is show_info['seriesname'] \
                    or '' == show_info['seriesname'] \
                    or None is show_info['id'] \
                    or '' == show_info['id']:
                logger.error(f'Incomplete info for show with id {show_obj.prodid}'
                             f' on {sickgear.TVInfoAPI(show_obj.tvid).name}, skipping it')
                return False
        except BaseTVinfoAttributenotfound:
            logger.error(f'Incomplete info for show with id {show_obj.prodid}'
                         f' on {sickgear.TVInfoAPI(show_obj.tvid).name}, skipping it')
            return False

        SeriesName = etree.SubElement(tv_node, 'title')
        if None is not show_info['seriesname']:
            SeriesName.text = '%s' % show_info['seriesname']
        else:
            SeriesName.text = ''

        Genres = etree.SubElement(tv_node, 'genres')
        if None is not show_info['genre']:
            for genre in show_info['genre'].split('|'):
                if genre and genre.strip():
                    cur_genre = etree.SubElement(Genres, 'Genre')
                    cur_genre.text = '%s' % genre.strip()

        FirstAired = etree.SubElement(tv_node, 'premiered')
        if None is not show_info['firstaired']:
            FirstAired.text = '%s' % show_info['firstaired']

        year = etree.SubElement(tv_node, 'year')
        year_text = self.get_show_year(show_obj, show_info)
        if year_text:
            year.text = '%s' % year_text

        if None is not show_info['rating']:
            try:
                rating = int((float(show_info['rating']) * 10))
            except ValueError:
                rating = 0
            Rating = etree.SubElement(tv_node, 'rating')
            rating_text = str(rating)
            if None is not rating_text:
                Rating.text = '%s' % rating_text

        Status = etree.SubElement(tv_node, 'status')
        if None is not show_info['status']:
            Status.text = '%s' % show_info['status']

        mpaa = etree.SubElement(tv_node, 'mpaa')
        if None is not show_info['contentrating']:
            mpaa.text = '%s' % show_info['contentrating']

        IMDB_ID = etree.SubElement(tv_node, 'id')
        if None is not show_info['imdb_id']:
            IMDB_ID.attrib['moviedb'] = 'imdb'
            IMDB_ID.text = '%s' % show_info['imdb_id']

        prodid = etree.SubElement(tv_node, 'indexerid')
        if None is not show_info['id']:
            prodid.text = '%s' % show_info['id']

        Runtime = etree.SubElement(tv_node, 'runtime')
        if None is not show_info['runtime']:
            Runtime.text = '%s' % show_info['runtime']

        cast = etree.SubElement(tv_node, 'cast')
        self.add_actor_element(show_info, etree, cast)

        sg_helpers.indent_xml(rootNode)

        data = etree.ElementTree(rootNode)

        return data

    def _ep_data(self, ep_obj):
        # type: (sickgear.tv.TVEpisode) -> Optional[Union[bool, etree.Element]]
        """
        Creates an elementTree XML structure for a MediaBrowser style episode.xml
        and returns the resulting data object.

        show_obj: a TVShow instance to create the NFO for
        """

        ep_obj_list_to_write = [ep_obj] + ep_obj.related_ep_obj

        show_lang = ep_obj.show_obj.lang

        try:
            # There's gotta be a better way of doing this but we don't wanna
            # change the language value elsewhere
            tvinfo_config = sickgear.TVInfoAPI(ep_obj.show_obj.tvid).api_params.copy()

            if show_lang and not 'en' == show_lang:
                tvinfo_config['language'] = show_lang

            if 0 != ep_obj.show_obj.dvdorder:
                tvinfo_config['dvdorder'] = True

            t = sickgear.TVInfoAPI(ep_obj.show_obj.tvid).setup(**tvinfo_config)
            show_info = t.get_show(ep_obj.show_obj.prodid, language=ep_obj.show_obj.lang)
        except BaseTVinfoShownotfound as e:
            raise exceptions_helper.ShowNotFoundException(ex(e))
        except BaseTVinfoError as e:
            logger.error(f'Unable to connect to {sickgear.TVInfoAPI(ep_obj.show_obj.tvid).name}'
                         f' while creating meta files - skipping - {ex(e)}')
            return False

        if not self._valid_show(show_info, ep_obj.show_obj):
            return

        rootNode = etree.Element('details')
        movie = etree.SubElement(rootNode, 'movie')

        movie.attrib['isExtra'] = 'false'
        movie.attrib['isSet'] = 'false'
        movie.attrib['isTV'] = 'true'

        # write an MediaBrowser XML containing info for all matching episodes
        for cur_ep_obj in ep_obj_list_to_write:

            try:
                ep_info = show_info[cur_ep_obj.season][cur_ep_obj.episode]
            except (BaseException, Exception):
                logger.log(f'Unable to find episode {cur_ep_obj.season}x{cur_ep_obj.episode} on tvdb...'
                           f' has it been removed? Should it be deleted from the db?')
                return None

            if cur_ep_obj == ep_obj:
                # root (or single) episode

                # default to today's date for specials if firstaired is not set
                if None is ep_info['firstaired'] and 0 == ep_obj.season:
                    ep_info['firstaired'] = str(datetime.date.fromordinal(1))

                if None is ep_info['episodename'] or None is ep_info['firstaired']:
                    return None

                episode = movie

                EpisodeName = etree.SubElement(episode, 'title')
                if None is not cur_ep_obj.name:
                    EpisodeName.text = '%s' % cur_ep_obj.name
                else:
                    EpisodeName.text = ''

                SeasonNumber = etree.SubElement(episode, 'season')
                SeasonNumber.text = str(cur_ep_obj.season)

                EpisodeNumber = etree.SubElement(episode, 'episode')
                EpisodeNumber.text = str(ep_obj.episode)

                year = etree.SubElement(episode, 'year')
                year_text = self.get_show_year(ep_obj.show_obj, show_info)
                if year_text:
                    year.text = '%s' % year_text

                plot = etree.SubElement(episode, 'plot')
                if None is not show_info['overview']:
                    plot.text = '%s' % show_info['overview']

                Overview = etree.SubElement(episode, 'episodeplot')
                if None is not cur_ep_obj.description:
                    Overview.text = '%s' % cur_ep_obj.description
                else:
                    Overview.text = ''

                mpaa = etree.SubElement(episode, 'mpaa')
                if None is not show_info['contentrating']:
                    mpaa.text = '%s' % show_info['contentrating']

                if not ep_obj.related_ep_obj:
                    if None is not ep_info['rating']:
                        try:
                            rating = int((float(ep_info['rating']) * 10))
                        except ValueError:
                            rating = 0
                        Rating = etree.SubElement(episode, 'rating')
                        rating_text = str(rating)
                        if None is not rating_text:
                            Rating.text = '%s' % rating_text

                director = etree.SubElement(episode, 'director')
                director_text = ep_info['director']
                if None is not director_text:
                    director.text = '%s' % director_text

                credits = etree.SubElement(episode, 'credits')
                credits_text = ep_info['writer']
                if None is not credits_text:
                    credits.text = '%s' % credits_text

                cast = etree.SubElement(episode, 'cast')
                self.add_actor_element(show_info, etree, cast)

            else:
                # append data from (if any) related episodes

                if cur_ep_obj.name:
                    if not EpisodeName.text:
                        EpisodeName.text = '%s' % cur_ep_obj.name
                    else:
                        EpisodeName.text = '%s, %s' % (EpisodeName.text, cur_ep_obj.name)

                if cur_ep_obj.description:
                    if not Overview.text:
                        Overview.text = '%s' % cur_ep_obj.description
                    else:
                        Overview.text = '%s\r%s' % (Overview.text, cur_ep_obj.description)

        sg_helpers.indent_xml(rootNode)
        data = etree.ElementTree(rootNode)

        return data

    @staticmethod
    def add_actor_element(show_info, et, node):
        # type: (Dict, etree, etree.Element) -> None
        for actor in getattr(show_info, 'actors', []):
            cur_actor_name_text = actor['character']['name'] and actor['person']['name'] \
                                  and actor['character']['name'] != actor['person']['name'] \
                                  and '%s (%s)' % (actor['character']['name'], actor['person']['name']) \
                                  or actor['person']['name'] or actor['character']['name']
            if cur_actor_name_text:
                cur_actor = et.SubElement(node, 'actor')
                cur_actor.text = '%s' % cur_actor_name_text


# present a standard "interface" from the module
metadata_class = Mede8erMetadata