#!/usr/bin/env python3.5
from argparse import ArgumentParser
import json
import sys
import requests
from requests.auth import HTTPBasicAuth

api_ver = "9"


# =============================================================================
# Functions & classes
# =============================================================================

def check_request_result(result):
    if result.status_code != 200:
        text = "Invalid return code {}. Response is:\n{}".format(
            result.status_code,
            result.text
        )
        raise Exception(text)


def last_version(options):
    """Return last version number.
    :return: version number or None
    """
    # Swarm's API requires HTTP Basic Access Authentication for endpoints.
    # The host-unlocked ticket can be used instead of password.
    result = requests.get(
        "{swarm_url}/api/v{api_ver}/reviews/{review_id}".format(
            swarm_url=options.swarm_url,
            api_ver=api_ver,
            review_id=options.review_id),
        auth=HTTPBasicAuth(options.user, options.password)
    )
    check_request_result(result)
    try:
        versions = result.json()["review"]["versions"]
    except (KeyError, ValueError):
        text = "Error parsing Swarm server response.\
         Full response is the following:\n{}".format(r.text)
        raise Exception(text)
    return len(versions)


def post_comment(options, filename=None, line=None,
                 version=None, no_notification=False):
    request = {"body": options.text,
               "topic": "reviews/{}".format(options.review_id)}
    if filename:
        request["context[file]"] = filename
        if line:
            request["context[rightLine]"] = line
        if version:
            request["context[version]"] = version
        if no_notification:
            request["silenceNotification"] = "true"

    result = requests.post(
        "{swarm_url}/api/v{api_ver}/comments".format(
            swarm_url=options.swarm_url, api_ver=api_ver),
        auth=HTTPBasicAuth(options.user, options.password),
        data=request
    )
    check_request_result(result)


def vote_review(options, version=None):
    if options.action == "clear":
        required(options, value=False)
    request = {"vote[value]": options.action}
    if not version:
        request["vote[version]"] = last_version(options)

    result = requests.patch(
        "{swarm_url}/api/v{api_ver}/reviews/{review_id}".format(
            swarm_url=options.swarm_url,
            api_ver=api_ver,
            review_id=options.review_id),
        auth=HTTPBasicAuth(options.user, options.password),
        data=request
    )
    check_request_result(result)


def required(options, value=True):
    payload = {'required': value}
    result = requests.post(
        "{swarm_url}/reviews/{review_id}/reviewers/{user}?_method=PATCH".format(
            swarm_url=options.swarm_url,
            review_id=options.review_id,
            user=options.user),
        auth=HTTPBasicAuth(options.user, options.password),
        data=payload)
    check_request_result(result)

# =============================================================================
# Parse script options
# =============================================================================


def parse_arguments():
    parser = ArgumentParser()
    parser.add_argument(
        "action",
        metavar="action",
        help="REQUIRED. One of {comment, up, down, clear, required}. \
        Action to apply to swarm. \
        \"comment\" - posts comment, \
        \"required\" - set my vote required, \
        \"up\" - votes review up. \
        \"down\" - votes review down, \
        \"clear\" - clear vote and set reviewer as optional",
        choices=["comment", "up", "down", "required", "clear"]
    )
    parser.add_argument(
        "text",
        nargs="?",
        help="Comment text. Mandatory parameter for posting comments."
    )
    parser.add_argument(
        "--id",
        dest="review_id",
        required=True,
        help="REQUIRED. Id of the swarm review to apply action to."
    )
    parser.add_argument(
        "--user",
        required=True,
        help="REQUIRED. Swarm user name."
    )
    parser.add_argument(
        "--password",
        required=True,
        help="REQUIRED. Swarm user password."
    )
    parser.add_argument(
        "--url",
        dest="swarm_url",
        required=True,
        help="REQUIRED. Swarm main page address, no slash in the end."
    )

    options = parser.parse_args()
    if options.action == "comment" and options.text is None:
        print("Error: comment text is required for posting comments")
        parser.print_help()
        sys.exit(1)

    return options


# =============================================================================
# Main functionality
# =============================================================================


def main():
    options = parse_arguments()
    functions = {
        "comment": post_comment,
        "up": vote_review,
        "down": vote_review,
        "clear": vote_review,
        "required": required
    }
    functions[options.action](options)

# =============================================================================
# Actual executing
# =============================================================================


if __name__ == '__main__':
    main()
