#!/usr/bin/python
# PYTHON_ARGCOMPLETE_OK

import argparse
import argcomplete
import os
import json
import inspect
import sys
import logging
from datetime import datetime

from voidpp_tools.json_encoder import JsonEncoder

from vcp import VCP
from vcp.exceptions import ProjectException, RepositoryException
from vcp.colors import Colors
from vcp.cli_arguments_tree_parser import CLIArgumentsTreeParser

class ColoredFormatter(logging.Formatter):
    def format(self, record):
        colors = {
            logging.ERROR: Colors.red,
            logging.WARNING: Colors.yellow,
            logging.INFO: Colors.default,
            logging.DEBUG: Colors.cyan
        }

        if record.levelno == logging.DEBUG:
            msg = '%s - %s:%s: ' % (datetime.now(), record.name, record.lineno)
            msg += Colors.default + record.getMessage()
        else:
            msg = record.getMessage()

        return colors.get(record.levelno, Colors.default) + msg + Colors.default

logger_handler = logging.StreamHandler(sys.stdout)
logger_handler.setFormatter(ColoredFormatter())
logging.root.addHandler(logger_handler)
# the generic argument parser is not ready at this time, but logging info must be set here
logging.root.setLevel(logging.DEBUG if '--debug' in sys.argv else logging.INFO)
logging.raiseExceptions = True

logger = logging.getLogger('vcp')

vcp = VCP()

project_names = vcp.projects.keys()
repository_names = vcp.repositories.keys()

project_mod_subcommands = []

for name in vcp.projects:
    project = vcp.projects[name]
    appendable_repos = list(set(repository_names) - set(project.repositories))
    project_mod_subcommands.append(
        dict(
            name = name,
            desc = dict(),
            subcommands = [
                dict(
                    name = 'repository',
                    desc = dict(),
                    subcommands = [
                        dict(
                            name = 'add',
                            desc = dict(help = 'Add repo'),
                            arguments = [
                                dict(arg_name = 'names', help = 'repo names', nargs = '*', choices = appendable_repos),
                            ]
                        ),
                        dict(
                            name = 'remove',
                            desc = dict(help = 'Remove repo'),
                            arguments = [
                                dict(arg_name = 'names', help = 'repo names', nargs = '*', choices = project.repositories),
                            ]
                        ),
                        dict(
                            name = 'clear',
                            desc = dict(help = 'Remove all repo from project'),
                        ),
                    ],
                ),
                dict(
                    name = 'description',
                    desc = dict(help = 'Set project description'),
                    arguments = [
                        dict(arg_name = 'data'),
                    ]
                ),
            ],
        )
    )

# NOTE: project name parameter added later!
project_action_commands = [
    dict(
        name = 'status',
        desc = dict(help = 'Project status'),
    ),
    dict(
        name = 'cmd',
        desc = dict(help = 'Execute a command on a project (all repos)'),
        arguments = [
            dict(arg_name = 'command', help = 'command and params'),
        ]
    ),
    dict(
        name = 'untracked',
        desc = dict(help = 'Untracked files'),
    ),
    dict(
        name = 'dirty',
        desc = dict(help = 'Dirty files (list of untracked or modified files)'),
    ),
    dict(
        name = 'pushables',
        desc = dict(help = 'Show all unpushed local commits'),
        arguments = [
            dict(arg_name = '--remote', help = 'remote name', default = 'origin/master')
        ]
    ),
    dict(
        name = 'fetch',
        desc = dict(help = 'Fetch repositories'),
    ),
    dict(
        name = 'news',
        desc = dict(help = 'Project news'),
        arguments = [
            dict(arg_name = ['--fromcache', '-c'], help = 'does not do fetch before the check', action = 'store_true'),
        ]
    ),
    dict(
        name = 'diff',
        desc = dict(help = 'Show diff in all repositories'),
    ),
]

manager_commands = [
    dict(
        name = 'project',
        desc = dict(help = 'Project manager commands'),
        subcommands = [
            dict(
                name = 'create',
                desc = dict(help = 'Create project'),
                arguments = [
                    dict(arg_name = 'name', help = 'project name'),
                    dict(arg_name = '--description', default = '', help = 'project description'),
                    dict(arg_name = '--repositories', nargs = '*', choices = repository_names, default = [], help = 'project repos'),
                    dict(arg_name = '--default', help = 'make this project default', action = 'store_true'),
                ]
            ),
            dict(
                name = 'remove',
                desc = dict(help = 'Remove project'),
                arguments = [
                    dict(arg_name = 'name', help = 'project name', choices = project_names),
                ]
            ),
            dict(
                name = 'show',
                desc = dict(help = 'Remove project'),
                arguments = [
                    dict(arg_name = 'name', help = 'project name', choices = project_names),
                ]
            ),
            dict(
                name = 'list',
                desc = dict(help = 'List of projects'),
            ),
            dict(
                name = 'modify',
                desc = dict(help = 'Modify project'),
                subcommands = project_mod_subcommands,
            ),
            dict(
                name = 'default',
                desc = dict(help = 'Set project for default. If default project is specified, the \'project\' parameter in the generic commands is optional'),
                arguments = [
                    dict(arg_name = 'name', help = 'project name', choices = project_names),
                ]
            ),
        ]
    ),
    dict(
        name = 'repository',
        desc = dict(help = 'Repository manager commands'),
        subcommands = [
            dict(
                name = 'create',
                desc = dict(help = 'Create repository'),
                arguments = [
                    dict(arg_name = 'path', help = 'Repository path'),
                    dict(arg_name = '--name', help = 'Repository name. Default: repo dir name', default = None),
                    dict(arg_name = '--type', help = 'Repository type', choices = ['git'], default = 'git'),
                    dict(arg_name = '--add-to', help = 'add to projects', choices = project_names, nargs = '*'),
                ]
            ),
            dict(
                name = 'cmd',
                desc = dict(help = 'Execute a command on a repository'),
                arguments = [
                    dict(arg_name = 'name', help = 'repository name', choices = repository_names),
                    dict(arg_name = 'command', help = 'command and params'),
                ]
            ),
            dict(
                name = 'remove',
                desc = dict(help = 'Remove repository'),
                arguments = [
                    dict(arg_name = 'name', help = 'repository name', choices = repository_names),
                ]
            ),
            dict(
                name = 'list',
                desc = dict(help = 'List of repositories'),
            ),
        ]
    ),
    dict(
        name = 'version',
        desc = dict(help = 'Show VCP version'),
    ),
]

# refactor this "if-else" to a cycle if there is a 3rd source
if 'VCP_DEFAULT_PROJECT' in os.environ:
    default_project  = os.environ.get('VCP_DEFAULT_PROJECT', None)
    logger.debug("Default project set to '%s' by environment" % default_project)
else:
    default_project = vcp.default_project
    logger.debug("Default project set to '%s' by config" % default_project)

if default_project not in project_names:
    logger.warning("Default project '%s' is unknown! Ignored." % default_project)
    default_project = None

# add project name parameter for project_action_commands
for command in project_action_commands:
    project_param = dict(arg_name = 'name', help = 'project name', choices = project_names)
    if default_project:
        project_param.update(nargs = '?', default = default_project)
    if 'arguments' not in command:
        command['arguments'] = []
    command['arguments'].insert(0, project_param)
    command['arguments'].append(dict(arg_name = '--list', help = 'Print only repo names', action = 'store_true'))

vcp.action_commands_lookup(project_action_commands)

parser = CLIArgumentsTreeParser(project_action_commands + manager_commands, 'vcp', argparse.ArgumentParser())
parser.build()
argcomplete.autocomplete(parser.parser, exclude = ['-h', '--help'])
parser.parser.add_argument('--debug', action = 'store_true')
data = parser.parse()

debug = data['sub']['args']['debug']
del data['sub']['args']['debug']

logger.debug("Parsed command line data: %s" %  data)

def fetch(arg_data, handler):
    if 'sub' in arg_data:
        fetch(arg_data['sub'], getattr(handler, arg_data.name)())
    elif 'args' in arg_data:
        getattr(handler, arg_data.name)(**arg_data['args'])
    else:
        getattr(handler, arg_data.name)()

try:
    fetch(data['sub'], vcp)
except Exception as e:
    raise
