#!/usr/bin/env python3

from gitz.git import GIT, functions, root
from gitz.program import ARGS, PROGRAM
from pathlib import Path
import webbrowser

SUMMARY = 'Open a browser page for the current repo'

HELP = """
Usage:

  ``git go [<location>]``

where <location> is one or more letters from:

* between: open *all* commits between HEAD and main or master

* commits: the list of commits for the current branch (the default)

* directory: the subdirectory in the current branch

* files : open the source for specific files

* history: open *multiple* historical versions of a file

* issues: the issues page

* new_releases: a form to create new releases

* pull: open the form for a pull request

* releases: a list of existing releases

* source: the root directory for the branch

* travis: the Travis page for that repo
"""

EXAMPLES = """

git go

"""

GITHUB = {
    'between': 'https://{host}/{user}/{project}/commits/{branch}',
    'commits': 'https://{host}/{user}/{project}/commits/{branch}',
    'directory': 'https://{host}/{user}/{project}/tree/{branch}{path}',
    'files': 'https://{host}/{user}/{project}/blob/{branch}{path}/{{file}}',
    'history': (
        'https://{host}/{user}/{project}/blob/{{commit_id}}{path}/{{file}}'
    ),
    'issues': 'https://{host}/{user}/{project}/issues',
    'new_releases': 'https://{host}/{user}/{project}/releases/new',
    'pulls': (
        'https://{host}/{up}/{project}/compare/main...{user}:{branch}?expand=1'
    ),
    'releases': 'https://{host}/{user}/{project}/releases',
    'source': 'https://{host}/{user}/{project}/tree/{branch}',
    'travis': 'https://travis-ci.com/{host_name}/{user}/{project}',
}


URLS = {'github.com': GITHUB}


def open_url(url, new=0):
    webbrowser.open(url, new=new, autoraise=True)


def git_go():
    host, host_name, user, project = _get_remote('origin')
    urls = URLS[host]

    cmd = ARGS.cmd
    cmds = {c for c in urls if c.startswith(cmd)}
    if cmds:
        cmd = cmds.pop()
        if cmds:
            PROGRAM.exit('Ambiguous command', ARGS.cmd, 'from', cmd, *cmds)

    elif Path(cmd).exists():
        ARGS.files = cmd, *ARGS.files
        cmd = 'files'

    else:
        PROGRAM.exit('Command', cmd, 'is neither a command nor a file')

    url = urls[cmd]

    if '{branch}' in url:
        branch = functions.branch_name()

    if '{{file}}' in url:
        if not ARGS.files:
            PROGRAM.exit('`file` argument must be set')

        bad_files = [f for f in ARGS.files if not Path(f).exists()]
        if bad_files:
            PROGRAM.exit('Cannot find', *bad_files)

    if '{path}' in url:
        path = str(Path().absolute().relative_to(root.root()))
        path = '' if path == '.' else '/' + path

    if '{up}' in url:
        upstream = _get_remote('upstream')
        up = upstream and upstream[2] or user

    url = url.format(**locals())

    if cmd == 'between':
        urls = _between(url)

    elif cmd == 'history':
        urls = _history(url)

    elif cmd == 'files':
        urls = _files(url)

    else:
        urls = [url]

    for u in urls:
        open_url(u)


def _between(url):
    commit_ids = _commits()
    if not commit_ids:
        PROGRAM.exit('No commit IDs found')

    root, _ = url.rsplit('/', maxsplit=1)
    assert root.endswith('commits')
    root = root[:-1]

    open_url(url, new=1)
    for i, c in enumerate(reversed(commit_ids)):
        PROGRAM.log.message('Opening', u)
        index = len(commit_ids) - i - 1
        name = f'HEAD@{index}' if index else 'HEAD'
        yield f'{root}/{c}'


def _files(url):
    for file in ARGS.files:
        u = url.format(file=file)

        # Special case for opening '.' at the root!  :-)
        # We get https://github.com/rec/engora-search/blob/wsgi/
        # but we want https://github.com/rec/engora-search/tree/wsgi

        parts = u.split('/')
        if parts[-1] == '.':
            parts.pop()
            # https, b, host, user, project, blob, branch: 7
            if len(parts) == 7:
                parts[5] = 'tree'
            u = '/'.join(parts)

        print('Opening', u)
        yield u


def _history(url):
    def git_log_ids(*args):
        commits = GIT.log('--oneline', *args)
        return [c.split()[0] for c in commits if c]

    if '.' in ARGS.files:
        PROGRAM.exit('Cannot take history of .')

    for file in ARGS.files:
        args = (f'-{ARGS.count}',) if ARGS.count else ()
        history_ids = git_log_ids(*args, '--follow', '--', file)
        _commits_message(history_ids, f'for {file}')

        if not history_ids:
            PROGRAM.exit(f'No commits for {file}')

        if ARGS.count:
             history_ids = history_ids[:ARGS.count]

        hie = history_ids[-1]
        all_ids = git_log_ids(f'{hie}..HEAD') + [hie]
        id_to_index = {id: i for i, id in enumerate(all_ids)}

        for commit_id in reversed(history_ids):
            index = id_to_index[commit_id]
            name = f'HEAD@{index}' if index else 'HEAD'
            PROGRAM.log.message(f'Opening {commit_id} ({name})')
            yield url.format(**locals())


def _commits_message(r, message):
    if r:
        s = (len(r) != 1) * 's'
        PROGRAM.log.message(f'Opening {len(r)} commit{s} {message}')
    return r


def _commits():
    for branch in 'main', 'master':
        try:
            r = GIT.rev_list('--ancestry-path', f'{branch}..HEAD')
        except Exception:
            continue
        return _commits_message(r, f'{branch}..HEAD')
    return []


def _get_remote(name):
    origin = GIT.config('--get', f'remote.{name}.url')
    origin = origin and origin[0]
    if not origin:
        return

    prefix = next(p for p in ('git@', 'https://') if origin.startswith(p))
    origin = origin[len(prefix) :]
    host, user, project_git = origin.replace(':', '/').split('/')
    host_name, *_ = host.split('.')
    project, g = project_git.split('.')
    assert g == 'git'

    return host, host_name, user, project


def add_arguments(parser):
    parser.add_argument('cmd', nargs='?', default='commits', help=_HELP_CMD)
    parser.add_argument('files', nargs='*', default=(), help=_HELP_FILE)
    parser.add_argument('--count', '-c', type=int, default=8, help=_HELP_COUNT)


_HELP_CMD = 'Command to execute - choose from ' + ' '.join(URLS['github.com'])
_HELP_COUNT = (
    'Count of versions to display, 0 means all (for `git open history`)'
)
_HELP_FILE = 'Zero or more files (for `git open file` or `history`)'

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