import github3
import datetime
import os
import getpass
import time


class GitHub_LLNL_Year_Commits:
    def __init__(self):
        self.commits_dict_list = []
        self.commits = {}
        self.sorted_weeks = []

    def get_year_commits(
        self, username="", password="", organization="llnl", force=True
    ):
        """
        Does setup such as login, printing API info, and waiting for GitHub to
        build the commit statistics. Then gets the last year of commits and
        prints them to file.
        """
        file_path = "year_commits.csv"
        if force or not os.path.isfile(file_path):
            my_github.login(username, password)
            calls_beginning = self.logged_in_gh.ratelimit_remaining + 1
            print("Rate Limit: " + str(calls_beginning))
            my_github.get_org(organization)
            my_github.repos(building_stats=True)
            print("Letting GitHub build statistics.")
            time.sleep(30)
            print("Trying again.")
            my_github.repos(building_stats=False)
            my_github.calc_total_commits(starting_commits=35163)
            my_github.write_to_file()
            calls_remaining = self.logged_in_gh.ratelimit_remaining
            calls_used = calls_beginning - calls_remaining
            print(
                "Rate Limit Remaining: "
                + str(calls_remaining)
                + "\nUsed "
                + str(calls_used)
                + " API calls."
            )

    def login(self, username="", password=""):
        """
        Performs a login and sets the Github object via given credentials. If
        credentials are empty or incorrect then prompts user for credentials.
        Stores the authentication token in a CREDENTIALS_FILE used for future
        logins. Handles Two Factor Authentication.
        """
        try:

            token = ""
            id = ""
            if not os.path.isfile("CREDENTIALS_FILE"):
                if username == "" or password == "":
                    username = raw_input("Username: ")
                    password = getpass.getpass("Password: ")
                note = "GitHub Organization Stats App"
                note_url = "http://software.llnl.gov/"
                scopes = ["user", "repo"]
                auth = github3.authorize(
                    username,
                    password,
                    scopes,
                    note,
                    note_url,
                    two_factor_callback=self.prompt_2fa,
                )
                token = auth.token
                id = auth.id
                with open("CREDENTIALS_FILE", "w+") as fd:
                    fd.write(token + "\n")
                    fd.write(str(id))
                fd.close()
            else:
                with open("CREDENTIALS_FILE", "r") as fd:
                    token = fd.readline().strip()
                    id = fd.readline().strip()
                fd.close()
            print("Logging in.")
            self.logged_in_gh = github3.login(
                token=token, two_factor_callback=self.prompt_2fa
            )
            self.logged_in_gh.user().to_json()
        except (ValueError, AttributeError, github3.models.GitHubError):
            print("Bad credentials. Try again.")
            self.login()

    def prompt_2fa(self):
        """
        Taken from
        http://github3py.readthedocs.io/en/master/examples/two_factor_auth.html
        Prompts a user for their 2FA code and returns it.
        """
        code = ""
        while not code:
            code = raw_input("Enter 2FA code: ")
        return code

    def get_org(self, organization_name=""):
        """
        Retrieves an organization via given org name. If given
        empty string, prompts user for an org name.
        """
        if organization_name == "":
            organization_name = raw_input("Organization: ")
        print("Getting organization.")
        self.org_retrieved = self.logged_in_gh.organization(organization_name)

    def repos(self, building_stats=False):
        """
        Retrieves the last year of commits for the organization and stores them
        in weeks (UNIX time) associated with number of commits that week.
        """
        print("Getting repos.")
        for repo in self.org_retrieved.iter_repos():
            for activity in repo.iter_commit_activity():
                if not building_stats:
                    self.commits_dict_list.append(activity)

    def calc_total_commits(self, starting_commits=0):
        """
        Uses the weekly commits and traverses back through the last
        year, each week subtracting the weekly commits and storing them. It
        needs an initial starting commits number, which should be taken from
        the most up to date number from github_stats.py output.
        """
        for week_of_commits in self.commits_dict_list:
            try:
                self.commits[week_of_commits["week"]] -= week_of_commits["total"]
            except KeyError:
                total = self.commits[week_of_commits["week"]] = -week_of_commits[
                    "total"
                ]
        self.sorted_weeks = sorted(self.commits)

        # reverse because lower numbered weeks are older in time.
        # we traverse from most recent to oldest
        for week in reversed(self.sorted_weeks):
            self.commits[week] = self.commits[week] + starting_commits
            starting_commits = self.commits[week]

    def write_to_file(self):
        """
        Writes the weeks with associated commits to file.
        """
        with open("../github_stats_output/last_year_commits.csv", "w+") as output:
            output.write(
                "date,organization,repos,members,teams,"
                + "unique_contributors,total_contributors,forks,"
                + "stargazers,pull_requests,open_issues,has_readme,"
                + "has_license,pull_requests_open,pull_requests_closed,"
                + "commits\n"
            )
            # no reverse this time to print oldest first
            previous_commits = 0
            for week in self.sorted_weeks:
                if str(self.commits[week]) != previous_commits:  # delete dups
                    week_formatted = datetime.datetime.utcfromtimestamp(week).strftime(
                        "%Y-%m-%d"
                    )
                    output.write(
                        week_formatted
                        + ",llnl,0,0,0,0,0,0,0,0,0,0,0,0,0,"
                        + str(self.commits[week])
                        + "\n"
                    )
                    previous_commits = str(self.commits[week])


if __name__ == "__main__":
    my_github = GitHub_LLNL_Year_Commits()
    my_github.get_year_commits()