import argparse
import logging
import os
import pygit2
import re
from subprocess import CalledProcessError
import sys
from gitubuntu.__main__ import top_level_defaults
from gitubuntu.git_repository import (
    GitUbuntuRepository,
    GitUbuntuRepositoryFetchError,
    GitUbuntuChangelogError,
)
from gitubuntu.run import decode_binary, run


def parse_args(subparsers=None, base_subparsers=None):
    kwargs = dict(
        description='Add a launchpad user\'s repository as a remote',
        formatter_class=argparse.RawTextHelpFormatter)
    if base_subparsers:
        kwargs['parents'] = base_subparsers
    if subparsers:
        parser = subparsers.add_parser('remote', **kwargs)
        parser.set_defaults(func=cli_main)
    else:
        parser = argparse.ArgumentParser(**kwargs)
    known_subcommands = {
        'add': 'Add a launchpad user\'s repository as a remote',
    }
    subsubparsers = parser.add_subparsers(
        dest='subsubcommand',
        help='',
        # this will look off until more than one subcommand exists
        metavar='%s' % '|'.join(sorted(known_subcommands.keys())),
    )
    add_parser = subsubparsers.add_parser(
        'add',
        help=known_subcommands['add']
    )
    add_parser.add_argument(
        'user',
        type=str,
        help="Launchpad user to add as a remote. If 'debian', parse "
             "pkg/ubuntu/devel:debian/control for either "
             "X-Debian-Vcs-Git or Vcs-Git. If found, add it as a remote "
             "named 'debian'.",
    )
    add_parser.add_argument(
        'url',
        type=str,
        help="Specify URL to add as a remote. If not specified, a URL is "
             "derived from USER and the repository source package.",
        default=None,
        nargs='?',
    )
    add_parser.add_argument(
        '--directory',
        type=str,
        help="Local git directory to modify. If not specified, the "
             "current directory is assumed.",
    )
    add_parser.add_argument(
        '-l', '--lp-user',
        type=str,
        help=argparse.SUPPRESS
    )
    add_parser.add_argument(
        '-r',  '--remote-name',
        type=str,
        help="Specify remote name. If not specified the remote will be "
             "named after the user argument.",
    )
    add_parser.add_argument(
        '--no-fetch',
        action='store_true',
        help="Do not fetch the remote after adding",
    )
    add_parser.add_argument(
        '--package',
        type=str,
        help="Specify the source package rather than automatically "
             "determining it from debian/changelog.",
    )
    if not subparsers:
        return parser.parse_args()
    return "remote - %s" % kwargs['description']

def cli_main(args):
    if args.directory is not None:
        directory = os.path.abspath(args.directory)
    else:
        directory = os.getcwd()

    return main(
        args.subsubcommand,
        args.user,
        args.package,
        args.url,
        args.lp_user,
        directory,
        args.remote_name,
        args.no_fetch,
        args.proto,
    )

def do_add_debian(repo, package, no_fetch=False):
    """add a Debian remote to a repository

    Parses debian/control in pkg/ubuntu/devel, and uses the value of:
    1) X-Debian-Vcs-Git
    or
    2) Vcs-Git, only if there is no X-Debian-Vcs-Git

    as the URL for a remote named Debian.

    @repo: GitUbuntuRepository to modify
    @package: source package name (XXX: is this derive-able from @repo?)
    @no_fetch: if True, do not fetch the remote after adding it
    """
    remote_name = 'debian'

    control_file = None
    try:
        control_file = repo.extract_file_from_treeish(
            treeish_string='pkg/ubuntu/devel',
            filename='debian/control',
        )
        control_file.seek(0)
        x_debian_vcs_git = None
        vcs_git = None
        for line in control_file:
            line = decode_binary(line)
            if line.startswith('X-Debian-Vcs-Git:'):
                _, x_debian_vcs_git = line.split(':', 1)
                x_debian_vcs_git = x_debian_vcs_git.strip()
                break
            if line.startswith('Vcs-Git:'):
                _, vcs_git = line.split(':', 1)
                vcs_git = vcs_git.strip()

        if not x_debian_vcs_git and not vcs_git:
            logging.error(
                "Unable to find any Vcs metadata in "
                "pkg/ubuntu/devel:debian/control."
            )
            return 1

        url = x_debian_vcs_git if x_debian_vcs_git else vcs_git
        repo.add_remote_by_url(remote_name, url)
    finally:
        if control_file:
            control_file.close()

    if not no_fetch:
        try:
            repo.fetch_remote(remote_name, verbose=True)
        except GitUbuntuRepositoryFetchError:
            pass

    logging.debug(
        "added remote '%s' -> %s",
        remote_name,
        repo.raw_repo.remotes[remote_name].url,
    )

    return 0

def do_add(repo, package, user, url=None, remote_name=None, no_fetch=False):
    """add a remote to a repository

    @repo: GitUbuntuRepository to modify
    @package: source package name (XXX: is this derive-able from @repo?)
    @user: remote user's repository to add as remote
    @url: URL to add as a remote
    @remote_name: specify the name of the new remote
    @no_fetch: if True, do not fetch the remote after adding it

    If url is None, the remote URL will be derived from @package and
    @user.

    If @remote_name is not specified, it will be set to @user.
    """
    if remote_name is None:
        remote_name = user

    if url:
        repo.add_remote_by_url(remote_name, url)
    else:
        repo.add_remote(
            pkgname=package,
            repo_owner=user,
            remote_name=remote_name,
        )

    if not no_fetch:
        try:
            repo.fetch_remote(remote_name, verbose=True)
        except GitUbuntuRepositoryFetchError:
            pass

    logging.debug(
        "added remote '%s' -> %s",
        remote_name,
        repo.raw_repo.remotes[remote_name].url,
    )

    return 0

def main(
    subcommand,
    user,
    package=None,
    url=None,
    lp_user=None,
    directory=None,
    remote_name=None,
    no_fetch=False,
    proto=top_level_defaults.proto,
):
    """Entry point to remote subcommand
    @subcommand: string action to take. Currently only 'add' supported.
    @user: string Launchpad user's repository to add
    @package: string name of source package
    @url: the string URL of the remote to add
    @lp_user: string user to authenticate to Launchpad as
    @directory: directory containing git repository to modify
    @remote_name: name of remote
    @no_fetch: do not fetch remote after modifying it
    @proto: string protocol to use (one of 'http', 'https', 'git')

    If package is None, it will be derived from HEAD's debian/changelog.

    If url is None, it will be derived from @user and @package.

    If lp_user is None, value of `git config gitubuntu.lpuser` will be
    used.

    If directory is None, the current directory is used.

    If remote_name is None, the remote will be named @user.

    Returns 0 if the subcommand succeeded; 1 otherwise.
    """
    if directory is None:
        directory = os.path.abspath(os.getcwd())

    repo = GitUbuntuRepository(
        local_dir=directory,
        lp_user=lp_user,
        fetch_proto=proto
    )

    try:
        repo_pkg = repo.get_changelog_srcpkg_from_treeish('HEAD')
    except GitUbuntuChangelogError:
        repo_pkg = None

    if package is not None:
        if repo_pkg is not None and repo_pkg != package:
            logging.warning(
                "Specified sourcepackage (%s) does not match "
                "debian/changelog (%s).",
                package,
                repo_pkg,
            )
    else:
        if repo_pkg:
            package = repo_pkg
        else:
            logging.error(
                "Unable to determine source package name. Does "
                "debian/changelog exist in the current branch?"
            )
            return 1

    if subcommand == 'add':
        if user == 'debian':
            return do_add_debian(repo, package, no_fetch)
        else:
            return do_add(repo, package, user, url, remote_name, no_fetch)

    return 1

# vi: ts=4 expandtab
