import datetime

from loguru import logger
from requests.exceptions import RequestException

from flexget import plugin
from flexget.config_schema import one_or_more
from flexget.event import event
from flexget.plugin import PluginWarning
from flexget.utils.requests import Session as RequestSession
from flexget.utils.requests import TimedLimiter

plugin_name = 'pushover'
logger = logger.bind(name=plugin_name)

PUSHOVER_URL = 'https://api.pushover.net/1/messages.json'

requests = RequestSession(max_retries=3)
requests.add_domain_limiter(TimedLimiter('pushover.net', '5 seconds'))


class PushoverNotifier:
    """
    Example::

      notify:
        entries:
          via:
            - pushover:
                user_key: <USER_KEY> (can also be a list of userkeys)
                token: <TOKEN>
                [device: <DEVICE_STRING>]
                [priority: <PRIORITY>]
                [url: <URL>]
                [url_title: <URL_TITLE>]
                [sound: <SOUND>]
                [retry]: <RETRY>]
                [expire]: <EXPIRE>]
                [callback]: <CALLBACK>]
                [html]: <HTML>]
    """

    schema = {
        'type': 'object',
        'properties': {
            'user_key': one_or_more({'type': 'string'}),
            'api_key': {'type': 'string', 'default': 'aPwSHwkLcNaavShxktBpgJH4bRWc3m'},
            'device': one_or_more({'type': 'string'}),
            'priority': {
                'oneOf': [{'type': 'number', 'minimum': -2, 'maximum': 2}, {'type': 'string'}]
            },
            'url': {'type': 'string'},
            'url_title': {'type': 'string'},
            'sound': {'type': 'string'},
            'retry': {'type': 'integer', 'minimum': 30},
            'expire': {'type': 'integer', 'maximum': 86400},
            'callback': {'type': 'string'},
            'html': {'type': 'boolean'},
        },
        'required': ['user_key'],
        'additionalProperties': False,
    }

    def notify(self, title, message, config):
        """
        Sends a Pushover notification

        :param str title: the message's title
        :param str message: the message to send
        :param dict config: The pushover config
        """
        notification = {
            'token': config.get('api_key'),
            'message': message,
            'title': title,
            'device': config.get('device'),
            'priority': config.get('priority'),
            'url': config.get('url'),
            'url_title': config.get('url_title'),
            'sound': config.get('sound'),
            'retry': config.get('retry'),
            'expire': config.get('expire'),
            'callback': config.get('callback'),
        }

        # HTML parsing mode
        if config.get('html'):
            notification['html'] = 1

        # Support multiple devices
        if isinstance(notification['device'], list):
            notification['device'] = ','.join(notification['device'])

        # Special case, verify certain fields exists if priority is 2
        priority = config.get('priority')
        expire = config.get('expire')
        retry = config.get('retry')
        if priority == 2 and not all([expire, retry]):
            logger.warning(
                'Priority set to 2 but fields "expire" and "retry" are not both present.Lowering priority to 1'
            )
            notification['priority'] = 1

        if not isinstance(config['user_key'], list):
            config['user_key'] = [config['user_key']]

        for user in config['user_key']:
            notification['user'] = user
            try:
                response = requests.post(PUSHOVER_URL, data=notification)
            except RequestException as e:
                if e.response is not None:
                    if e.response.status_code == 429:
                        app_reset = e.response.headers.get('X-Limit-App-Reset')
                        if app_reset:
                            reset_time = datetime.datetime.fromtimestamp(int(app_reset)).strftime(
                                '%Y-%m-%d %H:%M:%S'
                            )
                            error_message = (
                                f'Monthly pushover message limit reached. Next reset: {reset_time}'
                            )

                    else:
                        error_message = e.response.json()['errors'][0]
                else:
                    error_message = str(e)
                raise PluginWarning(error_message)

            reset_time = datetime.datetime.fromtimestamp(
                int(response.headers['X-Limit-App-Reset'])
            ).strftime('%Y-%m-%d %H:%M:%S')
            remaining = response.headers['X-Limit-App-Remaining']
            logger.debug(
                'Pushover notification sent. Notifications remaining until next reset: {}. Next reset at: {}',
                remaining,
                reset_time,
            )


@event('plugin.register')
def register_plugin():
    plugin.register(PushoverNotifier, plugin_name, api_ver=2, interfaces=['notifiers'])
