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

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

from urllib.parse import urlencode

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))
        obj["VideoVersionItemType"] = self.itemtype

        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 for movie %s: %s", obj["Id"], 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": obj["Filename"],
                "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"],
        )
