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

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

import json
import sys
import os
from urllib.parse import parse_qsl, urlencode

import xbmc
import xbmcvfs
import xbmcgui
import xbmcplugin
import xbmcaddon

from .. import client
from ..database import reset, get_sync, Database, jellyfin_db, get_credentials
from ..objects import Objects, Actions
from ..helper import (
    translate,
    event,
    settings,
    window,
    dialog,
    api,
    JSONRPC,
    LazyLogger,
)
from ..helper.utils import (
    JsonDebugPrinter,
    translate_path,
    kodi_version,
    path_replacements,
)
from ..jellyfin import Jellyfin

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

LOG = LazyLogger(__name__)

ADDON_BASE_URL = sys.argv[0]
try:
    PROCESS_HANDLE = int(sys.argv[1])
    QUERY_STRING = sys.argv[2]
except IndexError:
    pass

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


class Events(object):

    def __init__(self):
        """Parse the parameters. Reroute to our service.py
        where user is fully identified already.
        """
        base_url = ADDON_BASE_URL
        path = QUERY_STRING

        try:
            params = dict(parse_qsl(path[1:]))
        except Exception:
            params = {}

        mode = params.get("mode")
        server = params.get("server")

        if server == "None":
            server = None

        jellyfin_client = Jellyfin(server).get_client()
        api_client = jellyfin_client.jellyfin

        addon_data = translate_path(
            "special://profile/addon_data/plugin.video.jellyfin/data.json"
        )
        try:
            with open(addon_data, "rb") as infile:
                data = json.load(infile)

            server_data = data["Servers"][0]
            api_client.config.data["auth.server"] = server_data.get("address")
            api_client.config.data["auth.server-name"] = server_data.get("Name")
            api_client.config.data["auth.user_id"] = server_data.get("UserId")
            api_client.config.data["auth.token"] = server_data.get("AccessToken")
        except Exception as e:
            LOG.warning("Addon appears to not be configured yet: {}".format(e))

        LOG.info("path: %s params: %s", path, JsonDebugPrinter(params))

        if "/extrafanart" in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get("id")
            get_fanart(jellyfin_id, jellyfin_path, server, api_client)

        elif "/Extras" in base_url or "/VideoFiles" in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get("id")
            get_video_extras(jellyfin_id, jellyfin_path, server, api_client)

        elif mode == "play":

            item = api_client.get_item(params["id"])
            item["resumePlayback"] = sys.argv[3].split(":")[1] == "true"
            Actions(server, api_client).play(
                item,
                params.get("dbid"),
                params.get("transcode") == "true",
                playlist=params.get("playlist") == "true",
            )

        elif mode == "playlist":
            api_client.post_session(
                api_client.config.data["app.session"],
                "Playing",
                {
                    "PlayCommand": "PlayNow",
                    "ItemIds": params["id"],
                    "StartPositionTicks": 0,
                },
            )
        elif mode == "deviceid":
            client.reset_device_id()
        elif mode == "reset":
            reset()
        elif mode == "delete":
            delete_item()
        elif mode == "refreshboxsets":
            event("SyncLibrary", {"Id": "Boxsets:Refresh"})
        elif mode == "nextepisodes":
            get_next_episodes(params["id"], params["limit"])
        elif mode == "browse":
            browse(
                params.get("type"),
                params.get("id"),
                params.get("folder"),
                server,
                api_client,
            )
        elif mode == "synclib":
            event("SyncLibrary", {"Id": params.get("id")})
        elif mode == "updatelib":
            event("SyncLibrary", {"Id": params.get("id"), "Update": True})
        elif mode == "repairlib":
            event("RepairLibrary", {"Id": params.get("id")})
        elif mode == "removelib":
            event("RemoveLibrary", {"Id": params.get("id")})
        elif mode == "repairlibs":
            event("RepairLibrarySelection")
        elif mode == "updatelibs":
            event("SyncLibrarySelection")
        elif mode == "removelibs":
            event("RemoveLibrarySelection")
        elif mode == "addlibs":
            event("AddLibrarySelection")
        elif mode == "addserver":
            event("AddServer")
        elif mode == "login":
            event("ServerConnect", {"Id": server})
        elif mode == "removeserver":
            event("RemoveServer", {"Id": server})
        elif mode == "settings":
            xbmc.executebuiltin("Addon.OpenSettings(plugin.video.jellyfin)")
        elif mode == "adduser":
            add_user(api_client)
        elif mode == "updatepassword":
            event("UpdatePassword")
        elif mode == "thememedia":
            get_themes(api_client)
        elif mode == "managelibs":
            manage_libraries()
        elif mode == "managepaths":
            path_replacements()
        elif mode == "backup":
            backup()
        elif mode == "restartservice":
            window("jellyfin.restart.bool", True)
        elif (
            mode is None
            and not params
            and base_url != "plugin://plugin.video.jellyfin/"
        ):
            # Used when selecting "Browse" from a context menu, see #548
            item_id = base_url.strip("/").split("/")[-1]
            browse("", item_id, None, server, api_client)
        else:
            listing()


def listing():
    """Display all jellyfin nodes and dynamic entries when appropriate."""
    total = int(window("Jellyfin.nodes.total") or 0)
    sync = get_sync()
    whitelist = [x.replace("Mixed:", "") for x in sync["Whitelist"]]
    servers = get_credentials()["Servers"][1:]

    for i in range(total):

        window_prop = "Jellyfin.nodes.%s" % i
        path = window("%s.index" % window_prop)

        if not path:
            path = window("%s.content" % window_prop) or window("%s.path" % window_prop)

        label = window("%s.title" % window_prop)
        node = window("%s.type" % window_prop)
        artwork = window("%s.artwork" % window_prop)
        view_id = window("%s.id" % window_prop)
        context = []

        if (
            view_id
            and node in ("movies", "tvshows", "musicvideos", "music", "mixed")
            and view_id not in whitelist
        ):
            label = "%s %s" % (label, translate(33166))
            context.append(
                (
                    translate(33123),
                    "RunPlugin(plugin://plugin.video.jellyfin/?mode=synclib&id=%s)"
                    % view_id,
                )
            )

        if (
            view_id
            and node in ("movies", "tvshows", "musicvideos", "music")
            and view_id in whitelist
        ):

            context.append(
                (
                    translate(33136),
                    "RunPlugin(plugin://plugin.video.jellyfin/?mode=updatelib&id=%s)"
                    % view_id,
                )
            )
            context.append(
                (
                    translate(33132),
                    "RunPlugin(plugin://plugin.video.jellyfin/?mode=repairlib&id=%s)"
                    % view_id,
                )
            )
            context.append(
                (
                    translate(33133),
                    "RunPlugin(plugin://plugin.video.jellyfin/?mode=removelib&id=%s)"
                    % view_id,
                )
            )

        LOG.debug("--[ listing/%s/%s ] %s", node, label, path)

        if path:
            directory(label, path, artwork=artwork, context=context)

    for server in servers:
        context = []

        if server.get("ManualAddress"):
            context.append(
                (
                    translate(33141),
                    "RunPlugin(plugin://plugin.video.jellyfin/?mode=removeserver&server=%s)"
                    % server["Id"],
                )
            )

        if "AccessToken" not in server:
            directory(
                "%s (%s)" % (server["Name"], translate(30539)),
                "plugin://plugin.video.jellyfin/?mode=login&server=%s" % server["Id"],
                False,
                context=context,
            )
        else:
            directory(
                server["Name"],
                "plugin://plugin.video.jellyfin/?mode=browse&server=%s" % server["Id"],
                context=context,
            )

    directory(translate(33194), "plugin://plugin.video.jellyfin/?mode=managelibs", True)
    directory(
        translate(33203), "plugin://plugin.video.jellyfin/?mode=managepaths", True
    )
    directory(translate(33134), "plugin://plugin.video.jellyfin/?mode=addserver", False)
    directory(translate(33054), "plugin://plugin.video.jellyfin/?mode=adduser", False)
    directory(translate(5), "plugin://plugin.video.jellyfin/?mode=settings", False)
    directory(
        translate(33161), "plugin://plugin.video.jellyfin/?mode=updatepassword", False
    )
    directory(translate(33058), "plugin://plugin.video.jellyfin/?mode=reset", False)
    directory(
        translate(33180), "plugin://plugin.video.jellyfin/?mode=restartservice", False
    )

    if settings("backupPath"):
        directory(
            translate(33092), "plugin://plugin.video.jellyfin/?mode=backup", False
        )

    xbmcplugin.setContent(PROCESS_HANDLE, "files")
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def directory(label, path, folder=True, artwork=None, fanart=None, context=None):
    """Add directory listitem. context should be a list of tuples [(label, action)*]"""
    li = dir_listitem(label, path, artwork, fanart)

    if context:
        li.addContextMenuItems(context)

    xbmcplugin.addDirectoryItem(PROCESS_HANDLE, path, li, folder)

    return li


def dir_listitem(label, path, artwork=None, fanart=None):
    """Gets the icon paths for default node listings"""
    li = xbmcgui.ListItem(label, path=path)
    li.setArt(
        {
            "thumb": artwork
            or "special://home/addons/plugin.video.jellyfin/resources/icon.png",
            "fanart": fanart
            or "special://home/addons/plugin.video.jellyfin/resources/fanart.png",
            "landscape": artwork
            or fanart
            or "special://home/addons/plugin.video.jellyfin/resources/fanart.png",
        }
    )

    return li


def manage_libraries():

    directory(
        translate(33098), "plugin://plugin.video.jellyfin/?mode=refreshboxsets", False
    )
    directory(translate(33154), "plugin://plugin.video.jellyfin/?mode=addlibs", False)
    directory(
        translate(33139), "plugin://plugin.video.jellyfin/?mode=updatelibs", False
    )
    directory(
        translate(33140), "plugin://plugin.video.jellyfin/?mode=repairlibs", False
    )
    directory(
        translate(33184), "plugin://plugin.video.jellyfin/?mode=removelibs", False
    )
    directory(
        translate(33060), "plugin://plugin.video.jellyfin/?mode=thememedia", False
    )

    xbmcplugin.setContent(PROCESS_HANDLE, "files")
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def browse(media, view_id=None, folder=None, server_id=None, api_client=None):
    """Browse content dynamically."""
    LOG.info("--[ v:%s/%s ] %s", view_id, media, folder)

    if not window("jellyfin_online.bool") and server_id is None:

        monitor = xbmc.Monitor()

        for _i in range(300):
            if window("jellyfin_online.bool"):
                break
            elif monitor.waitForAbort(0.1):
                return
        else:
            LOG.error("Default server is not online.")

            return

    folder = folder.lower() if folder else None

    if folder is None and media in ("homevideos", "movies", "books", "audiobooks"):
        return browse_subfolders(media, view_id, server_id)

    if folder and folder == "firstletter":
        return browse_letters(media, view_id, server_id)

    if view_id:

        view = api_client.get_item(view_id)
        xbmcplugin.setPluginCategory(PROCESS_HANDLE, view["Name"])

    content_type = "files"

    if media in (
        "tvshows",
        "seasons",
        "episodes",
        "movies",
        "musicvideos",
        "songs",
        "albums",
    ):
        content_type = media
    elif media in ("homevideos", "photos"):
        content_type = "images"
    elif media in ("books", "audiobooks"):
        content_type = "videos"
    elif media == "music":
        content_type = "artists"

    if folder == "recentlyadded":
        listing = api_client.get_recently_added(None, view_id, None)
    elif folder == "genres":
        listing = api_client.get_genres(view_id)
    elif media == "livetv":
        listing = api_client.get_channels()
    elif folder == "unwatched":
        listing = get_filtered_section(
            view_id,
            None,
            None,
            None,
            None,
            None,
            ["IsUnplayed"],
            None,
            server_id,
            api_client,
        )
    elif folder == "favorite":
        listing = get_filtered_section(
            view_id,
            None,
            None,
            None,
            None,
            None,
            ["IsFavorite"],
            None,
            server_id,
            api_client,
        )
    elif folder == "inprogress":
        listing = get_filtered_section(
            view_id,
            None,
            None,
            None,
            None,
            None,
            ["IsResumable"],
            None,
            server_id,
            api_client,
        )
    elif folder == "boxsets":
        listing = get_filtered_section(
            view_id,
            get_media_type("boxsets"),
            None,
            True,
            None,
            None,
            None,
            None,
            server_id,
            api_client,
        )
    elif folder == "random":
        listing = get_filtered_section(
            view_id,
            get_media_type(content_type),
            25,
            True,
            "Random",
            None,
            None,
            None,
            server_id,
            api_client,
        )
    elif (folder or "").startswith("firstletter-"):
        listing = get_filtered_section(
            view_id,
            get_media_type(content_type),
            None,
            None,
            None,
            None,
            None,
            {"NameStartsWith": folder.split("-")[1]},
            server_id,
            api_client,
        )
    elif (folder or "").startswith("genres-"):
        listing = get_filtered_section(
            view_id,
            get_media_type(content_type),
            None,
            None,
            None,
            None,
            None,
            {"GenreIds": folder.split("-")[1]},
            server_id,
            api_client,
        )
    elif folder == "favepisodes":
        listing = get_filtered_section(
            None,
            get_media_type(content_type),
            25,
            None,
            None,
            None,
            ["IsFavorite"],
            None,
            server_id,
            api_client,
        )
    elif folder and media == "playlists":
        listing = get_filtered_section(
            folder,
            get_media_type(content_type),
            None,
            False,
            "None",
            None,
            None,
            None,
            server_id,
            api_client,
        )
    elif media == "homevideos":
        listing = get_filtered_section(
            folder or view_id,
            get_media_type(content_type),
            None,
            False,
            None,
            None,
            None,
            None,
            server_id,
            api_client,
        )
    elif media in ["movies", "episodes"]:
        listing = get_filtered_section(
            folder or view_id,
            get_media_type(content_type),
            None,
            True,
            None,
            None,
            None,
            None,
            server_id,
            api_client,
        )
    elif media in ("boxset", "library"):
        listing = get_filtered_section(
            folder or view_id,
            None,
            None,
            True,
            None,
            None,
            None,
            None,
            server_id,
            api_client,
        )
    elif media == "boxsets":
        listing = get_filtered_section(
            folder or view_id,
            None,
            None,
            False,
            None,
            None,
            ["Boxsets"],
            None,
            server_id,
            api_client,
        )
    elif media == "tvshows":
        listing = get_filtered_section(
            folder or view_id,
            get_media_type(content_type),
            None,
            True,
            None,
            None,
            None,
            None,
            server_id,
            api_client,
        )
    elif media == "seasons":
        listing = api_client.get_seasons(folder)
    elif media != "files":
        listing = get_filtered_section(
            folder or view_id,
            get_media_type(content_type),
            None,
            False,
            None,
            None,
            None,
            None,
            server_id,
            api_client,
        )
    else:
        listing = get_filtered_section(
            folder or view_id,
            None,
            None,
            False,
            None,
            None,
            None,
            None,
            server_id,
            api_client,
        )

    if listing:

        actions = Actions(server_id, api_client)
        list_li = []
        listing = listing if isinstance(listing, list) else listing.get("Items", [])

        for item in listing:

            li = xbmcgui.ListItem()
            li.setProperty("jellyfinid", item["Id"])
            li.setProperty("jellyfinserver", server_id)
            actions.set_listitem(item, li)

            if item.get("IsFolder"):

                params = {
                    "id": view_id or item["Id"],
                    "mode": "browse",
                    "type": get_folder_type(item, media) or media,
                    "folder": item["Id"],
                    "server": server_id,
                }
                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                context = []

                if item["Type"] in ("Series", "Season", "Playlist"):
                    context.append(
                        (
                            "Play",
                            "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)"
                            % (item["Id"], server_id),
                        )
                    )

                if item["UserData"]["Played"]:
                    context.append(
                        (
                            translate(16104),
                            "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)"
                            % (item["Id"], server_id),
                        )
                    )
                else:
                    context.append(
                        (
                            translate(16103),
                            "RunPlugin(plugin://plugin.video.jellyfin/?mode=watched&id=%s&server=%s)"
                            % (item["Id"], server_id),
                        )
                    )

                li.addContextMenuItems(context)
                list_li.append((path, li, True))

            elif item["Type"] == "Genre":

                params = {
                    "id": view_id or item["Id"],
                    "mode": "browse",
                    "type": get_folder_type(item, media) or media,
                    "folder": "genres-%s" % item["Id"],
                    "server": server_id,
                }
                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                list_li.append((path, li, True))

            else:
                if item["Type"] not in ("Photo", "PhotoAlbum"):
                    params = {"id": item["Id"], "mode": "play", "server": server_id}
                    path = "%s?%s" % (
                        "plugin://plugin.video.jellyfin/",
                        urlencode(params),
                    )
                    li.setProperty("path", path)
                    context = [
                        (
                            translate(13412),
                            "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)"
                            % (item["Id"], server_id),
                        )
                    ]

                    if item["UserData"]["Played"]:
                        context.append(
                            (
                                translate(16104),
                                "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)"
                                % (item["Id"], server_id),
                            )
                        )
                    else:
                        context.append(
                            (
                                translate(16103),
                                "RunPlugin(plugin://plugin.video.jellyfin/?mode=watched&id=%s&server=%s)"
                                % (item["Id"], server_id),
                            )
                        )

                    li.addContextMenuItems(context)

                list_li.append((li.getProperty("path"), li, False))

        xbmcplugin.addDirectoryItems(PROCESS_HANDLE, list_li, len(list_li))

    if content_type == "images":
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_VIDEO_TITLE)
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_DATE)
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_VIDEO_RATING)
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_VIDEO_RUNTIME)

    xbmcplugin.setContent(PROCESS_HANDLE, content_type)
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def browse_subfolders(media, view_id, server_id=None):
    """Display submenus for jellyfin views."""
    from ..views import DYNNODES

    view = Jellyfin(server_id).get_client().jellyfin.get_item(view_id)
    xbmcplugin.setPluginCategory(PROCESS_HANDLE, view["Name"])
    nodes = DYNNODES[media]

    for node in nodes:

        params = {
            "id": view_id,
            "mode": "browse",
            "type": media,
            "folder": view_id if node[0] == "all" else node[0],
            "server": server_id,
        }
        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
        directory(node[1] or view["Name"], path)

    xbmcplugin.setContent(PROCESS_HANDLE, "files")
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def browse_letters(media, view_id, server_id=None):
    """Display letters as options."""
    letters = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    view = Jellyfin(server_id).get_client().jellyfin.get_item(view_id)
    xbmcplugin.setPluginCategory(PROCESS_HANDLE, view["Name"])

    for node in letters:

        params = {
            "id": view_id,
            "mode": "browse",
            "type": media,
            "folder": "firstletter-%s" % node,
            "server": server_id,
        }
        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
        directory(node, path)

    xbmcplugin.setContent(PROCESS_HANDLE, "files")
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def get_folder_type(item, content_type=None):

    media = item["Type"]

    if media == "Series":
        return "seasons"
    elif media == "Season":
        return "episodes"
    elif media == "BoxSet":
        return "boxset"
    elif media == "MusicArtist":
        return "albums"
    elif media == "MusicAlbum":
        return "songs"
    elif media == "CollectionFolder":
        return item.get("CollectionType", "library")
    elif media == "Folder" and content_type == "music":
        return "albums"


def get_media_type(media):

    if media == "movies":
        return "Movie,BoxSet"
    elif media == "homevideos":
        return "Video,Folder,PhotoAlbum,Photo"
    elif media == "episodes":
        return "Episode"
    elif media == "boxsets":
        return "BoxSet"
    elif media == "tvshows":
        return "Series"
    elif media == "music":
        return "MusicArtist,MusicAlbum,Audio"


def get_fanart(item_id, path, server_id=None, api_client=None):
    """Get extra fanart for listitems. This is called by skinhelper.
    Images are stored locally, due to the Kodi caching system.
    """
    if not item_id and "plugin.video.jellyfin" in path:
        item_id = path.split("/")[-2]

    if not item_id:
        return

    LOG.info("[ extra fanart ] %s", item_id)
    objects = Objects()
    list_li = []
    directory = translate_path("special://thumbnails/jellyfin/%s/" % item_id)

    if not xbmcvfs.exists(directory):

        xbmcvfs.mkdirs(directory)
        item = api_client.get_item(item_id)
        obj = objects.map(item, "Artwork")
        backdrops = api.API(item).get_all_artwork(obj)
        tags = obj["BackdropTags"]

        for index, backdrop in enumerate(backdrops):

            tag = tags[index]
            fanart = os.path.join(directory, "fanart%s.jpg" % tag)
            li = xbmcgui.ListItem(tag, path=fanart)
            xbmcvfs.copy(backdrop, fanart)
            list_li.append((fanart, li, False))
    else:
        LOG.debug("cached backdrop found")
        dirs, files = xbmcvfs.listdir(directory)

        for file in files:
            fanart = os.path.join(directory, file)
            li = xbmcgui.ListItem(file, path=fanart)
            list_li.append((fanart, li, False))

    xbmcplugin.addDirectoryItems(PROCESS_HANDLE, list_li, len(list_li))
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def get_video_extras(item_id, path, server_id=None, api_client=None):
    """Returns the video files for the item as plugin listing, can be used
    to browse actual files or video extras, etc.
    """
    if not item_id and "plugin.video.jellyfin" in path:
        item_id = path.split("/")[-2]

    if not item_id:
        return

    # TODO implement????
    # Jellyfin(server_id).get_client().jellyfin.get_item(item_id)

    """
    def getVideoFiles(jellyfinId,jellyfinPath):
        #returns the video files for the item as plugin listing, can be used for browsing the actual files or videoextras etc.
        jellyfin = jellyfinserver.Read_JellyfinServer()
        if not jellyfinId:
            if "plugin.video.jellyfin" in jellyfinPath:
                jellyfinId = jellyfinPath.split("/")[-2]
        if jellyfinId:
            item = jellyfin.getItem(jellyfinId)
            putils = playutils.PlayUtils(item)
            if putils.isDirectPlay():
                #only proceed if we can access the files directly. TODO: copy local on the fly if accessed outside
                filelocation = putils.directPlay()
                if not filelocation.endswith("/"):
                    filelocation = filelocation.rpartition("/")[0]
                dirs, files = xbmcvfs.listdir(filelocation)
                for file in files:
                    file = filelocation + file
                    li = xbmcgui.ListItem(file, path=file)
                    xbmcplugin.addDirectoryItem(handle=PROCESS_HANDLE, url=file, listitem=li)
                for dir in dirs:
                    dir = filelocation + dir
                    li = xbmcgui.ListItem(dir, path=dir)
                    xbmcplugin.addDirectoryItem(handle=PROCESS_HANDLE, url=dir, listitem=li, isFolder=True)
        #xbmcplugin.endOfDirectory(PROCESS_HANDLE)
    """


def get_next_episodes(item_id, limit):
    """Only for synced content."""
    with Database("jellyfin") as jellyfindb:

        db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)
        library = db.get_view_name(item_id)

        if not library:
            return

    result = JSONRPC("VideoLibrary.GetTVShows").execute(
        {
            "sort": {"order": "descending", "method": "lastplayed"},
            "filter": {
                "and": [
                    {"operator": "true", "field": "inprogress", "value": ""},
                    {"operator": "is", "field": "tag", "value": "%s" % library},
                ]
            },
            "properties": ["title", "studio", "mpaa", "file", "art"],
        }
    )

    try:
        items = result["result"]["tvshows"]
    except (KeyError, TypeError):
        return

    list_li = []

    for item in items:
        if settings("ignoreSpecialsNextEpisodes.bool"):
            params = {
                "tvshowid": item["tvshowid"],
                "sort": {"method": "episode"},
                "filter": {
                    "and": [
                        {"operator": "lessthan", "field": "playcount", "value": "1"},
                        {"operator": "greaterthan", "field": "season", "value": "0"},
                    ]
                },
                "properties": [
                    "title",
                    "playcount",
                    "season",
                    "episode",
                    "showtitle",
                    "plot",
                    "file",
                    "rating",
                    "resume",
                    "tvshowid",
                    "art",
                    "streamdetails",
                    "firstaired",
                    "runtime",
                    "writer",
                    "dateadded",
                    "lastplayed",
                ],
                "limits": {"end": 1},
            }
        else:
            params = {
                "tvshowid": item["tvshowid"],
                "sort": {"method": "episode"},
                "filter": {"operator": "lessthan", "field": "playcount", "value": "1"},
                "properties": [
                    "title",
                    "playcount",
                    "season",
                    "episode",
                    "showtitle",
                    "plot",
                    "file",
                    "rating",
                    "resume",
                    "tvshowid",
                    "art",
                    "streamdetails",
                    "firstaired",
                    "runtime",
                    "writer",
                    "dateadded",
                    "lastplayed",
                ],
                "limits": {"end": 1},
            }

        result = JSONRPC("VideoLibrary.GetEpisodes").execute(params)

        try:
            episodes = result["result"]["episodes"]
        except (KeyError, TypeError):
            pass
        else:
            for episode in episodes:

                li = create_listitem(episode)
                list_li.append((episode["file"], li))

        if len(list_li) == limit:
            break

    xbmcplugin.addDirectoryItems(PROCESS_HANDLE, list_li, len(list_li))
    xbmcplugin.setContent(PROCESS_HANDLE, "episodes")
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)


def create_listitem(item):
    """Listitem based on jsonrpc items."""
    title = item["title"]
    label2 = ""
    li = xbmcgui.ListItem(title)
    li.setProperty("IsPlayable", "true")

    metadata = {
        "Title": title,
        "duration": str(item["runtime"] / 60),
        "Plot": item["plot"],
        "Playcount": item["playcount"],
    }

    if "showtitle" in item:
        metadata["TVshowTitle"] = item["showtitle"]
        label2 = item["showtitle"]

    if "episodeid" in item:
        # Listitem of episode
        metadata["mediatype"] = "episode"
        metadata["dbid"] = item["episodeid"]

    # TODO: Review once Krypton is RC - probably no longer needed if there's dbid
    if "episode" in item:
        episode = item["episode"]
        metadata["Episode"] = episode

    if "season" in item:
        season = item["season"]
        metadata["Season"] = season

    if season and episode:
        episodeno = "s%.2de%.2d" % (season, episode)
        li.setProperty("episodeno", episodeno)
        label2 = "%s - %s" % (label2, episodeno) if label2 else episodeno

    if "firstaired" in item:
        metadata["Premiered"] = item["firstaired"]

    if "rating" in item:
        metadata["Rating"] = str(round(float(item["rating"]), 1))

    if "director" in item:
        metadata["Director"] = " / ".join(item["director"])

    if "writer" in item:
        metadata["Writer"] = " / ".join(item["writer"])

    if "cast" in item:
        cast = []
        castandrole = []
        for person in item["cast"]:
            name = person["name"]
            cast.append(name)
            castandrole.append((name, person["role"]))
        metadata["Cast"] = cast
        metadata["CastAndRole"] = castandrole

    li.setLabel2(label2)
    li.setInfo(type="Video", infoLabels=metadata)
    li.setProperty("resumetime", str(item["resume"]["position"]))
    li.setProperty("totaltime", str(item["resume"]["total"]))
    li.setArt(item["art"])
    li.setProperty("dbid", str(item["episodeid"]))
    li.setProperty("fanart_image", item["art"].get("tvshow.fanart", ""))

    for key, value in item["streamdetails"].items():
        for stream in value:
            li.addStreamInfo(key, stream)

    return li


def add_user(api_client):
    """Add or remove users from the default server session."""
    if not window("jellyfin_online.bool"):
        return

    session = api_client.get_device(client.get_device_id())
    users = api_client.get_users()
    current = session[0]["AdditionalUsers"]

    result = dialog(
        "select",
        translate(33061),
        [translate(33062), translate(33063)] if current else [translate(33062)],
    )

    if result < 0:
        return

    if not result:  # Add user
        eligible = [
            x
            for x in users
            if x["Id"] not in [current_user["UserId"] for current_user in current]
        ]
        resp = dialog("select", translate(33064), [x["Name"] for x in eligible])

        if resp < 0:
            return

        user = eligible[resp]
        event("AddUser", {"Id": user["Id"], "Add": True})
    else:  # Remove user
        resp = dialog("select", translate(33064), [x["UserName"] for x in current])

        if resp < 0:
            return

        user = current[resp]
        event("AddUser", {"Id": user["UserId"], "Add": False})


def get_themes(api_client):
    """Add theme media locally, via strm. This is only for tv tunes.
    If another script is used, adjust this code.
    """
    from ..helper.utils import normalize_string
    from ..helper.playutils import PlayUtils
    from ..helper.xmls import tvtunes_nfo

    library = translate_path(
        "special://profile/addon_data/plugin.video.jellyfin/library"
    )
    play = settings("useDirectPaths") == "1"

    if not xbmcvfs.exists(library + "/"):
        xbmcvfs.mkdir(library)

    if xbmc.getCondVisibility("System.HasAddon(script.tvtunes)"):

        tvtunes = xbmcaddon.Addon(id="script.tvtunes")
        tvtunes.setSetting("custom_path_enable", "true")
        tvtunes.setSetting("custom_path", library)
        LOG.info("TV Tunes custom path is enabled and set.")
    else:
        dialog("ok", "{jellyfin}", translate(33152))

        return

    with Database("jellyfin") as jellyfindb:
        all_views = jellyfin_db.JellyfinDatabase(jellyfindb.cursor).get_views()
        views = [
            x.view_id
            for x in all_views
            if x.media_type in ("movies", "tvshows", "mixed")
        ]

    items = {}
    server = api_client.config.data["auth.server"]

    for view in views:
        result = api_client.get_items_theme_video(view)

        for item in result["Items"]:

            folder = normalize_string(item["Name"])
            items[item["Id"]] = folder

        result = api_client.get_items_theme_song(view)

        for item in result["Items"]:

            folder = normalize_string(item["Name"])
            items[item["Id"]] = folder

    for item in items:

        nfo_path = os.path.join(library, items[item])
        nfo_file = os.path.join(nfo_path, "tvtunes.nfo")

        if not xbmcvfs.exists(nfo_path):
            xbmcvfs.mkdir(nfo_path)

        themes = api_client.get_themes(item)
        paths = []

        for theme in (
            themes["ThemeVideosResult"]["Items"] + themes["ThemeSongsResult"]["Items"]
        ):
            putils = PlayUtils(theme, False, None, server, api_client)

            if play:
                paths.append(putils.direct_play(theme["MediaSources"][0]))
            else:
                paths.append(putils.direct_url(theme["MediaSources"][0]))

        tvtunes_nfo(nfo_file, paths)

    dialog(
        "notification",
        heading="{jellyfin}",
        message=translate(33153),
        icon="{jellyfin}",
        time=1000,
        sound=False,
    )


def delete_item():
    """Delete keymap action."""
    from . import context

    context.Context(delete=True)


def backup():
    """Jellyfin backup."""
    from ..helper.utils import delete_folder, copytree

    path = settings("backupPath")
    folder_name = "Kodi%s.%s" % (
        kodi_version(),
        xbmc.getInfoLabel("System.Date(dd-mm-yy)"),
    )
    folder_name = dialog("input", heading=translate(33089), defaultt=folder_name)

    if not folder_name:
        return

    backup = os.path.join(path, folder_name)

    if xbmcvfs.exists(backup + "/"):
        if not dialog("yesno", "{jellyfin}", translate(33090)):

            return backup()

        delete_folder(backup)

    addon_data = translate_path("special://profile/addon_data/plugin.video.jellyfin")
    destination_data = os.path.join(backup, "addon_data", "plugin.video.jellyfin")
    destination_databases = os.path.join(backup, "Database")

    if not xbmcvfs.mkdirs(path) or not xbmcvfs.mkdirs(destination_databases):

        LOG.info("Unable to create all directories")
        dialog(
            "notification",
            heading="{jellyfin}",
            icon="{jellyfin}",
            message=translate(33165),
            sound=False,
        )

        return

    copytree(addon_data, destination_data)

    databases = Objects().objects

    db = translate_path(databases["jellyfin"])
    xbmcvfs.copy(db, os.path.join(destination_databases, db.rsplit("\\", 1)[1]))
    LOG.info("copied jellyfin.db")

    db = translate_path(databases["video"])
    filename = db.rsplit("\\", 1)[1]
    xbmcvfs.copy(db, os.path.join(destination_databases, filename))
    LOG.info("copied %s", filename)

    if settings("enableMusic.bool"):

        db = translate_path(databases["music"])
        filename = db.rsplit("\\", 1)[1]
        xbmcvfs.copy(db, os.path.join(destination_databases, filename))
        LOG.info("copied %s", filename)

    LOG.info("backup completed")
    dialog("ok", "{jellyfin}", "%s %s" % (translate(33091), backup))


def get_filtered_section(
    parent_id=None,
    media=None,
    limit=None,
    recursive=None,
    sort=None,
    sort_order=None,
    filters=None,
    extra=None,
    server_id=None,
    api_client=None,
):
    """Get dynamic listings."""
    params = {
        "ParentId": parent_id,
        "IncludeItemTypes": media,
        "IsMissing": False,
        "Recursive": recursive if recursive is not None else True,
        "Limit": limit,
        "SortBy": sort or "SortName",
        "SortOrder": sort_order or "Ascending",
        "ImageTypeLimit": 1,
        "IsVirtualUnaired": False,
        "Fields": browse_info(),
    }
    if filters:
        if "Boxsets" in filters:
            filters.remove("Boxsets")
            params["CollapseBoxSetItems"] = settings("groupedSets.bool")

        params["Filters"] = ",".join(filters)

    if settings("getCast.bool"):
        params["Fields"] += ",People"

    if media and "Photo" in media:
        params["Fields"] += ",Width,Height"

    if extra is not None:
        params.update(extra)

    return api_client._get("Users/{UserId}/Items", params)


def browse_info():
    return (
        "DateCreated,EpisodeCount,SeasonCount,Path,Genres,Studios,Taglines,MediaStreams,Overview,Etag,"
        "ProductionLocations,Width,Height,RecursiveItemCount,ChildCount"
    )