# -*- coding: utf-8 -*-
from flask import abort, url_for, flash, redirect, g, session, render_template, request

from coaster.utils import valid_username
from coaster.views import get_next_url
from baseframe import _, csrf
from baseframe.signals import exception_catchall
from lastuser_core import login_registry
from lastuser_core.models import db, getextid, merge_users, User, UserEmail, UserExternalId, UserEmailClaim
from lastuser_core.registry import LoginInitError, LoginCallbackError
from lastuser_core.signals import user_data_changed
from .. import lastuser_oauth
from ..forms.profile import ProfileMergeForm
from ..mailclient import send_email_verify_link
from ..views.helpers import login_internal, register_internal, set_loginmethod_cookie, requires_login


@csrf.exempt
@lastuser_oauth.route('/login/<service>', methods=['GET', 'POST'])
def login_service(service):
    """
    Handle login with a registered service.
    """
    if service not in login_registry:
        abort(404)
    provider = login_registry[service]
    next_url = get_next_url(referrer=False, default=None)
    callback_url = url_for('.login_service_callback', service=service, next=next_url, _external=True)
    try:
        return provider.do(callback_url=callback_url)
    except (LoginInitError, LoginCallbackError) as e:
        msg = _(u"{service} login failed: {error}").format(service=provider.title, error=unicode(e))
        exception_catchall.send(e, message=msg)
        flash(msg, category='danger')
        return redirect(next_url or get_next_url(referrer=True))


@csrf.exempt
@lastuser_oauth.route('/login/<service>/callback', methods=['GET', 'POST'])
def login_service_callback(service):
    """
    Callback handler for a login service.
    """
    if service not in login_registry:
        abort(404)
    provider = login_registry[service]
    try:
        userdata = provider.callback()
    except (LoginInitError, LoginCallbackError) as e:
        msg = _(u"{service} login failed: {error}").format(service=provider.title, error=unicode(e))
        exception_catchall.send(e, message=msg)
        flash(msg, category='danger')
        if g.user:
            return redirect(get_next_url(referrer=False))
        else:
            return redirect(url_for('.login'))
    return login_service_postcallback(service, userdata)


def get_user_extid(service, userdata):
    """
    Retrieves a 'user', 'extid' and 'useremail' from the given service and userdata.
    """
    provider = login_registry[service]
    extid = getextid(service=service, userid=userdata['userid'])

    useremail = None
    if userdata.get('email'):
        useremail = UserEmail.get(email=userdata['email'])

    user = None
    if extid is not None:
        user = extid.user
    elif useremail is not None and useremail.user is not None:
        user = useremail.user
    else:
        # Cross-check with all other instances of the same LoginProvider (if we don't have a user)
        # This is (for eg) for when we have two Twitter services with different access levels.
        for other_service, other_provider in login_registry.items():
            if other_service != service and other_provider.__class__ == provider.__class__:
                other_extid = getextid(service=other_service, userid=userdata['userid'])
                if other_extid is not None:
                    user = other_extid.user
                    break

    # TODO: Make this work when we have multiple confirmed email addresses available
    return user, extid, useremail


def login_service_postcallback(service, userdata):
    user, extid, useremail = get_user_extid(service, userdata)

    if extid is not None:
        extid.oauth_token = userdata.get('oauth_token')
        extid.oauth_token_secret = userdata.get('oauth_token_secret')
        extid.oauth_token_type = userdata.get('oauth_token_type')
        extid.username = userdata.get('username')
        # TODO: Save refresh token and expiry date where present
        extid.oauth_refresh_token = userdata.get('oauth_refresh_token')
        extid.oauth_expiry_date = userdata.get('oauth_expiry_date')
        extid.oauth_refresh_expiry = userdata.get('oauth_refresh_expiry')  # TODO: Check this
    else:
        # New external id. Register it.
        extid = UserExternalId(
            user=user,  # This may be None right now. Will be handled below
            service=service,
            userid=userdata['userid'],
            username=userdata.get('username'),
            oauth_token=userdata.get('oauth_token'),
            oauth_token_secret=userdata.get('oauth_token_secret'),
            oauth_token_type=userdata.get('oauth_token_type')
            # TODO: Save refresh token
            )
        db.session.add(extid)