# -*- coding: utf-8 -*-

import xbmc
import xbmcaddon
import xbmcgui
import requests
from requests.compat import urljoin
from requests.exceptions import HTTPError, RequestException
import json
import os, sys
import jwt


addon = xbmcaddon.Addon()
addon_name = addon.getAddonInfo('name')
dialog = xbmcgui.Dialog()

TIMEOUT = 60


def dialog_notification(message, heading='Medusa failed downloads', icon=xbmcgui.NOTIFICATION_INFO):
    dialog.notification(heading, message, icon)


def dialog_ok(line1, *args, **kwargs):
    dialog.ok(addon_name, line1)


def format_episode(season, episode):
    return 's{}e{}'.format(str(season).zfill(2), str(episode).zfill(2))

class MedusaApi(object):
    """Class for communicating with Medusa's apiv1, apiv2 and webroutes."""
    MEDUSA_SESSION = requests.Session()
    MEDUSA_API_V2_SESSION = requests.Session()

    def __init__(self, settings):
        self.url = settings.url
        self.username = settings.username
        self.password = settings.password
        self.dialog = xbmcgui.Dialog()
        self.api_key = ''

    def authenticate(self):
        """
        Authenticate against /api/v2/authenticate and use the username/password to get the jwt token.
        The jwt token is decoded without verification to get the api-key
        """
        response = None
        try:
            headers = {
                'Content-Type': 'application/json'
            }
            url = urljoin(self.url, 'api/v2/authenticate')
            data = {
                'username': self.username,
                'password': self.password
            }
            response = MedusaApi.MEDUSA_SESSION.post(url, json=data, headers=headers, verify=False, timeout=TIMEOUT)
            response.raise_for_status()
        except HTTPError as error:
            xbmc.log(
                'Something went wrong, couldn not authenticate to {0} with error: {1}'.format(
                    self.url, error
                ),
                xbmc.LOGWARNING)
        except Exception as error:
            xbmc.log(
                'Something went wrong, couldn not authenticate to {0} with error: {1}'.format(
                    self.url, error
                ), xbmc.LOGWARNING)

        try:
            jwt_encoded = response.json() if response else response
        except (AttributeError, ValueError):
            return None

        # Decode the jwt into the api-key
        if jwt_encoded.get('token'):
            decoded = jwt.decode(jwt_encoded['token'], '', algorithms=['HS256'], verify=False, timeout=TIMEOUT)
            MedusaApi.MEDUSA_API_V2_SESSION.headers.update({
                'X-Api-Key': decoded['apiKey']
            })
            self.api_key = decoded['apiKey']

    def get_series(self, unique_ids=None):
        """Use the apiv2 to get the series data with the indexer provided."""
        for indexer in ['imdb', 'tmdb', 'tvdb', 'tvmaze']:
            try:
                if not unique_ids.get(indexer):
                    continue

                series_id = unique_ids[indexer]
                if indexer == 'imdb' and series_id:
                    series_id = int(series_id.split('tt')[-1])

                response = self.api_v2_request(url='api/v2/series/{indexer}{series_id}'.format(
                    indexer=indexer, series_id=series_id
                ))
                response.raise_for_status()
            except HTTPError as error:
                # Show not found. This can happen because we search against multiple indexers.
                xbmc.log('Failed retrieving series, error: {0}'.format(error), xbmc.LOGINFO)
            except RequestException as error:
                dialog_notification(
                    'Something went wrong trying to connect to {url}. Error: {error}'.format(
                        url=self.url, error=error
                    ),
                    xbmcgui.NOTIFICATION_WARNING
                )
                xbmc.log('Something went wrong trying to connect to {url}. Error: {error}'.format(
                    url=self.url, error=error
                ), xbmc.LOGERROR)
            else:
                return response.json()

    def api_v2_request(self, method='get', url=None, params=None, data=None, headers=None):
        """Request a resource using medusa's api v2."""
        if not MedusaApi.MEDUSA_API_V2_SESSION.headers.get('X-Api-Key'):
            dialog_notification('Your not authenticated to medusas api v2!', xbmcgui.NOTIFICATION_WARNING)

        full_url = urljoin(self.url, url)
        return MedusaApi.MEDUSA_API_V2_SESSION.request(method, full_url, params=params, json=data, verify=False, timeout=TIMEOUT, headers=headers)


class MedusaFailed(object):
    def __init__(self, settings):
        self.settings = settings
        self.addon = xbmcaddon.Addon()
        self.addon_name = self.addon.getAddonInfo('name')
        self.medusa = MedusaApi(settings)
        self.medusa.authenticate()

    def match_series(self, episode_db_id):
        unique_ids = None

        # Get episode details
        json_data = {
            'jsonrpc': '2.0', 'id': '1', 'method': 'VideoLibrary.GetEpisodeDetails',
            'params': {
                'episodeid': int(episode_db_id), 'properties': ['tvshowid']
            }
        }
        response = xbmc.executeJSONRPC(json.dumps(json_data))
        json_response = json.loads(response)

        # Get show details
        json_data = {
            'jsonrpc': '2.0', 'id': 1,
            'method': 'VideoLibrary.GetTVShowDetails',
            'params': {'tvshowid': json_response['result']['episodedetails']['tvshowid'], 'properties': ['uniqueid']}
        }
        response = xbmc.executeJSONRPC(json.dumps(json_data))
        json_response = json.loads(response)

        if json_response.get('result'):
            unique_ids = json_response['result']['tvshowdetails']['uniqueid']
            xbmc.log('Found a show for provided episode id, with tvdb id: {0}'.format(unique_ids), xbmc.LOGDEBUG)

        if unique_ids:
            return self.medusa.get_series(unique_ids)

    def search_episode(self, show, season, episode):
        """Search for episode using a normal forced search."""
        headers = {'Content-Type': 'application/json'}
        data = {
            'showSlug': show['id']['slug'],
            'episodes': [
                format_episode(season, episode)
            ],
            'options': {}
        }
        return self.medusa.api_v2_request(method='put', url='api/v2/search/backlog', data=data, headers=headers)

    def retry_episode(self, show, season, episode):
        """Search for episode using the failed search process."""
        headers = {'Content-Type': 'application/json'}
        data = {
            'showSlug': show['id']['slug'],
            'episodes': [
                format_episode(season, episode)
            ],
            'options': {}
        }
        return self.medusa.api_v2_request(method='put', url='api/v2/search/failed', data=data, headers=headers)

    def start_search(self, show, season, episode):
        # Start a new forced search
        dialog_notification('Started search for for {episode} of show {show}'.format(
            episode=format_episode(season, episode), show=show.get('title')
        ), xbmcgui.NOTIFICATION_INFO)

        try:
            response = self.retry_episode(show, season, episode)
            response.raise_for_status()
        except HTTPError as error:
            dialog_notification(
                'Error while trying to start a search. Error: {error}'.format(error=error),
                xbmcgui.NOTIFICATION_WARNING
            )
            xbmc.log('Error while trying to start a search. Error: {error}'.format(error=error), xbmc.LOGERROR)
        except RequestException as error:
            dialog_notification(
                'Something went wrong trying to connect to {url}. Error: {error}'.format(
                    url=self.medusa.url, error=error
                ),
                xbmcgui.NOTIFICATION_WARNING
            )
            xbmc.log('Something went wrong trying to connect to {url}. Error: {error}'.format(
                    url=self.medusa.url, error=error
            ), xbmc.LOGERROR)
        else:
            xbmc.log(
                'Search url: {url}\nrequest: {request!r}\nresponse: {response!r}'.format(
                    url=response.request.url,
                    request=response.request.headers,
                    response=response.text
                ),
                xbmc.LOGDEBUG
            )

            if 'started' in response.text:
                dialog_ok('Successful started search for {episode} of show {show}'.format(
                    episode=format_episode(season, episode), show=show.get('title')
                ))
            else:
                dialog_notification(
                    'Error while searching for episode. Error: {error}'.format(error=response.text),
                    xbmcgui.NOTIFICATION_WARNING
                )
                xbmc.log(
                    'Error while searching for episode. Error: {error}'.format(error=response.text),
                    xbmcgui.NOTIFICATION_WARNING
                )

    def run(self):
        """Run main of plugin."""
        list_item_show_title = sys.listitem.getVideoInfoTag().getTVShowTitle()
        list_item_season = sys.listitem.getVideoInfoTag().getSeason()
        list_item_episode = sys.listitem.getVideoInfoTag().getEpisode()

        # Let's match kodi's episode dbId -> kodi's series dbId -> medusa's tvdb id.
        show = self.match_series(sys.listitem.getVideoInfoTag().getDbId())

        if not show:
            dialog_notification("Medusa could not locate series {0}".format(
                list_item_show_title
            ), xbmcgui.NOTIFICATION_WARNING)
            xbmc.log("Medusa could not locate series {0}".format(list_item_show_title), xbmc.LOGWARNING)
            return

        # Give medusa the instruction to start a new forced search.
        self.start_search(show, list_item_season, list_item_episode)
