# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals

##################################################################################################

from six.moves.urllib.parse import urlencode
from kodi_six.utils import py2_encode

from .. import downloader as server
from ..database import jellyfin_db, queries as QUEM
from ..helper import api, stop, validate, validate_bluray_dir, validate_dvd_dir, jellyfin_item, values, Local
from ..helper import LazyLogger
from ..helper.utils import find_library
from ..helper.exceptions import PathValidationException

from .obj import Objects
from .kodi import Movies as KodiDb, queries as QU

##################################################################################################

LOG = LazyLogger(__name__)

##################################################################################################


class Movies(KodiDb):

    def __init__(self, server, jellyfindb, videodb, direct_path, library=None):

        self.server = server
        self.jellyfin = jellyfindb
        self.video = videodb
        self.direct_path = direct_path

        self.jellyfin_db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)
        self.objects = Objects()
        self.item_ids = []
        self.library = library

        KodiDb.__init__(self, videodb.cursor)

    @stop
    @jellyfin_item
    def movie(self, item, e_item):

        ''' If item does not exist, entry will be added.
            If item exists, entry will be updated.
        '''
        server_address = self.server.auth.get_server_info(self.server.auth.server_id)['address']
        API = api.API(item, server_address)
        obj = self.objects.map(item, 'Movie')
        update = True

        try:
            obj['MovieId'] = e_item[0]
            obj['FileId'] = e_item[1]
            obj['PathId'] = e_item[2]
            obj['LibraryId'] = e_item[6]
            obj['LibraryName'] = self.jellyfin_db.get_view_name(obj['LibraryId'])
        except TypeError:
            update = False
            LOG.debug("MovieId %s not found", obj['Id'])

            library = self.library or find_library(self.server, item)
            if not library:
                # This item doesn't belong to a whitelisted library
                return

            obj['MovieId'] = self.create_entry()
            obj['LibraryId'] = library['Id']
            obj['LibraryName'] = library['Name']
        else:
            if self.get(*values(obj, QU.get_movie_obj)) is None:

                update = False
                LOG.info("MovieId %s missing from kodi. repairing the entry.", obj['MovieId'])

        obj['Path'] = API.get_file_path(obj['Path'])
        obj['Genres'] = obj['Genres'] or []
        obj['Studios'] = [API.validate_studio(studio) for studio in (obj['Studios'] or [])]
        obj['People'] = obj['People'] or []
        obj['Genre'] = " / ".join(obj['Genres'])
        obj['Writers'] = " / ".join(obj['Writers'] or [])
        obj['Directors'] = " / ".join(obj['Directors'] or [])
        obj['Plot'] = API.get_overview(obj['Plot'])
        obj['Mpaa'] = API.get_mpaa(obj['Mpaa'])
        obj['Resume'] = API.adjust_resume((obj['Resume'] or 0) / 10000000.0)
        obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
        obj['People'] = API.get_people_artwork(obj['People'])
        obj['DateAdded'] = Local(obj['DateAdded']).split('.')[0].replace('T', " ")
        obj['DatePlayed'] = None if not obj['DatePlayed'] else Local(obj['DatePlayed']).split('.')[0].replace('T', " ")
        obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount'])
        obj['Artwork'] = API.get_all_artwork(self.objects.map(item, 'Artwork'))
        obj['Video'] = API.video_streams(obj['Video'] or [], obj['Container'])
        obj['Audio'] = API.audio_streams(obj['Audio'] or [])
        obj['Streams'] = API.media_streams(obj['Video'], obj['Audio'], obj['Subtitles'])
        if obj['Premiere'] is not None:
            obj['Premiere'] = str(obj['Premiere']).split('T')[0]

        self.get_path_filename(obj)
        self.trailer(obj)

        if obj['Countries']:
            self.add_countries(*values(obj, QU.update_country_obj))

        tags = list(obj['Tags'] or [])
        tags.append(obj['LibraryName'])

        if obj['Favorite']:
            tags.append('Favorite movies')

        obj['Tags'] = tags

        if update:
            self.movie_update(obj)
        else:
            self.movie_add(obj)

        self.update_path(*values(obj, QU.update_path_movie_obj))
        self.update_file(*values(obj, QU.update_file_obj))
        self.add_tags(*values(obj, QU.add_tags_movie_obj))
        self.add_genres(*values(obj, QU.add_genres_movie_obj))
        self.add_studios(*values(obj, QU.add_studios_movie_obj))
        self.add_playstate(*values(obj, QU.add_bookmark_obj))
        self.add_people(*values(obj, QU.add_people_movie_obj))
        self.add_streams(*values(obj, QU.add_streams_obj))
        self.artwork.add(obj['Artwork'], obj['MovieId'], "movie")
        self.item_ids.append(obj['Id'])

        return not update

    def movie_add(self, obj):

        ''' Add object to kodi.
        '''
        obj['RatingId'] = self.create_entry_rating()
        self.add_ratings(*values(obj, QU.add_rating_movie_obj))

        obj['Unique'] = self.create_entry_unique_id()
        self.add_unique_id(*values(obj, QU.add_unique_id_movie_obj))

        obj['PathId'] = self.add_path(*values(obj, QU.add_path_obj))
        obj['FileId'] = self.add_file(*values(obj, QU.add_file_obj))

        self.add(*values(obj, QU.add_movie_obj))
        self.add_videoversion(*values(obj, QU.add_video_version_obj))
        self.jellyfin_db.add_reference(*values(obj, QUEM.add_reference_movie_obj))
        LOG.debug("ADD movie [%s/%s/%s] %s: %s", obj['PathId'], obj['FileId'], obj['MovieId'], obj['Id'], obj['Title'])

    def movie_update(self, obj):

        ''' Update object to kodi.
        '''
        obj['RatingId'] = self.get_rating_id(*values(obj, QU.get_rating_movie_obj))
        self.update_ratings(*values(obj, QU.update_rating_movie_obj))

        obj['Unique'] = self.get_unique_id(*values(obj, QU.get_unique_id_movie_obj))
        self.update_unique_id(*values(obj, QU.update_unique_id_movie_obj))

        self.update(*values(obj, QU.update_movie_obj))
        self.jellyfin_db.update_reference(*values(obj, QUEM.update_reference_obj))
        LOG.debug("UPDATE movie [%s/%s/%s] %s: %s", obj['PathId'], obj['FileId'], obj['MovieId'], obj['Id'], obj['Title'])

    def trailer(self, obj):

        try:
            if obj['LocalTrailer']:

                trailer = self.server.jellyfin.get_local_trailers(obj['Id'])
                obj['Trailer'] = "plugin://plugin.video.jellyfin/trailer?id=%s&mode=play" % trailer[0]['Id']

            elif obj['Trailer']:
                obj['Trailer'] = "plugin://plugin.video.youtube/play/?video_id=%s" % obj['Trailer'].rsplit('=', 1)[1]
        except Exception as error:

            LOG.exception("Failed to get trailer: %s", error)
            obj['Trailer'] = None

    def get_path_filename(self, obj):

        ''' Get the path and filename and build it into protocol://path
        '''
        obj['Filename'] = obj['Path'].rsplit('\\', 1)[1] if '\\' in obj['Path'] else obj['Path'].rsplit('/', 1)[1]

        if self.direct_path:

            if not validate(obj['Path']):
                raise PathValidationException("Failed to validate path. User stopped.")

            obj['Path'] = obj['Path'].replace(obj['Filename'], "")

            '''check dvd directories and point it to ./VIDEO_TS/VIDEO_TS.IFO'''
            if validate_dvd_dir(obj['Path'] + obj['Filename']):
                obj['Path'] = obj['Path'] + obj['Filename'] + '/VIDEO_TS/'
                obj['Filename'] = 'VIDEO_TS.IFO'
                LOG.debug("DVD directory %s", obj['Path'])

            '''check bluray directories and point it to ./BDMV/index.bdmv'''
            if validate_bluray_dir(obj['Path'] + obj['Filename']):
                obj['Path'] = obj['Path'] + obj['Filename'] + '/BDMV/'
                obj['Filename'] = 'index.bdmv'
                LOG.debug("Bluray directory %s", obj['Path'])

        else:
            obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
            params = {
                'filename': py2_encode(obj['Filename'], 'utf-8'),
                'id': obj['Id'],
                'dbid': obj['MovieId'],
                'mode': "play"
            }
            obj['Filename'] = "%s?%s" % (obj['Path'], urlencode(params))

    @stop
    @jellyfin_item
    def boxset(self, item, e_item):

        ''' If item does not exist, entry will be added.
            If item exists, entry will be updated.

            Process movies inside boxset.
            Process removals from boxset.
        '''
        server_address = self.server.auth.get_server_info(self.server.auth.server_id)['address']
        API = api.API(item, server_address)
        obj = self.objects.map(item, 'Boxset')

        obj['Overview'] = API.get_overview(obj['Overview'])

        try:
            obj['SetId'] = e_item[0]
            self.update_boxset(*values(obj, QU.update_set_obj))
        except TypeError:
            LOG.debug("SetId %s not found", obj['Id'])
            obj['SetId'] = self.add_boxset(*values(obj, QU.add_set_obj))

        self.boxset_current(obj)
        obj['Artwork'] = API.get_all_artwork(self.objects.map(item, 'Artwork'))

        for movie in obj['Current']:

            temp_obj = dict(obj)
            temp_obj['Movie'] = movie
            temp_obj['MovieId'] = obj['Current'][temp_obj['Movie']]
            self.remove_from_boxset(*values(temp_obj, QU.delete_movie_set_obj))
            self.jellyfin_db.update_parent_id(*values(temp_obj, QUEM.delete_parent_boxset_obj))
            LOG.debug("DELETE from boxset [%s] %s: %s", temp_obj['SetId'], temp_obj['Title'], temp_obj['MovieId'])

        self.artwork.add(obj['Artwork'], obj['SetId'], "set")
        self.jellyfin_db.add_reference(*values(obj, QUEM.add_reference_boxset_obj))
        LOG.debug("UPDATE boxset [%s] %s", obj['SetId'], obj['Title'])

    def boxset_current(self, obj):

        ''' Add or removes movies based on the current movies found in the boxset.
        '''
        try:
            current = self.jellyfin_db.get_item_id_by_parent_id(*values(obj, QUEM.get_item_id_by_parent_boxset_obj))
            movies = dict(current)
        except ValueError:
            movies = {}

        obj['Current'] = movies

        for all_movies in server.get_movies_by_boxset(obj['Id']):
            for movie in all_movies['Items']:

                temp_obj = dict(obj)
                temp_obj['Title'] = movie['Name']
                temp_obj['Id'] = movie['Id']

                try:
                    temp_obj['MovieId'] = self.jellyfin_db.get_item_by_id(*values(temp_obj, QUEM.get_item_obj))[0]
                except TypeError:
                    LOG.info("Failed to process %s to boxset.", temp_obj['Title'])

                    continue

                if temp_obj['Id'] not in obj['Current']:

                    self.set_boxset(*values(temp_obj, QU.update_movie_set_obj))
                    self.jellyfin_db.update_parent_id(*values(temp_obj, QUEM.update_parent_movie_obj))
                    LOG.debug("ADD to boxset [%s/%s] %s: %s to boxset", temp_obj['SetId'], temp_obj['MovieId'], temp_obj['Title'], temp_obj['Id'])
                else:
                    obj['Current'].pop(temp_obj['Id'])

    def boxsets_reset(self):

        ''' Special function to remove all existing boxsets.
        '''
        boxsets = self.jellyfin_db.get_items_by_media('set')
        for boxset in boxsets:
            self.remove(boxset[0])

    @stop
    @jellyfin_item
    def userdata(self, item, e_item):

        ''' This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks
            Poster with progress bar
        '''
        server_address = self.server.auth.get_server_info(self.server.auth.server_id)['address']
        API = api.API(item, server_address)
        obj = self.objects.map(item, 'MovieUserData')

        try:
            obj['MovieId'] = e_item[0]
            obj['FileId'] = e_item[1]
        except TypeError:
            return

        obj['Resume'] = API.adjust_resume((obj['Resume'] or 0) / 10000000.0)
        obj['Runtime'] = round(float((obj['Runtime'] or 0) / 10000000.0), 6)
        obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount'])

        if obj['DatePlayed']:
            obj['DatePlayed'] = Local(obj['DatePlayed']).split('.')[0].replace('T', " ")

        if obj['Favorite']:
            self.get_tag(*values(obj, QU.get_tag_movie_obj))
        else:
            self.remove_tag(*values(obj, QU.delete_tag_movie_obj))

        LOG.debug("New resume point %s: %s", obj['Id'], obj['Resume'])
        self.add_playstate(*values(obj, QU.add_bookmark_obj))
        self.jellyfin_db.update_reference(*values(obj, QUEM.update_reference_obj))
        LOG.debug("USERDATA movie [%s/%s] %s: %s", obj['FileId'], obj['MovieId'], obj['Id'], obj['Title'])

    @stop
    @jellyfin_item
    def remove(self, item_id, e_item):

        ''' Remove movieid, fileid, jellyfin reference.
            Remove artwork, boxset
        '''
        obj = {'Id': item_id}

        try:
            obj['KodiId'] = e_item[0]
            obj['FileId'] = e_item[1]
            obj['Media'] = e_item[4]
        except TypeError:
            return

        self.artwork.delete(obj['KodiId'], obj['Media'])

        if obj['Media'] == 'movie':
            self.delete(*values(obj, QU.delete_movie_obj))
        elif obj['Media'] == 'set':

            for movie in self.jellyfin_db.get_item_by_parent_id(*values(obj, QUEM.get_item_by_parent_movie_obj)):

                temp_obj = dict(obj)
                temp_obj['MovieId'] = movie[1]
                temp_obj['Movie'] = movie[0]
                self.remove_from_boxset(*values(temp_obj, QU.delete_movie_set_obj))
                self.jellyfin_db.update_parent_id(*values(temp_obj, QUEM.delete_parent_boxset_obj))

            self.delete_boxset(*values(obj, QU.delete_set_obj))

        self.jellyfin_db.remove_item(*values(obj, QUEM.delete_item_obj))
        LOG.debug("DELETE %s [%s/%s] %s", obj['Media'], obj['FileId'], obj['KodiId'], obj['Id'])
