#!/usr/bin/env python3

from gitz.git import GIT
from gitz.git import functions
from gitz.git import root
from gitz.program import ARGS
from gitz.program import PROGRAM
from gitz.program import RUN
from pathlib import Path
import os
import sys

SUMMARY = 'Perform a command for each branch or directory'

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'

USAGE = """
USAGE
    git all - <command> [argument argument...]
"""


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

    branches = functions.branches() if root.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):
        root.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 root.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(name, branches)
        except Exception as e:
            PROGRAM.error('Exception', e, 'for name', name)
            if ARGS.fail:
                PROGRAM.exit()


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


def _run_command(name, branches):
    if name in branches:
        if not ARGS.quiet:
            print('Branch %s:' % name)
        saved_branch = functions.branch_name()
        GIT.checkout('-q', name)
        try:
            lines = RUN(*_COMMAND)
        finally:
            GIT.checkout('-q', saved_branch)
    else:
        name = str(_expand_path(name))
        if name.startswith('.'):
            return
        if not (ARGS.all or root.root(name)):
            return
        if not ARGS.quiet:
            print('Directory %s:' % name)
        lines = RUN(*_COMMAND, cwd=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-for-each command fails'
_HELP_INDENT = 'Number of columns to indent output of commands'
_HELP_NAME = 'Names of branches or directories to iterate over'


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