#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (C) 2010 Canonical
#
# Authors:
#  Michael Vogt
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 3.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


from __future__ import absolute_import, print_function

from gi.repository import GObject, GLib
from gettext import gettext as _

import logging
import os

import piston_mini_client.auth
import piston_mini_client.failhandlers


import softwarecenter.paths

# mostly for testing
from .fake_review_settings import FakeReviewSettings, network_delay
from .spawn_helper import SpawnHelper
from softwarecenter.config import get_config
from softwarecenter.backend.login import get_login_backend

from softwarecenter.backend.piston.ubuntusso_pristine import (
    UbuntuSsoAPI as PristineUbuntuSsoAPI,
)
# patch default_service_root to the one we use
from softwarecenter.enums import UBUNTU_SSO_SERVICE
# *Don't* append /api/1.0, as it's already included in UBUNTU_SSO_SERVICE
PristineUbuntuSsoAPI.default_service_root = UBUNTU_SSO_SERVICE

from softwarecenter.enums import (
    SOFTWARE_CENTER_NAME_KEYRING,
    SOFTWARE_CENTER_SSO_DESCRIPTION,
)
from softwarecenter.utils import clear_token_from_ubuntu_sso_sync

LOG = logging.getLogger(__name__)


class UbuntuSSO(GObject.GObject):
    """ Ubuntu SSO interface using the oauth token from the keyring

    The methods that work synchronously are suffixed with _sync()
    """

    __gsignals__ = {
        "whoami": (GObject.SIGNAL_RUN_LAST,
                   GObject.TYPE_NONE,
                   (GObject.TYPE_PYOBJECT,),
                   ),
        "error": (GObject.SIGNAL_RUN_LAST,
                  GObject.TYPE_NONE,
                  (GObject.TYPE_PYOBJECT,),
                  ),
    }

    def __init__(self, xid=0):
        GObject.GObject.__init__(self)
        self.oauth = None
        self.xid = xid
        self.loop = GLib.MainLoop(GLib.main_context_default())

    def _on_whoami_data(self, spawner, piston_whoami):
        # once we have data, make sure to save it
        config = get_config()
        config.email = piston_whoami["preferred_email"]
        config.reviews_username = piston_whoami["username"]
        # emit
        self.emit("whoami", piston_whoami)

    def whoami(self):
        """ trigger request for getting the account information, this
            will also verify if the current token is valid and if not,
            trigger a cleanup/re-authenticate
        """
        LOG.debug("whoami called")
        spawner = SpawnHelper()
        spawner.connect("data-available", self._on_whoami_data)
        spawner.connect("error", lambda spawner, err: self.emit("error", err))
        spawner.needs_auth = True
        spawner.run_generic_piston_helper("UbuntuSsoAPI", "whoami")

    def _login_successful(self, sso_backend, oauth_result):
        LOG.debug("_login_successful")
        self.oauth = oauth_result
        self.loop.quit()

    # sync calls
    def verify_token_sync(self, token):
        """ Verify that the token is valid

            Note that this may raise httplib2 exceptions if the server
            is not reachable
        """
        LOG.debug("verify_token")
        auth = piston_mini_client.auth.OAuthAuthorizer(
            token["token"], token["token_secret"],
            token["consumer_key"], token["consumer_secret"])
        api = PristineUbuntuSsoAPI(auth=auth)
        try:
            res = api.whoami()
        except piston_mini_client.failhandlers.APIError as e:
            LOG.exception("api.whoami failed with APIError: '%s'" % e)
            return False
        return len(res) > 0

    def clear_token(self):
        clear_token_from_ubuntu_sso_sync(SOFTWARE_CENTER_NAME_KEYRING)

    def _get_login_backend_and_connect(self):
        sso = get_login_backend(
            self.xid,
            SOFTWARE_CENTER_NAME_KEYRING,
            _(SOFTWARE_CENTER_SSO_DESCRIPTION))
        sso.connect("login-successful", self._login_successful)
        sso.connect("login-failed", lambda s: self.loop.quit())
        sso.connect("login-canceled", lambda s: self.loop.quit())
        return sso

    def find_oauth_token_sync(self):
        self.oauth = None
        sso = self. _get_login_backend_and_connect()
        sso.find_credentials()
        self.loop.run()
        return self.oauth

    def get_oauth_token_sync(self):
        self.oauth = None
        sso = self. _get_login_backend_and_connect()
        sso.login_or_register()
        self.loop.run()
        return self.oauth

    def get_oauth_token_and_verify_sync(self, no_relogin=False):
        token = self.get_oauth_token_sync()
        # check if the token is valid and reset it if not
        if token:
            # verify token will return false if there is a API error,
            # but there may be httplib2 errors if there is no network,
            # so ignore them
            try:
                if not self.verify_token_sync(token):
                    attempt_relogin = not no_relogin
                    if attempt_relogin:
                        self.clear_token()
                        # re-trigger login once
                        token = self.get_oauth_token_sync()
                    else:
                        return None
            except Exception as e:
                LOG.warn(
                    "token could not be verified (network problem?): %s" % e)
        return token


class UbuntuSSOAPIFake(UbuntuSSO):

    def __init__(self):
        UbuntuSSO.__init__(self)
        self._fake_settings = FakeReviewSettings()

    @network_delay
    def whoami(self):
        if self._fake_settings.get_setting('whoami_response') == "whoami":
            self.emit("whoami", self._create_whoami_response())
        elif self._fake_settings.get_setting('whoami_response') == "error":
            self.emit("error", self._make_error())

    def _create_whoami_response(self):
        username = (self._fake_settings.get_setting('whoami_username') or
            "anyuser")
        response = {
                    u'username': username.decode('utf-8'),
                    u'preferred_email': u'user@email.com',
                    u'displayname': u'Fake User',
                    u'unverified_emails': [],
                    u'verified_emails': [],
                    u'openid_identifier': u'fnerkWt'
        }
        return response

    def _make_error():
        return 'HTTP Error 401: Unauthorized'


def get_ubuntu_sso_backend():
    """
    factory that returns an ubuntu sso loader singleton
    """
    if "SOFTWARE_CENTER_FAKE_REVIEW_API" in os.environ:
        ubuntu_sso_class = UbuntuSSOAPIFake()
        LOG.warn('Using fake Ubuntu SSO API. Only meant for testing purposes')
    else:
        ubuntu_sso_class = UbuntuSSO()
    return ubuntu_sso_class


# test code
def _login_success(lp, token):
    print("success", lp, token)


def _login_failed(lp):
    print("fail", lp)


def _login_need_user_and_password(sso):
    import sys
    sys.stdout.write("user: ")
    sys.stdout.flush()
    user = sys.stdin.readline().strip()
    sys.stdout.write("pass: ")
    sys.stdout.flush()
    password = sys.stdin.readline().strip()
    sso.login(user, password)


# interactive test code
if __name__ == "__main__":
    def _whoami(sso, result):
        print("res: ", result)
        Gtk.main_quit()

    def _error(sso, result):
        print("err: ", result)
        Gtk.main_quit()

    def _dbus_maybe_login_successful(ssologin, oauth_result):
        print("got token, verify it now")
        sso = UbuntuSSO()
        sso.connect("whoami", _whoami)
        sso.connect("error", _error)
        sso.whoami()

    from gi.repository import Gtk
    logging.basicConfig(level=logging.DEBUG)
    softwarecenter.paths.datadir = "./data"

    backend = get_login_backend("", "appname", "help_text")
    backend.connect("login-successful", _dbus_maybe_login_successful)
    backend.login_or_register()
    Gtk.main()
