"""Utilities for interacting with GitHub"""
import os
import json
import webbrowser
import stat
import sys
from git import Repo

from .context import Context

event_dict = {
    "added_to_project": (
        lambda event: "{} added the issue to a project.".format(event["actor"]["login"])
    ),
    "assigned": (
        lambda event: "{} assigned the issue to {}.".format(
            event["actor"]["login"], event["assignee"]["login"]
        )
    ),
    "closed": (lambda event: "{} closed this issue.".format(event["actor"]["login"])),
    "converted_note_to_issue": (
        lambda event: "{} created this issue from a note.".format(
            event["actor"]["login"]
        )
    ),
    "demilestoned": (lambda event: "The issue was removed from a milestone."),
    "head_ref_deleted": (lambda event: "The pull request's branch was deleted."),
    "head_ref_restored": (lambda event: "The pull request's branch was restored."),
    "labelled": (
        lambda event: "{} added {} label to the issue.".format(
            event["actor"]["login"], event["label"]
        )
    ),
    "locked": (
        lambda event: "The issue was locked by {}.".format(event["actor"]["login"])
    ),
    "mentioned": (
        lambda event: "{} was mentioned in the issue's body.".format(
            event["actor"]["login"]
        )
    ),
    "marked_as_duplicate": (
        lambda event: "The issue was marked duplicate by {}.".format(
            event["actor"]["login"]
        )
    ),
    "merged": (
        lambda event: "The issue was merged by {}.".format(event["actor"]["login"])
    ),
    "milestoned": (lambda event: "The issue was added to a milestone."),
    "moved_columns_in_project": (
        lambda event: "The issue was moved between columns in a project board."
    ),
    "referenced": (lambda event: "The issue was referenced from a commit message."),
    "renamed": (lambda event: "The title of the issue was changed."),
    "reopened": (
        lambda event: "The issue was reopened by {}".format(event["actor"]["login"])
    ),
    "review_dismissed": (
        lambda event: "{} dismissed a review from the pull request.".format(
            event["actor"]["login"]
        )
    ),
    "review_requested": (
        lambda event: "{} requested review from the subject on this pull request.".format(
            event["actor"]["login"]
        )
    ),
    "review_request_removed": (
        lambda event: "{} removed the review request for the subject on this pull request.".format(
            event["actor"]["login"]
        )
    ),
    "subscribed": (
        lambda event: "{} subscribed to receive notifications for the issue.".format(
            event["actor"]["login"]
        )
    ),
    "transferred": (lambda event: "The issue was transferred to another repository."),
    "unassigned": (
        lambda event: "{} was unassigned from the issue.".format(
            event["actor"]["login"]
        )
    ),
    "unlabeled": (lambda event: "A label was removed from the issue."),
    "unlocked": (
        lambda event: "The issue was unlocked by {}".format(event["actor"]["login"])
    ),
    "unmarked_as_duplicate": (lambda event: "The was unmarked as dublicate."),
    "user_blocked": (lambda event: "A user was blocked from the organization."),
}


def authorize(ghub, reauthorize=False, fromenv=False):
    """Authorize a user for GHub

    Keyword arguments:
    ghub -- the ghub object that needs authorization
    reauthorize -- performs authorization again (default False)
    """
    if fromenv:
        oauth_data = json.loads(os.environ["GHUB_CRED"])
        ghub.oauth_data = oauth_data
        ghub.github.token = oauth_data
        return True
    if not os.path.isfile(ghub.data_path / ghub.auth_filename) or reauthorize:
        authorization_base_url = "https://github.com/login/oauth/authorize"
        token_url = "https://github.com/login/oauth/access_token"
        authorization_url, _ = ghub.github.authorization_url(authorization_base_url)
        webbrowser.open(authorization_url)
        print("Please visit this site and grant access: {}".format(authorization_url))
        redirect_response = input(
            "Please enter the URL you were redirected to after granting access: "
        )
        try:
            response = ghub.github.fetch_token(
                token_url,
                client_secret=ghub.client_secret,
                authorization_response=redirect_response,
            )
        except Exception as e:
            print(e)
            print(
                "Network Error. Make sure you have a working internet connection and try again."
            )
            sys.exit(1)
        if not os.path.isdir(ghub.data_path):
            os.makedirs(ghub.data_path)
        data_file = open(ghub.data_path / ghub.auth_filename, "w+")
        json.dump(response, data_file)
        data_file.close()
        os.chmod(ghub.data_path / ghub.auth_filename, stat.S_IRUSR | stat.S_IWUSR)
        ghub.oauth_data = response
        return True
    else:
        data_file = open(ghub.data_path / ghub.auth_filename, "r")
        oauth_data = json.loads(data_file.read())
        data_file.close()
        ghub.oauth_data = oauth_data
        ghub.github.token = oauth_data
        return True


def get_user(ghub, user):
    url = ghub.api_url + ghub.endpoints["users"] + user
    response = ghub.github.get(url)
    if response.status_code == 200:
        ghub.context = Context(prev_context=ghub.context)
        ghub.context.context = "user"
        ghub.context.location = user
        ghub.context.cache = response.json()
        return True
    return False


def get_org(ghub, org):
    url = ghub.api_url + ghub.endpoints["orgs"] + org
    response = ghub.github.get(url)
    if response.status_code == 200:
        ghub.context = Context(prev_context=ghub.context)
        ghub.context.context = "org"
        ghub.context.location = org
        ghub.context.cache = response.json()
        return True
    return False


def get_user_tabs(ghub, tab=""):
    tabs = ["repos", "stars", "followers", "following", "notifications"]
    if tab not in tabs:
        print("{} is not a valid user tab".format(tab))
        return
    if ghub.context.context == "root":
        if tab == "":
            ghub.context.set_context_to_root()
        elif tab == "repos":
            response = ghub.github.get(ghub.api_url + ghub.endpoints["user"] + "/repos")
            if response.status_code == 200:
                ghub.context = Context(prev_context=ghub.context)
                ghub.context.cache = response.json()
                ghub.context.location = ghub.user["login"] + "/" + "repos"
                ghub.context.context = "repos"
            else:
                print("Error getting data - " + response.status_code)
        elif tab == "stars":
            response = ghub.github.get(
                ghub.api_url + ghub.endpoints["user"] + "/starred"
            )
            if response.status_code == 200:
                ghub.context = Context(prev_context=ghub.context)
                ghub.context.cache = response.json()
                ghub.context.location = ghub.user["login"] + "/" + "stars"
                ghub.context.context = "stars"
            else:
                print("Error getting data - " + response.status_code)
        elif tab == "followers" or tab == "following":
            response = ghub.github.get(
                ghub.api_url + ghub.endpoints["user"] + "/" + tab
            )
            if response.status_code == 200:
                ghub.context = Context(prev_context=ghub.context)
                ghub.context.cache = response.json()
                ghub.context.location = ghub.user["login"] + "/" + tab
                ghub.context.context = tab
            else:
                print("Error getting data - " + response.status_code)
        elif tab == "notifications":
            response = ghub.github.get(ghub.api_url + ghub.endpoints["notifications"])
            if response.status_code == 200:
                ghub.context = Context(prev_context=ghub.context)
                ghub.context.cache = response.json()
                ghub.context.location = ghub.user["login"] + "/" + tab
                ghub.context.context = tab
            else:
                print("Error getting data - " + response.status_code)
    elif ghub.context.context == "user" or ghub.context.context == "org":
        if tab == "":
            ghub.context.set_context_to_root()
        elif tab == "repos":
            if ghub.context.context == "user":
                url = (
                    ghub.api_url
                    + ghub.endpoints["users"]
                    + ghub.context.location
                    + "/repos"
                )
            else:
                url = (
                    ghub.api_url
                    + ghub.endpoints["orgs"]
                    + ghub.context.location
                    + "/repos"
                )
            response = ghub.github.get(url)
            if response.status_code == 200:
                ghub.context = Context(prev_context=ghub.context)
                ghub.context.cache = response.json()
                ghub.context.location = (
                    ghub.context.prev_context.location + "/" + "repos"
                )
                ghub.context.context = "repos"
            else:
                print("Error getting data - " + response.status_code)
        elif tab == "stars":
            response = ghub.github.get(
                ghub.api_url
                + ghub.endpoints["users"]
                + ghub.context.location
                + "/starred"
            )
            if response.status_code == 200:
                ghub.context = Context(prev_context=ghub.context)
                ghub.context.cache = response.json()
                ghub.context.location = (
                    ghub.context.prev_context.location + "/" + "star"
                )
                ghub.context.context = "stars"
            else:
                print("Error getting data - " + response.status_code)
        elif tab == "followers" or tab == "following":
            response = ghub.github.get(
                ghub.api_url
                + ghub.endpoints["users"]
                + ghub.context.location
                + "/"
                + tab
            )
            if response.status_code == 200:
                ghub.context = Context(prev_context=ghub.context)
                ghub.context.cache = response.json()
                ghub.context.location = ghub.context.prev_context.location + "/" + tab
                ghub.context.context = tab
            else:
                print("Error getting data - " + response.status_code)
    else:
        pass


def get_latest_commit(ghub, repo, branch="master"):
    api_url = "https://api.github.com/repos/{}/branches/{}".format(repo, branch)
    response = ghub.github.get(api_url)
    if response.status_code == 200:
        response = response.json()
        return response["commit"]["commit"]
    else:
        return False


def get_tree(ghub, repo=None, branch="master", tree_url=None):
    if tree_url == None:
        latest_commit = get_latest_commit(ghub, repo, branch)
        if latest_commit == False:
            return False
        response = ghub.github.get(latest_commit["tree"]["url"])
        if response.status_code == 200:
            response = response.json()
            return response
        return False
    else:
        response = ghub.github.get(tree_url)
        if response.status_code == 200:
            response = response.json()
            return response


def get_blob(ghub, blob_url):
    response = ghub.github.get(blob_url)
    if response.status_code == 200:
        return response.json()
    return False


def clone_repo(ghub, dir, repo_name=None):
    print("Preparing to clone...")
    if repo_name == None:
        repo_name = "/".join(ghub.context.location.split("/")[:2])
    if dir[0] == "~":
        dir = os.path.expanduser("~") + dir[1:]
    dir = dir + "/" + repo_name.split("/")[1]
    try:
        Repo.clone_from("https://github.com/" + repo_name, dir)
        print("{} cloned to {}".format(repo_name, dir))
        return True
    except Exception as e:
        print(e)
        return False


def star_repo(ghub, repo_name=None):
    print("Starring repo...")
    if repo_name == None:
        repo_name = ghub.context.location
    star_url = ghub.api_url + ghub.endpoints["user"] + "/" + "starred/" + repo_name
    response = ghub.github.get(star_url)
    if response.status_code == 204:
        print("Repo is already starred.")
    elif response.status_code == 404:
        resp = ghub.github.put(star_url)
        if resp.status_code == 204:
            print("{} starred".format(repo_name))
        else:
            print("Error starring repo")


def unstar_repo(ghub, repo_name=None):
    print("Unstarring repo...")
    if repo_name == None:
        repo_name = ghub.context.location
    star_url = ghub.api_url + ghub.endpoints["user"] + "/" + "starred/" + repo_name
    response = ghub.github.get(star_url)
    if response.status_code == 204:
        resp = ghub.github.delete(star_url)
        if resp.status_code == 204:
            print("{} unstarred".format(repo_name))
        else:
            print("Error unstarring repo")
    elif response.status_code == 404:
        print("Repo is not starred.")


def watch_repo(ghub, repo_name=None):
    print("Subscribing to repo...")
    if repo_name == None:
        repo_name = ghub.context.location
    watch_url = ghub.api_url + ghub.endpoints["repos"] + repo_name + "/subscription"
    response = ghub.github.get(watch_url)
    if response.status_code == 200:
        print("You are already watching this repo.")
    elif response.status_code == 404:
        resp = ghub.github.put(watch_url)
        if resp.status_code == 200:
            print("Watching {}".format(repo_name))
        else:
            print("Error subscribing to repo")


def unwatch_repo(ghub, repo_name=None):
    print("Unsubscribing repo...")
    if repo_name == None:
        repo_name = ghub.context.location
    watch_url = ghub.api_url + ghub.endpoints["repos"] + repo_name + "/subscription"
    response = ghub.github.get(watch_url)
    if response.status_code == 200:
        resp = ghub.github.delete(watch_url)
        if resp.status_code == 204:
            print("{} unsubscribed".format(repo_name))
        else:
            print("Error unsubscribing to repo")
    elif response.status_code == 404:
        print("You are not watching this repo.")


def fork_repo(ghub, repo_name=None):
    print("Forking Repo...")
    if repo_name == None:
        repo_name = ghub.context.location.split("/")
        repo_name = "/".join(repo_name[:2])
    true_repo_name = repo_name.split("/")[1]
    forked_url = (
        ghub.api_url
        + ghub.endpoints["repos"]
        + ghub.get_user_username()
        + "/"
        + true_repo_name
    )
    response = ghub.github.get(forked_url)
    if response.status_code == 200:
        print("Cannot fork. Repo Already Exists.")
        return False
    print("Repo is being forked. Please wait for it to complete.", end="")
    response = ghub.github.post(
        ghub.api_url + ghub.endpoints["repos"] + repo_name + "/forks"
    )
    if response.status_code == 202:
        print(
            "\nForking complete. Forked repo to {}".format(
                ghub.get_user_username() + "/" + true_repo_name
            )
        )
        return True
    else:
        print("Error while trying fork.")
        return False


def get_prs(ghub, repo_name=None):
    if repo_name == None:
        repo_name = "/".join(ghub.context.location.split("/")[:2])
    pr_url = ghub.api_url + ghub.endpoints["repos"] + repo_name + "/pulls"
    response = ghub.github.get(pr_url)
    if response.status_code == 200:
        ghub.context = Context(prev_context=ghub.context)
        ghub.context.context = "pull_requests"
        ghub.context.location = repo_name + "/pull_requests"
        ghub.context.cache = response.json()
        return True
    return False


def get_pr(ghub, pr_no):
    if not pr_no.isdigit():
        print("Invalid PR number")
        return False
    repo_name = "/".join(ghub.context.location.split("/")[:2])
    pr_url = ghub.api_url + ghub.endpoints["repos"] + repo_name + "/pulls/" + pr_no
    response = ghub.github.get(pr_url)
    if response.status_code == 200:
        ghub.context = Context(prev_context=ghub.context)
        ghub.context.context = "pull_request"
        ghub.context.location = repo_name + "/pull_requests/" + pr_no
        ghub.context.cache = response.json()
        return True
    elif response.status_code == 404:
        print("No PR found with PR number {}".format(pr_no))
    return False


def get_pr_info(ghub, info_type="comments"):
    info_url = ghub.context.cache["_links"][info_type]["href"]
    response = ghub.github.get(info_url)
    return response.json(), response.status_code


def get_issues(ghub, repo_name=None):
    if repo_name == None:
        repo_name = "/".join(ghub.context.location.split("/")[:2])
    issue_url = ghub.api_url + ghub.endpoints["repos"] + repo_name + "/issues"
    response = ghub.github.get(issue_url)
    if response.status_code == 200:
        ghub.context = Context(prev_context=ghub.context)
        ghub.context.context = "issues"
        ghub.context.location = repo_name + "/issues"
        ghub.context.cache = response.json()
        return True
    return False


def get_issue(ghub, issue_no):
    if not issue_no.isdigit():
        print("Invalid issue number")
        return False
    repo_name = "/".join(ghub.context.location.split("/")[:2])
    issue_url = (
        ghub.api_url + ghub.endpoints["repos"] + repo_name + "/issues/" + issue_no
    )
    response = ghub.github.get(issue_url)
    if response.status_code == 200:
        ghub.context = Context(prev_context=ghub.context)
        ghub.context.context = "issue"
        ghub.context.location = repo_name + "/issues/" + issue_no
        ghub.context.cache = response.json()
        return True
    elif response.status_code == 404:
        print("No issue found with issue number {}".format(issue_no))
    return False


def get_issue_info(ghub, info_type="comments"):
    info_url = ghub.context.cache["{}_url".format(info_type)]
    response = ghub.github.get(info_url)
    return response.json(), response.status_code

[((102, 21, 102, 56), 'json.loads', 'json.loads', ({(102, 32, 102, 55): "os.environ['GHUB_CRED']"}, {}), "(os.environ['GHUB_CRED'])", False, 'import json\n'), ((110, 8, 110, 42), 'webbrowser.open', 'webbrowser.open', ({(110, 24, 110, 41): 'authorization_url'}, {}), '(authorization_url)', False, 'import webbrowser\n'), ((130, 8, 130, 38), 'json.dump', 'json.dump', ({(130, 18, 130, 26): 'response', (130, 28, 130, 37): 'data_file'}, {}), '(response, data_file)', False, 'import json\n'), ((132, 8, 132, 82), 'os.chmod', 'os.chmod', ({(132, 17, 132, 52): '(ghub.data_path / ghub.auth_filename)', (132, 54, 132, 81): '(stat.S_IRUSR | stat.S_IWUSR)'}, {}), '(ghub.data_path / ghub.auth_filename, stat.S_IRUSR | stat.S_IWUSR)', False, 'import os\n'), ((321, 8, 321, 63), 'git.Repo.clone_from', 'Repo.clone_from', ({(321, 24, 321, 57): "('https://github.com/' + repo_name)", (321, 59, 321, 62): 'dir'}, {}), "('https://github.com/' + repo_name, dir)", False, 'from git import Repo\n'), ((106, 11, 106, 62), 'os.path.isfile', 'os.path.isfile', ({(106, 26, 106, 61): '(ghub.data_path / ghub.auth_filename)'}, {}), '(ghub.data_path / ghub.auth_filename)', False, 'import os\n'), ((127, 15, 127, 44), 'os.path.isdir', 'os.path.isdir', ({(127, 29, 127, 43): 'ghub.data_path'}, {}), '(ghub.data_path)', False, 'import os\n'), ((128, 12, 128, 39), 'os.makedirs', 'os.makedirs', ({(128, 24, 128, 38): 'ghub.data_path'}, {}), '(ghub.data_path)', False, 'import os\n'), ((318, 14, 318, 37), 'os.path.expanduser', 'os.path.expanduser', ({(318, 33, 318, 36): '"""~"""'}, {}), "('~')", False, 'import os\n'), ((126, 12, 126, 23), 'sys.exit', 'sys.exit', ({(126, 21, 126, 22): '(1)'}, {}), '(1)', False, 'import sys\n')]