"""
Google OpenID and OAuth support

OAuth works straightforward using anonymous configurations, username
is generated by requesting email to the not documented, googleapis.com
service. Registered applications can define settings GOOGLE_CONSUMER_KEY
and GOOGLE_CONSUMER_SECRET and they will be used in the auth process.
Setting GOOGLE_OAUTH_EXTRA_SCOPE can be used to access different user
related data, like calendar, contacts, docs, etc.

OpenID also works straightforward, it doesn't need further configurations.
"""
from urllib2 import Request, urlopen

from django.conf import settings
from django.utils import simplejson

from social_auth.backends import OpenIdAuth, ConsumerBasedOAuth, \
                                 OAuthBackend, OpenIDBackend, USERNAME


# Google OAuth base configuration
GOOGLE_SERVER = 'www.google.com'
GOOGLE_REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken'
GOOGLE_ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken'
GOOGLE_AUTHORIZATION_URL = 'https://www.google.com/accounts/OAuthAuthorizeToken'
# scope for user email, specify extra scopes in settings, for example:
# GOOGLE_OAUTH_EXTRA_SCOPE = ['https://www.google.com/m8/feeds/']
GOOGLE_OAUTH_SCOPE = ['https://www.googleapis.com/auth/userinfo#email']
GOOGLEAPIS_EMAIL = 'https://www.googleapis.com/userinfo/email'
GOOGLE_OPENID_URL = 'https://www.google.com/accounts/o8/id'


# Backends
class GoogleOAuthBackend(OAuthBackend):
    """Google OAuth authentication backend"""
    name = 'google-oauth'

    def get_user_id(self, details, response):
        "Use google email as unique id"""
        return details['email']

    def get_user_details(self, response):
        """Return user details from Orkut account"""
        email = response['email']
        return {USERNAME: email.split('@', 1)[0],
                'email': email,
                'fullname': '',
                'first_name': '',
                'last_name': ''}


class GoogleBackend(OpenIDBackend):
    """Google OpenID authentication backend"""
    name = 'google'


# Auth classes
class GoogleAuth(OpenIdAuth):
    """Google OpenID authentication"""
    AUTH_BACKEND = GoogleBackend

    def openid_url(self):
        """Return Google OpenID service url"""
        return GOOGLE_OPENID_URL


class BaseGoogleOAuth(ConsumerBasedOAuth):
    """Base class for Google OAuth mechanism"""
    AUTHORIZATION_URL = GOOGLE_AUTHORIZATION_URL
    REQUEST_TOKEN_URL = GOOGLE_REQUEST_TOKEN_URL
    ACCESS_TOKEN_URL = GOOGLE_ACCESS_TOKEN_URL
    SERVER_URL = GOOGLE_SERVER
    AUTH_BACKEND = None

    def user_data(self, access_token):
        """Loads user data from G service"""
        raise NotImplementedError('Implement in subclass')


class GoogleOAuth(BaseGoogleOAuth):
    """Google OAuth authorization mechanism"""
    AUTH_BACKEND = GoogleOAuthBackend
    SETTINGS_KEY_NAME = 'GOOGLE_CONSUMER_KEY'
    SETTINGS_SECRET_NAME = 'GOOGLE_CONSUMER_SECRET'

    def user_data(self, access_token):
        """Loads user data data from googleapis service, only email so far
        as it's described in:
            http://sites.google.com/site/oauthgoog/Home/emaildisplayscope
        OAuth parameters needs to be passed in the queryset and
        Authorization header, this behavior is listed in:
            http://groups.google.com/group/oauth/browse_thread/thread/d15add9beb418ebc
        """
        url = self.oauth_request(access_token, GOOGLEAPIS_EMAIL,
                                 {'alt': 'json'}).to_url()
        params = url.split('?', 1)[1]
        request = Request(url)
        request.headers['Authorization'] = params  # setup header
        response = urlopen(request).read()
        try:
            return simplejson.loads(response)['data']
        except (simplejson.JSONDecodeError, KeyError):
            return None

    def oauth_request(self, token, url, extra_params=None):
        extra_params = extra_params or {}
        scope = GOOGLE_OAUTH_SCOPE + \
                getattr(settings, 'GOOGLE_OAUTH_EXTRA_SCOPE', [])
        extra_params.update({
            'scope': ' '.join(scope),
            'xoauth_displayname': getattr(settings, 'GOOGLE_DISPLAY_NAME',
                                          'Social Auth')
        })
        return super(GoogleOAuth, self).oauth_request(token, url, extra_params)

    def get_key_and_secret(self):
        """Return Google OAuth Consumer Key and Consumer Secret pair, uses
        anonymous by default, beware that this marks the application as not
        registered and a security badge is displayed on authorization page.
        http://code.google.com/apis/accounts/docs/OAuth_ref.html#SigningOAuth
        """
        try:
            return super(GoogleOAuth, self).get_key_and_secret()
        except AttributeError:
            return 'anonymous', 'anonymous'


# Backend definition
BACKENDS = {
    'google': GoogleAuth,
    'google-oauth': GoogleOAuth,
}
