import sys
import os
import getpass
import time
import json
from boj import BOJ
from git import Git
from option import Option


ERROR_FORMAT = '\n* ERROR: [%s] [%s]\n'
PRINT_FORMAT = '* %s\n'
DEFAULT_OPTION_FILE = 'option.json'


class Main:

    def __init__(self, boj, git, option):
        self.boj = boj
        self.git = git
        self.option = option

        self.candidate_problems = []

    def run(self):
        error, result = self.git.clone()
        if error:
            sys.exit(ERROR_FORMAT % ('git_clone', result))

        error, result = self.git.pull()
        if error:
            sys.exit(ERROR_FORMAT % ('git_pull', result))

        while True:
            error = self.analyze_solved_problems()
            if error:
                self.idle()
                continue

            self.push_solved_problems()
            self.idle()

    def analyze_solved_problems(self):
        error, solved_problems = self.boj.get_solved_problems()
        if error:
            print(ERROR_FORMAT % ('get_solved_problmes', solved_problems))
            return True

        solved_set = set()
        self.candidate_problems = []

        for problem in solved_problems:
            if problem['problem_id'] in solved_set:
                continue

            solved_set.add(problem['problem_id'])
            self.candidate_problems.append(problem)

        return False

    def push_solved_problems(self):
        for problem in self.candidate_problems:
            submission_id = problem['submission_id']
            problem_id = problem['problem_id']
            problem_title = problem['problem_title']
            language = problem['language']

            commit_message = self.option.commit_message(problem)
            source_tree = self.option.source_tree(problem, self.git.get_repo_name())
            source_name = self.option.source_name(problem)
            error, ext = self.option.get_ext(language)
            if error:
                print(ERROR_FORMAT % ('get_ext', ext))
                continue
            file_path = '%s/%s%s' % (source_tree, source_name, ext)

            support, same = self.option.lang(problem)
            if support:
                if not same:
                    print(PRINT_FORMAT % ("'%s' language is not supported (submission: %s, problem: %s)" % (language, submission_id, problem_id)))
                    continue

            private = self.option.private()
            if private:
                error, source_open = self.boj.get_private(submission_id)
                if error:
                    print(ERROR_FORMAT % ('get_private', source_open))
                    continue
                if not source_open:
                    print(PRINT_FORMAT % ('The source is private (submission: %s, problem: %s)' % (submission_id, problem_id)))
                    continue

            if os.path.exists(file_path):
                print(PRINT_FORMAT % ('The source already exists (submission: %s, problem: %s)' % (submission_id, problem_id)))
                continue
       
            print(PRINT_FORMAT % ('Download the source (submission: %s, problem: %s)' % (submission_id, problem_id)))
            error, source = self.boj.download_source(submission_id)
            if error:
                print(ERROR_FORMAT % ('download_source', source))
                continue
            print(source)

            self.save_source(source_tree, file_path, source)
            print(PRINT_FORMAT % ("Successfully saved the '%s'" % (file_path)))

            file_path = file_path.replace(self.git.get_repo_name(), '.', 1)
            error, result = self.git.all(file_path, commit_message)
            if error:
                sys.exit(ERROR_FORMAT % ('git_all', result))
            print(PRINT_FORMAT % ('Successfully pushed the source (submission: %s, problem: %s)' % (submission_id, problem_id)))

    def save_source(self, source_tree, file_path, source):
        if not os.path.isdir(source_tree):
            os.makedirs(source_tree)

        f = open(file_path, 'w')
        f.write(source)
        f.close()

    def idle(self):
        poll = self.option.poll()

        print(PRINT_FORMAT % ('Wait %d seconds... \n' % (poll)))
        time.sleep(poll)
        print(PRINT_FORMAT % ('Restart work'))


def set_options():
    if not os.path.isfile(DEFAULT_OPTION_FILE):
        sys.exit(ERROR_FORMAT % ('set_options', '%s not found' % (DEFAULT_OPTION_FILE)))
    
    try:
        option_info = json.loads(open(DEFAULT_OPTION_FILE, 'r').read())
    except json.decoder.JSONDecodeError as e:
        sys.exit(ERROR_FORMAT % ('set_options', e))

    return Option(option_info)

def login_boj():
    boj = BOJ()

    for i in range(3):        
        user_id = input('* BOJ id: ')
        user_password = getpass.getpass('* BOJ password: ')

        error, result = boj.login_boj(user_id, user_password)
        if error:
            print(PRINT_FORMAT % (result))
            continue
        
        print(PRINT_FORMAT % (result))
        return boj

    sys.exit(ERROR_FORMAT % ('login_boj', 'Login failed'))

def login_github():
    git = Git()
    
    for i in range(3):
        user_id = input('* GitHub id: ')
        user_password = getpass.getpass('* GitHub password: ')

        error, result = git.login_github(user_id, user_password)
        if error:
            print(PRINT_FORMAT % (result))
            continue

        print(PRINT_FORMAT % (result))
        repo_name = input('* Repository: ')
        git.set_repository(repo_name)
        return git
    
    sys.exit(ERROR_FORMAT % ('login_github', 'Login failed'))


if __name__ == '__main__':
    option = set_options()
    boj = login_boj()
    git = login_github()

    try:
        Main(boj, git, option).run()
    except KeyboardInterrupt:
        print('\n* bye\n')