"""
GitHub WebHook receiver for AWS Lambda.

Python based AWS lambda function that receives GitHub WebHooks
and publishes them to SNS topics.
"""

import hashlib
import hmac
import json
import os

import boto3
from chalice import BadRequestError, Chalice, UnauthorizedError

DEFAULT_HASHLIB_BLACKLIST = "CRC32,CRC32C,MD4,MD5,MDC2"


def parse_hashlib_blacklist(envstr):
    """Parse a comma separated string list of strings to a python set of strings."""
    return set(map(lambda s: s.strip().lower(), envstr.split(",")))


CONFIG = {
    "DEBUG": os.environ.get("DEBUG", "") in [1, "1", "True", "true"],
    "SECRET": os.environ.get("SECRET"),
    "S3_REGION": os.environ.get("S3_REGION", "eu-west-1"),
    "HASHLIB_BLACKLIST": parse_hashlib_blacklist(
        os.getenv("HASHLIB_BLACKLIST", DEFAULT_HASHLIB_BLACKLIST)
    ),
}

app = Chalice(app_name="github-webhooks")
app.debug = CONFIG["DEBUG"]

SNS = boto3.client("sns", region_name=CONFIG["S3_REGION"])


def validate_signature(request):
    """Validate that the signature in the header matches the payload."""
    if CONFIG["SECRET"] is None:
        return
    try:
        signature = request.headers["X-Hub-Signature"]
        hashname, hashval = signature.split("=")
    except (KeyError, ValueError):
        raise BadRequestError()

    if (hashname in CONFIG["HASHLIB_BLACKLIST"]) or (
        hashname not in hashlib.algorithms_available
    ):
        raise BadRequestError("X-Hub-Signature hash algorithm unavailable")

    digest = hmac.new(
        CONFIG["SECRET"].encode(), request.raw_body.encode(), hashname
    ).hexdigest()
    if not hmac.compare_digest(digest.encode(), hashval.encode("utf-8")):
        raise UnauthorizedError("X-Hub-Signature mismatch")


@app.route("/{integration}", methods=["POST"])
def index(integration):
    """Consume GitHub webhook and publish hooks to AWS SNS."""
    request = app.current_request
    validate_signature(request)

    try:
        event = request.headers["X-GitHub-Event"]
    except KeyError:
        raise BadRequestError()

    sns_topics = SNS.list_topics()["Topics"]
    topic_arns = {t["TopicArn"].rsplit(":")[-1]: t["TopicArn"] for t in sns_topics}
    topic = f"{integration}_{event}"
    if topic not in topic_arns.keys():
        topic_arns[topic] = SNS.create_topic(Name=topic)["TopicArn"]

    SNS.publish(
        TargetArn=topic_arns[topic],
        Subject=event,
        Message=json.dumps({"default": json.dumps(request.json_body)}),
        MessageStructure="json",
    )

    return {"Code": "Ok", "Message": "Webhook received."}