#!/usr/bin/python3
#
# This file is part of FreedomBox.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
"""
Configuration helper for Gitweb.
"""

import argparse
import configparser
import json
import os
import shutil
import subprocess

from plinth import action_utils
from plinth.modules.gitweb.forms import validate_repository
from plinth.modules.gitweb.manifest import GIT_REPO_PATH


def parse_arguments():
    """Return parsed command line arguments as dictionary."""
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')

    subparsers.add_parser(
        'setup', help='Perform post-installation operations for Gitweb')

    subparser = subparsers.add_parser('create-repo',
                                      help='Create a new repository')
    subparser.add_argument('--name', required=True,
                           help='Name of the repository')
    subparser.add_argument('--description', required=True,
                           help='Description of the repository')
    subparser.add_argument('--owner', required=True,
                           help='Repository’s owner name')
    subparser.add_argument(
        '--is-private', required=False, default=False, action='store_true',
        help='Allow only authorized users to access this repository')
    subparser.add_argument(
        '--keep-ownership', required=False, default=False, action="store_true",
        help='Do not chanege ownership of the repository directory')

    subparser = subparsers.add_parser(
        'repo-info', help='Get information about the repository')
    subparser.add_argument('--name', required=True,
                           help='Name of the repository')

    subparser = subparsers.add_parser('rename-repo',
                                      help='Rename an repository')
    subparser.add_argument('--oldname', required=True,
                           help='Old name of the repository')
    subparser.add_argument('--newname', required=True,
                           help='New name of the repository')

    subparser = subparsers.add_parser('set-repo-description',
                                      help='Set description of the repository')
    subparser.add_argument('--name', required=True,
                           help='Name of the repository')
    subparser.add_argument('--description', required=True,
                           help='Description of the repository')

    subparser = subparsers.add_parser('set-repo-owner',
                                      help='Set repository\'s owner name')
    subparser.add_argument('--name', required=True,
                           help='Name of the repository')
    subparser.add_argument('--owner', required=True,
                           help='Repository’s owner name')

    subparser = subparsers.add_parser(
        'set-repo-access', help='Set repository as private or public')
    subparser.add_argument('--name', required=True,
                           help='Name of the repository')
    subparser.add_argument('--access', required=True,
                           choices=['public', 'private'], help='Access status')

    subparser = subparsers.add_parser('delete-repo',
                                      help='Delete an existing repository')
    subparser.add_argument('--name', required=True,
                           help='Name of the repository to remove')

    subparsers.required = True
    return parser.parse_args()


def subcommand_setup(_):
    """Disable default Apache2 Gitweb configuration"""
    action_utils.webserver_disable('gitweb')


def _get_repo_description(repo):
    """Set description of the repository."""
    description_file = os.path.join(GIT_REPO_PATH, repo + '.git',
                                    'description')
    if os.path.exists(description_file):
        with open(description_file, 'r') as file_handle:
            description = file_handle.read()
    else:
        description = ''

    return description


def _set_repo_description(repo, description):
    """Set description of the repository."""
    description_file = os.path.join(GIT_REPO_PATH, repo + '.git',
                                    'description')
    with open(description_file, 'w') as file_handle:
        file_handle.write(description)


def _get_repo_owner(repo):
    """Set repository's owner name."""
    repo_config = os.path.join(GIT_REPO_PATH, repo + '.git', 'config')
    config = configparser.ConfigParser()
    config.read(repo_config)
    try:
        owner = config['gitweb']['owner']
    except KeyError:
        owner = ''

    return owner


def _set_repo_owner(repo, owner):
    """Set repository's owner name."""
    repo_config = os.path.join(GIT_REPO_PATH, repo + '.git', 'config')
    config = configparser.ConfigParser()
    config.read(repo_config)
    if not config.has_section('gitweb'):
        config.add_section('gitweb')

    config['gitweb']['owner'] = owner
    with open(repo_config, 'w') as file_handle:
        config.write(file_handle)


def _get_access_status(repo):
    """Get repository's access status"""
    private_file = os.path.join(GIT_REPO_PATH, repo + '.git', 'private')
    if os.path.exists(private_file):
        return 'private'

    return 'public'


def _set_access_status(repo, status):
    """Set repository as private or public"""
    private_file = os.path.join(GIT_REPO_PATH, repo + '.git', 'private')
    if status == 'private':
        open(private_file, 'a')
    elif status == 'public':
        if os.path.exists(private_file):
            os.remove(private_file)


def subcommand_rename_repo(arguments):
    """Rename a repository."""
    validate_repository(arguments.oldname)
    validate_repository(arguments.newname)
    oldpath = os.path.join(GIT_REPO_PATH, arguments.oldname + '.git')
    newpath = os.path.join(GIT_REPO_PATH, arguments.newname + '.git')
    os.rename(oldpath, newpath)


def subcommand_set_repo_description(arguments):
    """Set description of the repository."""
    validate_repository(arguments.name)
    _set_repo_description(arguments.name, arguments.description)


def subcommand_set_repo_owner(arguments):
    """Set repository's owner name."""
    validate_repository(arguments.name)
    _set_repo_owner(arguments.name, arguments.owner)


def subcommand_set_repo_access(arguments):
    """Set repository's access status."""
    validate_repository(arguments.name)
    _set_access_status(arguments.name, arguments.access)


def subcommand_repo_info(arguments):
    """Get information about repository."""
    validate_repository(arguments.name)
    repo_path = os.path.join(GIT_REPO_PATH, arguments.name + '.git')
    if not os.path.exists(repo_path):
        raise RuntimeError('Repository not found')

    print(
        json.dumps(
            dict(name=arguments.name, description=_get_repo_description(
                arguments.name), owner=_get_repo_owner(arguments.name),
                 access=_get_access_status(arguments.name))))


def subcommand_create_repo(arguments):
    """Create a new git repository."""
    validate_repository(arguments.name)
    repo_name = arguments.name + '.git'
    subprocess.check_call(['git', 'init', '--bare', repo_name],
                          cwd=GIT_REPO_PATH)
    if not arguments.keep_ownership:
        subprocess.check_call(['chown', '-R', 'www-data:www-data', repo_name],
                              cwd=GIT_REPO_PATH)
    _set_repo_description(arguments.name, arguments.description)
    _set_repo_owner(arguments.name, arguments.owner)
    if arguments.is_private:
        _set_access_status(arguments.name, 'private')


def subcommand_delete_repo(arguments):
    """Delete a git repository."""
    validate_repository(arguments.name)
    repo_path = os.path.join(GIT_REPO_PATH, arguments.name + '.git')
    shutil.rmtree(repo_path)


def main():
    """Parse arguments and perform all duties."""
    arguments = parse_arguments()

    subcommand = arguments.subcommand.replace('-', '_')
    subcommand_method = globals()['subcommand_' + subcommand]
    subcommand_method(arguments)


if __name__ == '__main__':
    main()
