import logging
import os
from git import Repo, InvalidGitRepositoryError, GitCommandError

from .version import get_signo
from .project_handler_base import ProjectHandlerBase, register_schema
from .tools import check_call, CalledProcessError

logger = logging.getLogger(__name__)

@register_schema('local')
class LocalProjectHandler(ProjectHandlerBase):

    def get_path(self, base_path):
        return os.path.join(base_path, self.url)

    def config_init(self):
        if not os.path.isdir(self.path):
            os.mkdir(self.path)

    def post_process_cli_config(self, config):
        pass

    def edit(self, name, summary, command_result):
        pass

    def create(self, name, default, command_result):
        pass

    def remove(self, name, command_result):
        pass

    def update(self):
        pass

@register_schema('git')
class GitProjectHandler(ProjectHandlerBase):

    def __init__(self, url, path):
        super(GitProjectHandler, self).__init__(url, path)
        self.__repo = None

    def get_path(self, base_path):
        dirname = self.url.split('/')[-1:][0]
        if dirname.endswith('.git'):
            dirname = dirname[:-4]
        return os.path.join(base_path, dirname)

    @property
    def repo(self):
        if self.__repo is None:
            self.__repo = Repo(self.path)
        return self.__repo

    def __clone(self):
        try:
            Repo.clone_from(self.url, self.path)
        except GitCommandError as e:
            logger.error(e, exc_info = True)

    def config_init(self):
        if not os.path.isdir(self.path):
            os.mkdir(self.path)
            self.__clone()
            return

        try:
            repo = Repo(self.path)
        except InvalidGitRepositoryError:
            self.__clone()

    def post_process_cli_config(self, config):
        add_nopush = ['edit', 'create', 'remove']
        # add nopush options for some command
        for command in config:
            if command['name'] != 'project':
                continue

            for subcommand in command['subcommands']:
                if subcommand['name'] in add_nopush:
                    subcommand['arguments'].append(
                        dict(arg_name = '--nopush', action = 'store_true', default = False, help = "Do not push to the remote")
                    )
            break

    def __commit_files(self, message, files, type = 'env'):
        index = self.repo.index
        index.add(files)
        index.commit("-- {}: {}\n-- remark: generated by {}\nreview: none".format(type, message, get_signo()))

    def __push(self):
        return
        self.repo.remote().push()

    def edit(self, name, summary, nopush, command_result):
        if not command_result:
            return
        message = summary if len(summary) else "Modify project '{}'".format(name)
        self.__commit_files(message, [self.get_project_config_path(name)])
        if not nopush:
            self.__push()
        logger.info("Project config saved{}".format("." if nopush else " and pushed to the remote."))

    def create(self, name, default, nopush, command_result):
        if not command_result:
            return
        self.__commit_files("New project '{}' created".format(name), [self.get_project_config_path(name)])
        if not nopush:
            self.__push()
        logger.info("Project config saved{}".format("." if nopush else " and pushed to the remote."))

    def remove(self, name, nopush, command_result):
        index = self.repo.index
        project_filename = self.get_project_config_path(name)
        index.remove([project_filename])
        index.commit("-- env: Project '{}' removed\n-- remark: generated by {}\nreview: none".format(name, get_signo()))
        if not nopush:
            self.__push()
        logger.info("Project config removed{}".format("." if nopush else " and pushed to the remote."))

    def update(self):
        try:
            check_call(['git', 'pull', '--rebase'], cwd = self.path)
            logger.info("Project config repo updated.")
        except CalledProcessError as e:
            logger.error("Cannot update the project's repository because of:\n%s", e.output)
