#!/usr/bin/env python3

from gitz import git_functions as gf
from gitz.program import PROGRAM
from gitz.program import git
from gitz.program import run
from pathlib import Path
import os
import sys

SUMMARY = 'Perform a command on each of multiple branches or directories'
USAGE = 'git all [name ...] - <command> [argument ...]'
HELP = """
Performs <command> [argument ...] for each `name`, or over all
branches if no `name` is given.

Note that this does not handle aliases within commands and might do
unexpected things with complex commands.  Please handle with care.
"""
EXAMPLES = """
git all - git log --oneline -5
    Performs git log --oneline -5 for each branch in this repo

git all * - git all - git log --oneline -5
    Performs git log --oneline -5 for each branch in each
    directory in the current directory
"""
DANGER = 'This is a bit janky'


def git_all():
    if not _COMMAND:
        PROGRAM.error(_ERROR_MISSING_COMMAND)
        print(PROGRAM.usage, file=sys.stderr)
        PROGRAM.exit()

    args = PROGRAM.args
    branches = gf.branches() if gf.find_git_root() else []
    names = args.name or branches

    if not args.all:
        names = [n for n in names if not n.startswith('.')]

    if set(branches).intersection(names):
        gf.check_clean_workspace()

    errors, names = [], []
    for name in args.name or branches:
        p = _expand_path(name)
        if name in branches:
            names.append(name)
        elif not p.is_dir():
            errors.append(str(p))
        elif not p.name.startswith('.'):
            if args.all or gf.find_git_root(p):
                names.append(str(p))

    if errors:
        errors = '"%s"' % '", "'.join(errors)
        PROGRAM.exit(_ERROR_BAD_NAME % errors)

    for name in names:
        try:
            _run_command(args, name, branches)
        except Exception as e:
            PROGRAM.error('Exception', e, 'for name', name)
            if args.fail:
                PROGRAM.exit()


def _expand_path(s):
    return Path(os.path.expandvars(s)).expanduser().resolve()


def _run_command(args, name, branches):
    if name in branches:
        if not args.quiet:
            print('Branch %s:' % name)
        saved_branch = gf.branch_name()
        git.checkout('-q', name)
        try:
            lines = run(*_COMMAND)
        finally:
            git.checkout('-q', saved_branch)
    else:
        name = _expand_path(name)
        if name.name.startswith('.'):
            return
        if not (args.all or gf.find_git_root(name)):
            return
        if not args.quiet:
            print('Directory %s:' % name)
        lines = run(*_COMMAND, cwd=str(name))
    indent = ' ' * args.indent
    for line in lines:
        print(indent, line, sep='')
    if not args.quiet:
        print()


def _pre():
    global _COMMAND
    argv = PROGRAM.argv
    dash = argv.index('-') if '-' in argv else len(argv)
    PROGRAM.argv, _COMMAND = argv[:dash], argv[dash + 1 :]


def add_arguments(parser):
    add = parser.add_argument
    add('name', nargs='*', help=_HELP_NAME)
    add('-a', '--all', action='store_true', help=_HELP_ALL)
    add('-f', '--fail', action='store_true', help=_HELP_FAIL)
    add('-i', '--indent', default=2, type=int, help=_HELP_INDENT)


_COMMAND = None

_ERROR_BAD_NAME = '%s: neither branch nor git repository'
_ERROR_MISSING_COMMAND = 'No command found'

_HELP_ALL = 'Visit non-git directories'
_HELP_FAIL = 'Fail immediately if any git-all command fails'
_HELP_INDENT = 'Number of columns to indent output of commands'
_HELP_NAME = 'Names of branches or directories to iterate over'
_HELP_QUIET = 'Print only command output and errors, no git or git-all output'
_HELP_VERBOSE = 'Echo all git commands and their responses'


if __name__ == '__main__':
    _pre()
    PROGRAM.start(**globals())
