#!/usr/bin/env python
""" Script to award the flock paparazzi badge.

We scrape g+ and flickr for photos tagged Flock and Fedora,
with the people we find who posted those, we try our best to match them with
a FAS username.  If we can, then we award them the badge.

Author: Ralph Bean <rbean@redhat.com>
"""

from __future__ import print_function

import __main__
__main__.__requires__ = __requires__ = ["tahrir-api", "sqlalchemy>=0.7"]
import pkg_resources
pkg_resources.require(__requires__)

import time
import os
import ConfigParser

import requests
import transaction
import tahrir_api.dbapi
from gssapi import Credentials
from gssapi.exceptions import GSSError
from requests_gssapi import HTTPSPNEGOAuth

import fedmsg
import fedmsg.config

fm_config = fedmsg.config.load_config()
fm_config['cert_prefix'] = 'fedbadges'
fm_config['name'] = 'relay_inbound'
fm_config['active'] = True
fedmsg.init(**fm_config)

import fedbadges.utils


# Get config secrets from a file
config = ConfigParser.ConfigParser()
config.read(['flock-paparazzi.ini', '/etc/flock-paparazzi.ini'])
flickr_api_key = config.get('general', 'flickr_api_key')
g_plus_key = config.get('general', 'g_plus_key')
userIP = config.get('general', 'userIP')

# API urls
flickr_url = 'https://api.flickr.com/services/rest/'
g_plus_url = 'https://www.googleapis.com/plus/v1/activities'

badge_id = 'flock-paparazzi'

http_client = None


def get_http_client():
    os.environ["KRB5_CLIENT_KTNAME"] = fm_config.get("keytab")
    try:
        creds = Credentials(usage="initiate")
    except GSSError as e:
        print("GSSError trying to authenticate with Kerberos", e)
    gssapi_auth = HTTPSPNEGOAuth(opportunistic_auth=True, creds=creds)
    session = requests.Session()
    session.auth = gssapi_auth
    return session


def get_g_plus_persons(query):
    token = None
    while True:
        params = dict(query=query, key=g_plus_key, userIP=userIP)

        if token:
            params['pageToken'] = token

        response = requests.get(g_plus_url, params=params)
        body = response.json()
        token = body.get('nextPageToken', None)

        # No more results
        if not body.get('items', None):
            break

        # Otherwise, we have a page to process
        for item in body['items']:
            for attach in item['object'].get('attachments', []):
                if attach['objectType'] == 'album':
                    yield item['actor']['displayName']

        time.sleep(0.5)  # So as to not get rate-limit banned.


def flickr_request(**kwargs):
    response = requests.get(flickr_url, params=dict(
        api_key=flickr_api_key,
        format='json',
        nojsoncallback=1,
        **kwargs))
    return response.json()


def get_flickr_page(tags, page=1):
    return flickr_request(
        method='flickr.photos.search',
        content_type=1,
        tags=tags,
        tag_mode='all',
        page=page,
    )


def get_flickr_persons(tags):
    pages = get_flickr_page(tags)['photos']['pages']

    seen = {}
    for i in range(1, pages + 1):
        d = get_flickr_page(tags, i)

        for photo in d['photos']['photo']:
            user_id = photo['owner']
            if user_id in seen:
                continue

            seen[user_id] = {}

            # https://secure.flickr.com/services/api/flickr.people.getInfo.html
            user = flickr_request(
                method='flickr.people.getInfo',
                user_id=user_id,
            )
            seen[user_id]['username1'] = user['person']['username']['_content']
            seen[user_id]['username2'] = user['person']['path_alias']
            if 'realname' in user['person']:
                seen[user_id]['realname1'] = \
                    user['person']['realname']['_content']

                if not seen[user_id]['realname1']:
                    continue

                try:
                    seen[user_id]['realname2'] = ' '.join([
                        seen[user_id]['realname1'].split()[0],
                        seen[user_id]['realname1'].split()[-1],
                    ])
                except Exception:
                    import traceback
                    traceback.print_exc()

                if "'" in seen[user_id]['realname1']:
                    seen[user_id]['username3'] = \
                        seen[user_id]['realname1'].split("'")[1]
                if '"' in seen[user_id]['realname1']:
                    seen[user_id]['username4'] = \
                        seen[user_id]['realname1'].split('"')[1]

    for user_id, d in seen.items():
        for key, value in d.items():
            yield value


def get_username(name):
    # First, check if the same username exists:
    response = http_client.get(
        "%susers/%s/" % (fm_config['fasjson_base_url'], name)
    )
    if response.ok:
        return name
    # Now try with the human name
    response = http_client.get(
        "%ssearch/users/" % fm_config['fasjson_base_url'],
        params={"human_name": name}
    )
    if not response.ok:
        return None
    response = response.json()
    if response["page"]["total_results"] != 1:
        return None
    return response["result"][0]["username"]


def get_persons():
    for person in get_g_plus_persons('Fedora FLOCK'):
        yield person
    for person in get_g_plus_persons('flocktofedora'):
        yield person
    for person in get_flickr_persons('fedora,flock'):
        yield person
    for person in get_flickr_persons('flocktofedora'):
        yield person


def main():
    global http_client
    # First, initialize the tahrir db connection
    uri = fm_config['badges_global']['database_uri']
    tahrir = tahrir_api.dbapi.TahrirDatabase(
        uri,
        notification_callback=fedbadges.utils.notification_callback,
    )

    http_client = get_http_client()

    badge = tahrir.get_badge(badge_id)
    already_has_it = [a.person.nickname for a in badge.assertions]

    # Finally, query the two services and award as we can.
    for person in get_persons():

        # Try to gracefully handle non-ascii names if we can
        try:
            person = person.encode('utf-8')
        except Exception:
            import traceback
            traceback.print_exc()
            continue

        print("* Considering", person)
        username = get_username(person)
        if username is not None:
            if username in already_has_it:
                print("Skipping %r" % username)
                continue

            print("  *", username, "gets the badge")
            already_has_it.append(username)
            email = username + "@fedoraproject.org"
            try:
                transaction.begin()
                tahrir.add_assertion(badge_id, email, None)
                transaction.commit()
                time.sleep(1)
            except Exception as e:
                transaction.abort()
                print("Failure:", e)


if __name__ == '__main__':
    main()
