"""Lambda handler triggered from events Kinesis stream.

Adds requests to ingest updates to blueprints in GitHub.
"""

import collections
import itertools
import json
import logging
import re
import sys
import urlparse

# Work around problem with lambda and code.justin.tv namespace.
if 'code' in sys.modules:
    del sys.modules['code']
from code.justin.tv.dta.rockpaperscissors import environment
from code.justin.tv.dta.rockpaperscissors import event_bus
from code.justin.tv.dta.rockpaperscissors import errors
from code.justin.tv.dta.rockpaperscissors import ingest_queue


logger = logging.getLogger()
logger.setLevel(logging.INFO)
logging.getLogger('boto3').setLevel(logging.WARNING)
logging.getLogger('botocore').setLevel(logging.WARNING)


def lambda_handler(event, context):
    env = environment.GetEnvironment(context)
    queue = env.IngestQueue()

    blueprints = ProcessRecords(event['Records'])
    requests = MakeRequests(blueprints)
    try:
        queue.AddRequests(requests)
    except errors.IngestQueueAddRequestsFailures as e:
        pass


def ProcessRecord(record):
    """Returns generator of (repository, path) tuples."""
    try:
        event = event_bus.DecodeKinesisRecordData(record['kinesis']['data'])
    except (errors.EventDecodeError, errors.EventInvalid) as e:
        logger.error(e)
        return

    if event.type != 'GitHub-push':
        return

    github_event = json.loads(event.body)

    # We only care about changes on the "default branch".
    default_branch_ref = (
      'refs/heads/%s' % github_event['repository']['default_branch'])
    if github_event['ref'] != default_branch_ref:
        return

    repo_host = urlparse.urlsplit(github_event['repository']['url']).netloc
    repo_name = github_event['repository']['full_name']
    for commit in github_event['commits']:
        # Was a .blueprint file added or modified in this commit?
        for path in itertools.chain(commit['added'], commit['modified']):
            if re.match(r'[^/]+\.blueprint$', path):
                yield ((repo_host, repo_name), path)


def ProcessRecords(records):
    """Returns dict, keys are repository names, values are sets of paths."""
    logger.info('Processing %d records', len(records))
    blueprints = collections.defaultdict(set)
    for record in records:
        for repository, path in ProcessRecord(record):
            blueprints[repository].add(path)
    return blueprints


def MakeRequests(blueprints):
    """Takes dict of repository/paths and returns generator of protos."""
    for repository, paths in blueprints.iteritems():
        repo_host, repo_name = repository
        for path in paths:
            logger.info('Detected change to %s/%s', repo_name, path)
            yield ingest_queue.IngestBlueprintRequest(
              github_host=repo_host,
              github_repository=repo_name,
              blueprint_path=path)
