"""
    mailthon.headers
    ~~~~~~~~~~~~~~~~

    Implements RFC compliant headers, and is the
    recommended way to put headers into enclosures
    or envelopes.

    :copyright: (c) 2015 by Eeo Jun
    :license: MIT, see LICENSE for details.
"""
import sys
from cgi import parse_header

from email.utils import quote, formatdate, make_msgid, getaddresses
from .helpers import format_addresses, UnicodeDict


IS_PY3 = int(sys.version[0]) == 3


class Headers(UnicodeDict):
    """
    :rfc:`2822` compliant subclass of the
    :class:`~mailthon.helpers.UnicodeDict`. The
    semantics of the dictionary is different from that
    of the standard library MIME object- only the
    latest header is preserved instead of preserving
    all headers. This makes header lookup deterministic
    and sane.
    """

    @property
    def resent(self):
        """
        Whether the email was resent, i.e. whether the
        ``Resent-Date`` header was set.
        """
        return 'Resent-Date' in self

    @property
    def sender(self):
        """
        Returns the sender, respecting the Resent-*
        headers. In any case, prefer Sender over From,
        meaning that if Sender is present then From is
        ignored, as per the RFC.
        """
        to_fetch = (
            ['Resent-Sender', 'Resent-From'] if self.resent else
            ['Sender', 'From']
        )
        for item in to_fetch:
            if item in self:
                _, addr = getaddresses([self[item]])[0]
                return addr

    @property
    def receivers(self):
        """
        Returns a list of receivers, obtained from the
        To, Cc, and Bcc headers, respecting the Resent-*
        headers if the email was resent.
        """
        attrs = (
            ['Resent-To', 'Resent-Cc', 'Resent-Bcc'] if self.resent else
            ['To', 'Cc', 'Bcc']
        )
        addrs = (v for v in (self.get(k) for k in attrs) if v)
        return [addr for _, addr in getaddresses(addrs)]

    def prepare(self, mime):
        """
        Prepares a MIME object by applying the headers
        to the *mime* object. Ignores any Bcc or
        Resent-Bcc headers.
        """
        for key in self:
            if key == 'Bcc' or key == 'Resent-Bcc':
                continue
            del mime[key]
            # Python 3.* email's compatibility layer will handle
            # unicode field values in proper way but Python 2
            # won't (it will encode not only additional field
            # values but also all header values)
            parsed_header, additional_fields = parse_header(
                self[key] if IS_PY3 else
                self[key].encode("utf-8")
            )
            mime.add_header(key, parsed_header, **additional_fields)


def subject(text):
    """
    Generates a Subject header with a given *text*.
    """
    yield 'Subject'
    yield text


def sender(address):
    """
    Generates a Sender header with a given *text*.
    *text* can be both a tuple or a string.
    """
    yield 'Sender'
    yield format_addresses([address])


def to(*addrs):
    """
    Generates a To header with the given *addrs*, where
    addrs can be made of ``Name <address>`` or ``address``
    strings, or a mix of both.
    """
    yield 'To'
    yield format_addresses(addrs)


def cc(*addrs):
    """
    Similar to ``to`` function. Generates a Cc header.
    """
    yield 'Cc'
    yield format_addresses(addrs)


def bcc(*addrs):
    """
    Generates a Bcc header. This is safe when using the
    mailthon Headers implementation because the Bcc
    headers will not be included in the MIME object.
    """
    yield 'Bcc'
    yield format_addresses(addrs)


def content_disposition(disposition, filename):
    """
    Generates a content disposition hedaer given a
    *disposition* and a *filename*. The filename needs
    to be the base name of the path, i.e. instead of
    ``~/file.txt`` you need to pass in ``file.txt``.
    The filename is automatically quoted.
    """
    yield 'Content-Disposition'
    yield '%s; filename="%s"' % (disposition, quote(filename))


def date(time=None):
    """
    Generates a Date header. Yields the *time* as the
    key if specified, else returns an RFC compliant
    date generated by formatdate.
    """
    yield 'Date'
    yield time or formatdate(localtime=True)


def message_id(string=None, idstring=None):
    """
    Generates a Message-ID header, by yielding a
    given *string* if specified, else an RFC
    compliant message-id generated by make_msgid
    and strengthened by an optional *idstring*.
    """
    yield 'Message-ID'
    yield string or make_msgid(idstring)


def content_id(name):
    yield 'Content-ID'
    yield '<%s>' % (name,)