"""Python API for user profiles.

Profile information includes a student's demographic information and preferences,
but does NOT include basic account information such as username, password, and
email address.

"""
from user_api.models import UserProfile
from user_api.helpers import intercept_errors


class ProfileRequestError(Exception):
    """ The request to the API was not valid. """
    pass


class ProfileUserNotFound(ProfileRequestError):
    """ The requested user does not exist. """
    pass


class ProfileInvalidField(ProfileRequestError):
    """ The proposed value for a field is not in a valid format. """

    def __init__(self, field, value):
        self.field = field
        self.value = value

    def __str__(self):
        return u"Invalid value '{value}' for profile field '{field}'".format(
            value=self.value,
            field=self.field
        )


class ProfileInternalError(Exception):
    """ An error occurred in an API call. """
    pass


FULL_NAME_MAX_LENGTH = 255


@intercept_errors(ProfileInternalError, ignore_errors=[ProfileRequestError])
def profile_info(username):
    """Retrieve a user's profile information

    Searches either by username or email.

    At least one of the keyword args must be provided.

    Arguments:
        username (unicode): The username of the account to retrieve.

    Returns:
        dict: If profile information was found.
        None: If the provided username did not match any profiles.

    """
    try:
        profile = UserProfile.objects.get(user__username=username)
    except UserProfile.DoesNotExist:
        return None

    profile_dict = {
        u'username': profile.user.username,
        u'email': profile.user.email,
        u'full_name': profile.name,
    }

    return profile_dict


@intercept_errors(ProfileInternalError, ignore_errors=[ProfileRequestError])
def update_profile(username, full_name=None):
    """Update a user's profile.

    Args:
        username (unicode): The username associated with the account.

    Keyword Arguments:
        full_name (unicode): If provided, set the user's full name to this value.

    Returns:
        None

    Raises:
        ProfileRequestError: If there is no profile matching the provided username.

    """
    try:
        profile = UserProfile.objects.get(user__username=username)
    except UserProfile.DoesNotExist:
        raise ProfileUserNotFound

    if full_name is not None:
        name_length = len(full_name)
        if name_length > FULL_NAME_MAX_LENGTH or name_length == 0:
            raise ProfileInvalidField("full_name", full_name)
        else:
            profile.update_name(full_name)


@intercept_errors(ProfileInternalError, ignore_errors=[ProfileRequestError])
def preference_info(username, preference_name):
    """Retrieve information about a user's preferences.

    Arguments:
        username (unicode): The username of the account to retrieve.
        preference_name (unicode): The name of the preference to retrieve.

    Returns:
        The JSON-deserialized value.

    """
    pass


@intercept_errors(ProfileInternalError, ignore_errors=[ProfileRequestError])
def update_preference(username, preference_name, preference_value):
    """Update a user's preference.

    Arguments:
        username (unicode): The username of the account to retrieve.
        preference_name (unicode): The name of the preference to set.
        preference_value (JSON-serializable): The new value for the preference.

    Returns:
        None

    """
    pass
