import logging

from O365.connection import Connection
from O365.fluent_message import Message

log = logging.getLogger(__name__)


class FluentInbox(object):
    url_dict = {
        'inbox': {
            '1.0': 'https://outlook.office365.com/api/v1.0/me/messages',
            '2.0': 'https://graph.microsoft.com/v1.0/me/messages',
        },

        'folders': {
            '1.0': 'https://outlook.office365.com/api/v1.0/me/Folders',
            '2.0': 'https://graph.microsoft.com/v1.0/me/MailFolders',
        },

        'folder': {
            '1.0': 'https://outlook.office365.com/api/v1.0/me/Folders/{folder_id}/messages',
            '2.0': 'https://graph.microsoft.com/v1.0/me/MailFolders/{folder_id}/messages',
        },
    }

    def __init__(self, verify=True):
        """ Creates a new inbox wrapper.

        :param verify: whether or not to verify SSL certificate
        """
        self.url = FluentInbox._get_url('inbox')
        self.fetched_count = 0
        self._filter = ''
        self._search = ''
        self.verify = verify
        self.messages = []

    def from_folder(self, folder_name):
        """ Configure to use this folder for fetching the mails

        :param folder_name: name of the outlook folder
        """
        self._reset()
        response = Connection.get_response(FluentInbox._get_url('folders'),
                                           verify=self.verify,
                                           params={'$top': 100})

        folder_id = None
        all_folders = []

        for folder in response:
            if folder['displayName'] == folder_name:
                folder_id = folder['id']
                break

            all_folders.append(folder['displayName'])

        if not folder_id:
            raise RuntimeError('Folder "{}" is not found, available folders '
                               'are {}'.format(folder_name, all_folders))

        self.url = FluentInbox._get_url('folder').format(folder_id=folder_id)

        return self

    def filter(self, filter_string):
        """ Set the value of a filter. More information on what filters are available can be found here:
        https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#RESTAPIResourcesMessage
        More improvements coming soon

        :param filter_string: The string that represents the filters you want to enact.
                should be something like: (HasAttachments eq true) and (IsRead eq false) or just: IsRead eq false
                test your filter string here: https://outlook.office365.com/api/v1.0/me/messages?$filter=
                if that accepts it then you know it works.
        """
        self._filter = filter_string
        return self

    def search(self, search_string):
        """ Set the value of a search. More information on what searches are available can be found here:
        https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#RESTAPIResourcesMessage
        More improvements coming soon

        :param search_string: The search string you want to use

        Should be something like: "Category:Action AND Subject:Test" or just: "Subject:Test".

        Test your search string here: "https://outlook.office365.com/api/v1.0/me/messages?$search="
        or directly in your mailbox, if that accepts it then you know it works.
        """
        self._search = search_string
        return self

    def fetch_first(self, count=10):
        """ Fetch the first n messages, where n is the specified count

        :param count: no.of messages to fetch
        """
        self.fetched_count = 0
        return self.fetch_next(count=count)

    def skip(self, count):
        """ Skips the first n messages, where n is the specified count

        :param count: no.of messages to skip
        """
        self.fetched_count = count
        return self

    def fetch(self, count=10):
        """ Fetch n messages from the result, where n is the specified count

        :param count: no.of messages to fetch
        """
        return self.fetch_next(count=count)

    def fetch_next(self, count=1):
        """ Fetch the next n messages after the previous fetch, where n is the specified count

        :param count: no.of messages to fetch
        """
        skip_count = self.fetched_count
        if self._search:
            params = {'$filter': self._filter, '$top': count,
                      '$search': '"{}"'.format(self._search)}
        else:
            params = {'$filter': self._filter, '$top': count,
                      '$skip': skip_count}

        response = Connection.get_response(self.url, verify=self.verify,
                                           params=params)
        self.fetched_count += count

        messages = []
        for message in response:
            messages.append(Message(message, Connection().auth))

        return messages

    @staticmethod
    def _get_url(key):
        """ Fetches the url for specified key as per the connection version configured

        :param key: the key for which url is required
        :return: URL to use for requests
        """
        return FluentInbox.url_dict[key][Connection().api_version]

    def _reset(self):
        """ Resets the current reference """
        self.fetched_count = 0
        self.messages = []
