import datetime as dt
import logging
from enum import Enum

from .message import Message
from .utils import (
    NEXT_LINK_KEYWORD,
    ApiComponent,
    OutlookWellKnowFolderNames,
    Pagination,
)

log = logging.getLogger(__name__)


class ExternalAudience(Enum):
    """Valid values for externalAudience."""

    NONE = "none"
    CONTACTSONLY = "contactsOnly"
    ALL = "all"


class AutoReplyStatus(Enum):
    """Valid values for status."""

    DISABLED = "disabled"
    ALWAYSENABLED = "alwaysEnabled"
    SCHEDULED = "scheduled"


class AutomaticRepliesSettings(ApiComponent):
    """The MailboxSettings."""

    def __init__(self, *, parent=None, con=None, **kwargs):
        """Representation of the AutomaticRepliesSettings.

        :param parent: parent object
        :type parent: Mailbox
        :param Connection con: connection to use if no parent specified
        :param Protocol protocol: protocol to use if no parent specified
         (kwargs)
        :param str main_resource: use this resource instead of parent resource
         (kwargs)
        """
        if parent and con:
            raise ValueError("Need a parent or a connection but not both")
        self.con = parent.con if parent else con

        # Choose the main_resource passed in kwargs over parent main_resource
        main_resource = kwargs.pop("main_resource", None) or (
            getattr(parent, "main_resource", None) if parent else None
        )

        super().__init__(
            protocol=parent.protocol if parent else kwargs.get("protocol"),
            main_resource=main_resource,
        )

        cloud_data = kwargs.get(self._cloud_data_key, {})
        self.__external_audience = ExternalAudience(
            cloud_data.get(self._cc("externalAudience"), "")
        )
        self.external_reply_message = cloud_data.get(
            self._cc("externalReplyMessage"), ""
        )
        self.internal_reply_message = cloud_data.get(
            self._cc("internalReplyMessage"), ""
        )
        scheduled_enddatetime_ob = cloud_data.get(self._cc("scheduledEndDateTime"), {})
        self.__scheduled_enddatetime = self._parse_date_time_time_zone(
            scheduled_enddatetime_ob
        )

        scheduled_startdatetime_ob = cloud_data.get(
            self._cc("scheduledStartDateTime"), {}
        )
        self.__scheduled_startdatetime = self._parse_date_time_time_zone(
            scheduled_startdatetime_ob
        )

        self.__status = AutoReplyStatus(cloud_data.get(self._cc("status"), ""))

    def __str__(self):
        """Representation of the AutomaticRepliesSettings via the Graph api as a string."""
        return self.__repr__()

    @property
    def scheduled_startdatetime(self):
        """Scheduled Start Time of auto reply.

        :getter: get the scheduled_startdatetime time
        :setter: set the scheduled_startdatetime time
        :type: datetime
        """
        return self.__scheduled_startdatetime

    @scheduled_startdatetime.setter
    def scheduled_startdatetime(self, value):
        if not isinstance(value, dt.date):
            raise ValueError(
                "'scheduled_startdatetime' must be a valid datetime object"
            )
        if not isinstance(value, dt.datetime):
            # force datetime
            value = dt.datetime(value.year, value.month, value.day)
        if value.tzinfo is None:
            # localize datetime
            value = self.protocol.timezone.localize(value)
        elif value.tzinfo != self.protocol.timezone:
            value = value.astimezone(self.protocol.timezone)
        self.__scheduled_startdatetime = value

    @property
    def scheduled_enddatetime(self):
        """Scheduled End Time of auto reply.

        :getter: get the scheduled_enddatetime time
        :setter: set the reminder time
        :type: datetime
        """
        return self.__scheduled_enddatetime

    @scheduled_enddatetime.setter
    def scheduled_enddatetime(self, value):
        if not isinstance(value, dt.date):
            raise ValueError("'scheduled_enddatetime' must be a valid datetime object")
        if not isinstance(value, dt.datetime):
            # force datetime
            value = dt.datetime(value.year, value.month, value.day)
        if value.tzinfo is None:
            # localize datetime
            value = self.protocol.timezone.localize(value)
        elif value.tzinfo != self.protocol.timezone:
            value = value.astimezone(self.protocol.timezone)
        self.__scheduled_enddatetime = value

    @property
    def status(self) -> AutoReplyStatus:
        """Status of auto reply.

        :getter: get the status of auto reply
        :setter: set the status of auto reply
        :type: autoreplystatus
        """
        return self.__status

    @status.setter
    def status(self, value: AutoReplyStatus = AutoReplyStatus.DISABLED):
        self.__status = AutoReplyStatus(value)

    @property
    def external_audience(self) -> ExternalAudience:
        """External Audience of auto reply.

        :getter: get the external audience of auto reply
        :setter: set the external audience of auto reply
        :type: autoreplystatus
        """
        return self.__external_audience

    @external_audience.setter
    def external_audience(self, value: ExternalAudience = ExternalAudience.ALL):
        if not value:
            value = ExternalAudience.ALL
        self.__external_audience = ExternalAudience(value)


class MailboxSettings(ApiComponent):
    """The MailboxSettings."""

    _endpoints = {
        "settings": "/mailboxSettings",
    }
    autoreply_constructor = AutomaticRepliesSettings

    def __init__(self, *, parent=None, con=None, **kwargs):
        """Representation of the MailboxSettings.

        :param parent: parent object
        :type parent: Mailbox
        :param Connection con: connection to use if no parent specified
        :param Protocol protocol: protocol to use if no parent specified
         (kwargs)
        :param str main_resource: use this resource instead of parent resource
         (kwargs)
        """
        if parent and con:
            raise ValueError("Need a parent or a connection but not both")
        self.con = parent.con if parent else con

        # Choose the main_resource passed in kwargs over parent main_resource
        main_resource = kwargs.pop("main_resource", None) or (
            getattr(parent, "main_resource", None) if parent else None
        )

        super().__init__(
            protocol=parent.protocol if parent else kwargs.get("protocol"),
            main_resource=main_resource,
        )

        cloud_data = kwargs.get(self._cloud_data_key, {})
        autorepliessettings = cloud_data.get("automaticRepliesSetting")
        self.automaticrepliessettings = self.autoreply_constructor(
            parent=self, **{self._cloud_data_key: autorepliessettings}
        )

    def __str__(self):
        """Representation of the MailboxSetting via the Graph api as a string."""
        return self.__repr__()

    def save(self):
        """Save the MailboxSettings.

        :return: Success / Failure
        :rtype: bool
        """
        url = self.build_url(self._endpoints.get("settings"))
        cc = self._cc
        ars = self.automaticrepliessettings
        automatic_reply_settings = {
            cc("status"): ars.status.value,
            cc("externalAudience"): ars.external_audience.value,
            cc("internalReplyMessage"): ars.internal_reply_message,
            cc("externalReplyMessage"): ars.external_reply_message,
        }
        if ars.status == AutoReplyStatus.SCHEDULED:
            automatic_reply_settings[
                cc("scheduledStartDateTime")
            ] = self._build_date_time_time_zone(ars.scheduled_startdatetime)
            automatic_reply_settings[
                cc("scheduledEndDateTime")
            ] = self._build_date_time_time_zone(ars.scheduled_enddatetime)

        data = {cc("automaticRepliesSetting"): automatic_reply_settings}

        response = self.con.patch(url, data=data)

        return bool(response)


class Folder(ApiComponent):
    """A Mail Folder representation."""

    _endpoints = {
        "root_folders": "/mailFolders",
        "child_folders": "/mailFolders/{id}/childFolders",
        "get_folder": "/mailFolders/{id}",
        "root_messages": "/messages",
        "folder_messages": "/mailFolders/{id}/messages",
        "copy_folder": "/mailFolders/{id}/copy",
        "move_folder": "/mailFolders/{id}/move",
        "message": "/messages/{id}",
    }
    message_constructor = Message

    def __init__(self, *, parent=None, con=None, **kwargs):
        """Create an instance to represent the specified folder in given
        parent folder

        :param parent: parent folder/account for this folder
        :type parent: mailbox.Folder or Account
        :param Connection con: connection to use if no parent specified
        :param Protocol protocol: protocol to use if no parent specified
         (kwargs)
        :param str main_resource: use this resource instead of parent resource
         (kwargs)
        :param str name: name of the folder to get under the parent (kwargs)
        :param str folder_id: id of the folder to get under the parent (kwargs)
        """
        if parent and con:
            raise ValueError("Need a parent or a connection but not both")
        self.con = parent.con if parent else con
        self.parent = parent if isinstance(parent, Folder) else None

        # This folder has no parents if root = True.
        self.root = kwargs.pop("root", False)

        # Choose the main_resource passed in kwargs over parent main_resource
        main_resource = kwargs.pop("main_resource", None) or (
            getattr(parent, "main_resource", None) if parent else None
        )

        super().__init__(
            protocol=parent.protocol if parent else kwargs.get("protocol"),
            main_resource=main_resource,
        )

        cloud_data = kwargs.get(self._cloud_data_key, {})

        # Fallback to manual folder if nothing available on cloud data
        self.name = cloud_data.get(self._cc("displayName"), kwargs.get("name", ""))
        if self.root is False:
            # Fallback to manual folder if nothing available on cloud data
            self.folder_id = cloud_data.get(
                self._cc("id"), kwargs.get("folder_id", None)
            )
            self.parent_id = cloud_data.get(self._cc("parentFolderId"), None)
            self.child_folders_count = cloud_data.get(self._cc("childFolderCount"), 0)
            self.unread_items_count = cloud_data.get(self._cc("unreadItemCount"), 0)
            self.total_items_count = cloud_data.get(self._cc("totalItemCount"), 0)
            self.updated_at = dt.datetime.now()
        else:
            self.folder_id = "root"

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return "{} from resource: {}".format(self.name, self.main_resource)

    def __eq__(self, other):
        return self.folder_id == other.folder_id

    def get_folders(self, limit=None, *, query=None, order_by=None, batch=None):
        """Return a list of child folders matching the query.

        :param int limit: max no. of folders to get. Over 999 uses batch.
        :param query: applies a filter to the request such as
         "displayName eq 'HelloFolder'"
        :type query: Query or str
        :param order_by: orders the result set based on this condition
        :type order_by: Query or str
        :param int batch: batch size, retrieves items in
         batches allowing to retrieve more items than the limit.
        :return: list of folders
        :rtype: list[mailbox.Folder] or Pagination
        """
        if self.root:
            url = self.build_url(self._endpoints.get("root_folders"))
        else:
            url = self.build_url(
                self._endpoints.get("child_folders").format(id=self.folder_id)
            )

        if limit is None or limit > self.protocol.max_top_value:
            batch = self.protocol.max_top_value

        params = {"$top": batch if batch else limit}

        if order_by:
            params["$orderby"] = order_by

        if query:
            if isinstance(query, str):
                params["$filter"] = query
            else:
                params.update(query.as_params())

        response = self.con.get(url, params=params)
        if not response:
            return []

        data = response.json()

        # Everything received from cloud must be passed as self._cloud_data_key
        self_class = getattr(self, "folder_constructor", type(self))
        folders = [
            self_class(parent=self, **{self._cloud_data_key: folder})
            for folder in data.get("value", [])
        ]
        next_link = data.get(NEXT_LINK_KEYWORD, None)
        if batch and next_link:
            return Pagination(
                parent=self,
                data=folders,
                constructor=self_class,
                next_link=next_link,
                limit=limit,
            )
        else:
            return folders

    def get_message(self, object_id=None, query=None, *, download_attachments=False):
        """Get one message from the query result.
         A shortcut to get_messages with limit=1
        :param object_id: the message id to be retrieved.
        :param query: applies a filter to the request such as
         "displayName eq 'HelloFolder'"
        :type query: Query or str
        :param bool download_attachments: whether or not to download attachments
        :return: one Message
        :rtype: Message or None
        """
        if object_id is None and query is None:
            raise ValueError("Must provide object id or query.")

        if object_id is not None:
            url = self.build_url(self._endpoints.get("message").format(id=object_id))
            params = None
            if query and (query.has_selects or query.has_expands):
                params = query.as_params()
            response = self.con.get(url, params=params)
            if not response:
                return None

            message = response.json()

            return self.message_constructor(
                parent=self,
                download_attachments=download_attachments,
                **{self._cloud_data_key: message},
            )

        else:
            messages = list(
                self.get_messages(
                    limit=1, query=query, download_attachments=download_attachments
                )
            )

            return messages[0] if messages else None

    def get_messages(
        self,
        limit=25,
        *,
        query=None,
        order_by=None,
        batch=None,
        download_attachments=False,
    ):
        """
        Downloads messages from this folder

        :param int limit: limits the result set. Over 999 uses batch.
        :param query: applies a filter to the request such as
         "displayName eq 'HelloFolder'"
        :type query: Query or str
        :param order_by: orders the result set based on this condition
        :type order_by: Query or str
        :param int batch: batch size, retrieves items in
         batches allowing to retrieve more items than the limit.
        :param bool download_attachments: whether or not to download attachments
        :return: list of messages
        :rtype: list[Message] or Pagination
        """

        if self.root:
            url = self.build_url(self._endpoints.get("root_messages"))
        else:
            url = self.build_url(
                self._endpoints.get("folder_messages").format(id=self.folder_id)
            )

        if not batch and (limit is None or limit > self.protocol.max_top_value):
            batch = self.protocol.max_top_value

        params = {"$top": batch if batch else limit}

        if order_by:
            params["$orderby"] = order_by

        if query:
            if isinstance(query, str):
                params["$filter"] = query
            else:
                params.update(query.as_params())

        response = self.con.get(url, params=params)
        if not response:
            return iter(())

        data = response.json()

        # Everything received from cloud must be passed as self._cloud_data_key
        messages = (
            self.message_constructor(
                parent=self,
                download_attachments=download_attachments,
                **{self._cloud_data_key: message},
            )
            for message in data.get("value", [])
        )

        next_link = data.get(NEXT_LINK_KEYWORD, None)
        if batch and next_link:
            return Pagination(
                parent=self,
                data=messages,
                constructor=self.message_constructor,
                next_link=next_link,
                limit=limit,
                download_attachments=download_attachments,
            )
        else:
            return messages

    def create_child_folder(self, folder_name):
        """Creates a new child folder under this folder

        :param str folder_name: name of the folder to add
        :return: newly created folder
        :rtype: mailbox.Folder or None
        """
        if not folder_name:
            return None

        if self.root:
            url = self.build_url(self._endpoints.get("root_folders"))
        else:
            url = self.build_url(
                self._endpoints.get("child_folders").format(id=self.folder_id)
            )

        response = self.con.post(url, data={self._cc("displayName"): folder_name})
        if not response:
            return None

        folder = response.json()

        self_class = getattr(self, "folder_constructor", type(self))
        # Everything received from cloud must be passed as self._cloud_data_key
        return self_class(parent=self, **{self._cloud_data_key: folder})

    def get_folder(self, *, folder_id=None, folder_name=None):
        """Get a folder by it's id or name

        :param str folder_id: the folder_id to be retrieved.
         Can be any folder Id (child or not)
        :param str folder_name: the folder name to be retrieved.
         Must be a child of this folder.
        :return: a single folder
        :rtype: mailbox.Folder or None
        """
        if folder_id and folder_name:
            raise RuntimeError("Provide only one of the options")

        if not folder_id and not folder_name:
            raise RuntimeError("Provide one of the options")

        if folder_id:
            # get folder by it's id, independent of the parent of this folder_id
            url = self.build_url(self._endpoints.get("get_folder").format(id=folder_id))
            params = None
        else:
            # get folder by name. Only looks up in child folders.
            if self.root:
                url = self.build_url(self._endpoints.get("root_folders"))
            else:
                url = self.build_url(
                    self._endpoints.get("child_folders").format(id=self.folder_id)
                )
            params = {
                "$filter": "{} eq '{}'".format(self._cc("displayName"), folder_name),
                "$top": 1,
            }

        response = self.con.get(url, params=params)
        if not response:
            return None

        if folder_id:
            folder = response.json()
        else:
            folder = response.json().get("value")
            folder = folder[0] if folder else None
            if folder is None:
                return None

        self_class = getattr(self, "folder_constructor", type(self))
        # Everything received from cloud must be passed as self._cloud_data_key
        # We don't pass parent, as this folder may not be a child of self.
        return self_class(
            con=self.con,
            protocol=self.protocol,
            main_resource=self.main_resource,
            **{self._cloud_data_key: folder},
        )

    def refresh_folder(self, update_parent_if_changed=False):
        """Re-download folder data
        Inbox Folder will be unable to download its own data (no folder_id)

        :param bool update_parent_if_changed: updates self.parent with new
         parent Folder if changed
        :return: Refreshed or Not
        :rtype: bool
        """
        folder_id = getattr(self, "folder_id", None)
        if self.root or folder_id is None:
            return False

        folder = self.get_folder(folder_id=folder_id)
        if folder is None:
            return False

        self.name = folder.name
        if folder.parent_id and self.parent_id:
            if folder.parent_id != self.parent_id:
                self.parent_id = folder.parent_id
                self.parent = (
                    self.get_parent_folder() if update_parent_if_changed else None
                )
        self.child_folders_count = folder.child_folders_count
        self.unread_items_count = folder.unread_items_count
        self.total_items_count = folder.total_items_count
        self.updated_at = folder.updated_at

        return True

    def get_parent_folder(self):
        """Get the parent folder from attribute self.parent or
        getting it from the cloud

        :return: Parent Folder
        :rtype: mailbox.Folder or None
        """
        if self.root:
            return None
        if self.parent:
            return self.parent

        if self.parent_id:
            self.parent = self.get_folder(folder_id=self.parent_id)
        return self.parent

    def update_folder_name(self, name, update_folder_data=True):
        """Change this folder name

        :param str name: new name to change to
        :param bool update_folder_data: whether or not to re-fetch the data
        :return: Updated or Not
        :rtype: bool
        """
        if self.root:
            return False
        if not name:
            return False

        url = self.build_url(
            self._endpoints.get("get_folder").format(id=self.folder_id)
        )

        response = self.con.patch(url, data={self._cc("displayName"): name})
        if not response:
            return False

        self.name = name
        if not update_folder_data:
            return True

        folder = response.json()

        self.name = folder.get(self._cc("displayName"), "")
        self.parent_id = folder.get(self._cc("parentFolderId"), None)
        self.child_folders_count = folder.get(self._cc("childFolderCount"), 0)
        self.unread_items_count = folder.get(self._cc("unreadItemCount"), 0)
        self.total_items_count = folder.get(self._cc("totalItemCount"), 0)
        self.updated_at = dt.datetime.now()

        return True

    def delete(self):
        """Deletes this folder

        :return: Deleted or Not
        :rtype: bool
        """

        if self.root or not self.folder_id:
            return False

        url = self.build_url(
            self._endpoints.get("get_folder").format(id=self.folder_id)
        )

        response = self.con.delete(url)
        if not response:
            return False

        self.folder_id = None
        return True

    def copy_folder(self, to_folder):
        """Copy this folder and it's contents to into another folder

        :param to_folder: the destination Folder/folder_id to copy into
        :type to_folder: mailbox.Folder or str
        :return: The new folder after copying
        :rtype: mailbox.Folder or None
        """
        to_folder_id = (
            to_folder.folder_id if isinstance(to_folder, Folder) else to_folder
        )

        if self.root or not self.folder_id or not to_folder_id:
            return None

        url = self.build_url(
            self._endpoints.get("copy_folder").format(id=self.folder_id)
        )

        response = self.con.post(url, data={self._cc("destinationId"): to_folder_id})
        if not response:
            return None

        folder = response.json()

        self_class = getattr(self, "folder_constructor", type(self))
        # Everything received from cloud must be passed as self._cloud_data_key
        return self_class(
            con=self.con,
            main_resource=self.main_resource,
            **{self._cloud_data_key: folder},
        )

    def move_folder(self, to_folder, *, update_parent_if_changed=True):
        """Move this folder to another folder

        :param to_folder: the destination Folder/folder_id to move into
        :type to_folder: mailbox.Folder or str
        :param bool update_parent_if_changed: updates self.parent with the
         new parent Folder if changed
        :return: The new folder after copying
        :rtype: mailbox.Folder or None
        """
        to_folder_id = (
            to_folder.folder_id if isinstance(to_folder, Folder) else to_folder
        )

        if self.root or not self.folder_id or not to_folder_id:
            return False

        url = self.build_url(
            self._endpoints.get("move_folder").format(id=self.folder_id)
        )

        response = self.con.post(url, data={self._cc("destinationId"): to_folder_id})
        if not response:
            return False

        folder = response.json()

        parent_id = folder.get(self._cc("parentFolderId"), None)

        if parent_id and self.parent_id:
            if parent_id != self.parent_id:
                self.parent_id = parent_id
                self.parent = (
                    self.get_parent_folder() if update_parent_if_changed else None
                )

        return True

    def new_message(self):
        """Creates a new draft message under this folder

        :return: new Message
        :rtype: Message
        """

        draft_message = self.message_constructor(parent=self, is_draft=True)

        if self.root:
            draft_message.folder_id = OutlookWellKnowFolderNames.DRAFTS.value
        else:
            draft_message.folder_id = self.folder_id

        return draft_message

    def delete_message(self, message):
        """Deletes a stored message

        :param message: message/message_id to delete
        :type message: Message or str
        :return: Success / Failure
        :rtype: bool
        """

        message_id = message.object_id if isinstance(message, Message) else message

        if message_id is None:
            raise RuntimeError("Provide a valid Message or a message id")

        url = self.build_url(self._endpoints.get("message").format(id=message_id))

        response = self.con.delete(url)

        return bool(response)


class MailBox(Folder):
    folder_constructor = Folder
    mailbox_settings_constructor = MailboxSettings

    def __init__(self, *, parent=None, con=None, **kwargs):
        super().__init__(parent=parent, con=con, root=True, **kwargs)
        self._endpoints["settings"] = "/mailboxSettings"

    def set_automatic_reply(
        self,
        internal_text: str,
        external_text: str,
        scheduled_start_date_time: dt.datetime = None,
        scheduled_end_date_time: dt.datetime = None,
        externalAudience: ExternalAudience = ExternalAudience.ALL,
    ):
        """Set an automatic reply for the mailbox.

        :return: Success / Failure
        :rtype: bool
        """
        mailboxsettings = self.get_settings()
        ars = mailboxsettings.automaticrepliessettings

        ars.external_audience = externalAudience
        ars.status = AutoReplyStatus.ALWAYSENABLED
        if scheduled_start_date_time or scheduled_end_date_time:
            ars.status = AutoReplyStatus.SCHEDULED
            ars.scheduled_startdatetime = scheduled_start_date_time
            ars.scheduled_enddatetime = scheduled_end_date_time
        ars.internal_reply_message = internal_text
        ars.external_reply_message = external_text

        return mailboxsettings.save()

    def _validate_datetime(self, value, erroritem):
        if not isinstance(value, dt.date):
            raise ValueError(f"'{erroritem} date' must be a valid datetime object")
        if not isinstance(value, dt.datetime):
            # force datetime
            value = dt.datetime(value.year, value.month, value.day)
        if value.tzinfo is None:
            # localize datetime
            value = self.protocol.timezone.localize(value)
        elif value.tzinfo != self.protocol.timezone:
            value = value.astimezone(self.protocol.timezone)
        return value

    def set_disable_reply(self):
        """Disable the automatic reply for the mailbox.

        :return: Success / Failure
        :rtype: bool
        """

        mailboxsettings = self.get_settings()
        ars = mailboxsettings.automaticrepliessettings

        ars.status = AutoReplyStatus.DISABLED
        return mailboxsettings.save()

    def inbox_folder(self):
        """Shortcut to get Inbox Folder instance

        :rtype: mailbox.Folder
        """
        return self.folder_constructor(
            parent=self, name="Inbox", folder_id=OutlookWellKnowFolderNames.INBOX.value
        )

    def junk_folder(self):
        """Shortcut to get Junk Folder instance

        :rtype: mailbox.Folder
        """
        return self.folder_constructor(
            parent=self, name="Junk", folder_id=OutlookWellKnowFolderNames.JUNK.value
        )

    def deleted_folder(self):
        """Shortcut to get DeletedItems Folder instance

        :rtype: mailbox.Folder
        """
        return self.folder_constructor(
            parent=self,
            name="DeletedItems",
            folder_id=OutlookWellKnowFolderNames.DELETED.value,
        )

    def drafts_folder(self):
        """Shortcut to get Drafts Folder instance

        :rtype: mailbox.Folder
        """
        return self.folder_constructor(
            parent=self,
            name="Drafts",
            folder_id=OutlookWellKnowFolderNames.DRAFTS.value,
        )

    def sent_folder(self):
        """Shortcut to get SentItems Folder instance

        :rtype: mailbox.Folder
        """
        return self.folder_constructor(
            parent=self,
            name="SentItems",
            folder_id=OutlookWellKnowFolderNames.SENT.value,
        )

    def outbox_folder(self):
        """Shortcut to get Outbox Folder instance

        :rtype: mailbox.Folder
        """
        return self.folder_constructor(
            parent=self,
            name="Outbox",
            folder_id=OutlookWellKnowFolderNames.OUTBOX.value,
        )

    def archive_folder(self):
        """Shortcut to get Archive Folder instance

        :rtype: mailbox.Folder
        """
        return self.folder_constructor(
            parent=self,
            name="Archive",
            folder_id=OutlookWellKnowFolderNames.ARCHIVE.value,
        )

    def get_settings(self):
        """Return the MailboxSettings.

        :rtype: mailboxsettings
        """
        url = self.build_url(self._endpoints.get("settings"))
        params = {}

        response = self.con.get(
            url, params=params, headers={"Prefer": 'outlook.timezone="UTC"'}
        )

        if not response:
            return iter(())

        data = response.json()

        return self.mailbox_settings_constructor(
            parent=self, **{self._cloud_data_key: data}
        )
