import json
import sys
from os import getenv, path
from pprint import pprint

import click  # pylint: disable=import-error
import requests  # pylint: disable=import-error
from dotenv import load_dotenv  # pylint: disable=import-error

env = load_dotenv()
api_url = getenv("API_URL", default="https://api.github.com/graphql")
github_token = getenv("GITHUB_TOKEN", default=None)

if github_token is None:
    sys.exit(
        "GitHub Token is not set."
        + "Please set the GITHUB_TOKEN env variable in your system or "
        + "the .env file of your project."
    )

client_id = getenv("CLIENT_ID", default="copy_labels.py")
headers = {
    "Authorization": "bearer {github_token}".format(github_token=github_token),
    "Accept": "application/vnd.github.bane-preview+json",
    "Content-Type": "application/json",
}


def create_label(repo_id, label):
    """
    Create label in the supplied repo.

    :param repo_id: Unique ID that represents the repo in GitHub
    :type repo_id: str
    :param label: Object with label information.
    :type label: dict
    :return: GitHub API request response
    """

    query_variables = {
        "createLabelInput": {
            "color": label["color"],
            "description": label["description"],
            "name": label["name"],
            "repositoryId": repo_id,
        }
    }

    with open(
        path.join(path.dirname(__file__), "queries/create_label.gql"), "r"
    ) as query_file:
        query = "".join(query_file.readlines())

    payload = {"query": query, "variables": query_variables}
    response = requests.post(api_url, data=json.dumps(payload), headers=headers).json()
    print("Created label {label}".format(label=label["name"]))

    return response


def get_labels(owner, repo):
    """
    Gets a list of labels from the supplied repo.
    :param owner: Repo owner GitHub login.
    :type owner: str
    :param repo: Repository name.
    :type repo: str
    :return: A tuple with the GitHub id for the repository and a list of labels defined in the repository
    """

    query_variables = {
        "owner": owner,
        "name": repo,
    }

    with open(
        path.join(path.dirname(__file__), "queries/get_repo_data.gql"), "r"
    ) as query_file:
        query = "".join(query_file.readlines())

    payload = {"query": query, "variables": query_variables}
    response = requests.post(api_url, data=json.dumps(payload), headers=headers)

    status_code = response.status_code
    result = response.json()

    if status_code >= 200 and status_code <= 300:
        repo_id = result["data"]["repository"]["id"]
        labels = result["data"]["repository"]["labels"]["nodes"]

        return repo_id, labels
    else:
        raise Exception(
            "[ERROR] getting issue labels. Status Code: {status_code} - Message: {result}".format(
                status_code=status_code, result=result["message"]
            )
        )


def delete_label(label_id):
    """
    Delete the specified label
    :param label_id: Label's node id.
    :type label_id: str
    :return: GitHub API request response.
    """

    query_variables = {
        "deleteLabelInput": {"clientMutationId": client_id, "id": label_id}
    }

    with open(
        path.join(path.dirname(__file__), "queries/delete_label.gql"), "r"
    ) as query_file:
        query = "".join(query_file.readlines())

    payload = {"query": query, "variables": query_variables}
    result = requests.post(api_url, data=json.dumps(payload), headers=headers).json()

    return result


@click.command()
@click.option("--dry", is_flag=True)
@click.argument("source_repo")
@click.argument("target_repo")
def copy_labels(source_repo, target_repo, dry):
    """
    Copy labels from the source repository to the target repository.
    \f
    :param source: The full name of a GitHub repo from where the labels will be copied from. Eg. github/opensourcefriday
    :type source: str
    :param target: The full name of a GitHub repo to where the labels will be copied. Eg. github/opensourcefriday
    :type target: str
    :return:
    """
    source_owner, source_repo_name = source_repo.split("/")
    target_owner, target_repo_name = target_repo.split("/")

    try:
        print(
            "Fetching labels for {source_repo_name} repo.".format(
                source_repo_name=source_repo_name
            )
        )
        _, source_repo_labels = get_labels(source_owner, source_repo_name)
        print(
            "Fetched labels for {source_repo_name}".format(
                source_repo_name=source_repo_name
            )
        )

        print(
            "Fetching labels for {target_repo_name} repo.".format(
                target_repo_name=target_repo_name
            )
        )
        target_repo_id, target_repo_labels = get_labels(target_owner, target_repo_name)
        print(
            "Fetched labels for {target_repo_name}".format(
                target_repo_name=target_repo_name
            )
        )

        filtered_labels = list(
            filter(lambda x: x not in target_repo_labels, source_repo_labels)
        )

        if dry:
            print("This is just a dry run. No labels will be copied/created.")
            print(
                "{label_count} labels would have been created.".format(
                    label_count=len(filtered_labels)
                )
            )
            pprint(filtered_labels, indent=4)
        else:
            print(
                "Preparing to created {label_count} labels in {target_repo}".format(
                    label_count=len(filtered_labels), target_repo=target_repo
                )
            )

            for label in filtered_labels:
                create_label(target_repo_id, label)
    except Exception as error:
        sys.exit(error)

    print("Done")


if __name__ == "__main__":
    # Pylint doesn't know that @click.command takes care of injecting the
    # function parameters. Disabling Pylint error.
    copy_labels()  # pylint: disable=no-value-for-parameter