"""
email_utils.py

Contains functionality for sending emails.
"""
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr

from .remedy_utils import get_ip

from flask import current_app, render_template, url_for
from flask.ext.login import current_user


def assert_defined(name, val):
    """
    Checks to see that the provided string value is defined
    (i.e. not None, an empty string, or whitespace),
    and raises an error if it does not.

    Args:
        name: The name of the value, which will be
            included in the error message.
        val: The string value to check.
    """
    if val is None or \
            not isinstance(val, basestring) or \
            len(val) == 0 or \
            val.isspace():
        raise RuntimeError(name + ' is not configured.')


def send_email(toaddr, subject, message_text, message_html):
    """
    Sends an email.

    Args:
        toaddr: The recipient of the email.
        subject: The subject line to include.
        message_text: The text version of the email.
        message_html: The HTML version of the email.
    """
    # Get our config options and throw an error
    # if we don't have any of them configured.
    username = current_app.config.get('EMAIL_USERNAME')
    password = current_app.config.get('EMAIL_PASSWORD')
    fromaddr = current_app.config.get('EMAIL_ADDRESS')
    server = current_app.config.get('EMAIL_SERVER')

    assert_defined('EMAIL_USERNAME', username)
    assert_defined('EMAIL_PASSWORD', password)
    assert_defined('EMAIL_ADDRESS', fromaddr)
    assert_defined('EMAIL_SERVER', server)

    # If we have a display name to include in our From line,
    # add that in.
    displayname = current_app.config.get('EMAIL_DISPLAY_NAME')
    if displayname and not displayname.isspace():
        fromaddr = formataddr((displayname, fromaddr))

    # Create the email container
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = fromaddr
    msg['To'] = toaddr

    # Build the plain-text and HTML versions.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    msg.attach(MIMEText(message_text, 'plain'))
    msg.attach(MIMEText(message_html, 'html'))

    # Send the email.
    server = smtplib.SMTP(server)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    server.sendmail(fromaddr, toaddr, msg.as_string())
    server.quit()


def send_resource_error(resource, comments):
    """
    Notifies administrators of an error in a resource.

    Args:
        resource: The resource in question.
        comments: The comments on the resource.
    """
    # Get our target email address and throw an error
    # if it's not defined.
    toaddr = current_app.config.get('EMAIL_ADDRESS')
    assert_defined('EMAIL_ADDRESS', toaddr)

    # Default the name to a public user, but if the requesting
    # user's authenticated, use that instead.
    from_name = "Public User"
    if current_user.is_authenticated:
        from_name = current_user.username

    # Append the IP
    from_name = from_name + " (" + get_ip() + ")"

    # Get the subject
    subject = "Resource Correction - " + resource.name

    # Get the URL to the resource
    resource_url = current_app.config.get('BASE_URL') + \
        url_for('remedy.resource', resource_id=resource.id)

    # Build the text of the message
    message_text = render_template(
        'email/resource-error.txt',
        subject=subject,
        from_name=from_name,
        resource=resource,
        resource_url=resource_url,
        comments=comments)

    # Now build the HTML version
    message_html = render_template(
        'email/resource-error.html',
        subject=subject,
        from_name=from_name,
        resource=resource,
        resource_url=resource_url,
        comments=comments)

    send_email(toaddr, subject, message_text, message_html)


def send_confirm_account(user):
    """
    Sends an email to the specified user to confirm their account.

    Args:
        user: The user to email.
    """
    # Generate the user's email address
    toaddr = formataddr((user.display_name, user.email))

    # Build the subject
    subject = 'RAD Remedy - Confirm Account'

    # Build the confirmation URL
    confirm_url = current_app.config.get('BASE_URL') + \
        url_for('auth.confirm_account', code=user.email_code)

    # Build the text of the message
    message_text = render_template(
        'email/confirm-account.txt',
        subject=subject,
        user=user,
        confirm_url=confirm_url)

    # Now build the HTML version
    message_html = render_template(
        'email/confirm-account.html',
        subject=subject,
        user=user,
        confirm_url=confirm_url)

    send_email(toaddr, subject, message_text, message_html)


def send_password_reset(user):
    """
    Sends an email to the specified user to reset their password.

    Args:
        user: The user to email.
    """
    # Generate the user's email address
    toaddr = formataddr((user.display_name, user.email))

    # Build the subject
    subject = 'RAD Remedy - Password Reset Request'

    # Build the reset URL
    reset_url = current_app.config.get('BASE_URL') + \
        url_for('auth.reset_password', code=user.email_code)

    # Get the IP of the person requesting the reset
    request_ip = get_ip()

    # Build the text of the message
    message_text = render_template(
        'email/reset-password.txt',
        subject=subject,
        user=user,
        reset_url=reset_url,
        request_ip=request_ip)

    # Now build the HTML version
    message_html = render_template(
        'email/reset-password.html',
        subject=subject,
        user=user,
        reset_url=reset_url,
        request_ip=request_ip)

    send_email(toaddr, subject, message_text, message_html)