#!/usr/bin/python -t
#
# Create an /etc/gitolite/conf/gitolite.conf file with acls for dist-git
#
# Takes no arguments!
#

import copy
import grp
import os
import sys
import json

import requests
from sqlalchemy.exc import SQLAlchemyError


if 'PAGURE_CONFIG' not in os.environ \
        and os.path.exists('/etc/pagure/pagure.cfg'):
    print 'Using configuration file `/etc/pagure/pagure.cfg`'
    os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg'

import pagure
from pagure import SESSION

TESTING = False

{% if env == 'staging' %}
VCS_URL = 'https://admin.stg.fedoraproject.org/pkgdb/api/vcs?format=json'
GRP_URL = 'https://admin.stg.fedoraproject.org/pkgdb/api/groups?format=json'
{% else %}
VCS_URL = 'https://admin.fedoraproject.org/pkgdb/api/vcs?format=json'
GRP_URL = 'https://admin.fedoraproject.org/pkgdb/api/groups?format=json'
{% endif %}


def create_user_obj(username):
    ''' Creates a sqlalchemy user object for pagure db '''
    try:
        user = pagure.lib.set_up_user(
            SESSION,
            username,
            username,
            '%s@fedorahosted.org' % username
        )
        SESSION.commit()
    except SQLAlchemyError:
        SESSION.rollback()
        if TESTING:
            print 'Creating user failed'

    return user


def create_groups_in_db(groups):
    ''' Creates groups in pagure db '''

    group_keys = groups.keys()
    for groupname in group_keys:

        # we don't need to do anything with empty groups, do we?
        if len(groups[groupname]) == 0:
            continue

        # first insure the users in the groups are present in the db
        group_users = groups[groupname]
        for guser in group_users:
            user_obj = pagure.lib.search_user(SESSION, username=guser)
            if not user_obj:
                user_obj = create_user_obj(guser)

        # check if the groups are present in the db
        group_obj = pagure.lib.search_groups(SESSION, group_name=groupname)
        if not group_obj:
            # add the group to the db using the first user in the group
            try:
                pagure.lib.add_group(
                    SESSION, groupname, 'user',
                    groups[groupname][0], False,
                    pagure.APP.config['BLACKLISTED_GROUPS'])
                SESSION.commit()
            except SQLAlchemyError:
                SESSION.rollback()
                if TESTING:
                    print 'Adding a user to group failed'

        # now that all groups are present in the db
        # ensure all the members are there in the group in the db
        for guser in group_users:
            if not pagure.lib.is_group_member(SESSION, guser, groupname):
                group_obj = pagure.lib.search_groups(
                    SESSION, group_name=groupname)
                try:
                    msg = pagure.lib.add_user_to_group(
                        SESSION,
                        guser,
                        group_obj,
                        groups[groupname][0],
                        False
                    )
                    SESSION.commit()
                except SQLAlchemyError:
                    SESSION.rollback()
                    if TESTING:
                        print 'Adding a user to group failed'


def update_owners_to_db(pkg, owners):
    ''' Adds owners to pagure db '''

    for owner in owners:
        # check if the owners are present in the db if not create them
        owner_obj = pagure.lib.search_user(SESSION, username=owner)
        if not owner_obj:
            owner_obj = create_user_obj(owner)

        # check if the repo exists, if not create
        pkg_obj = pagure.lib.get_project(SESSION, name='rpms/%s' % pkg)

        # this flag is for avoiding unnecessary db queries
        flag = True
        if not pkg_obj:
            pkgname = pkg
            try:
                msg = pagure.lib.new_project(
                    SESSION,
                    owner,
                    'rpms/%s' % pkgname,
                    pagure.APP.config['BLACKLISTED_PROJECTS'],
                    pagure.APP.config['ALLOWED_PREFIX'],
                    pagure.APP.config['GIT_FOLDER'],
                    pagure.APP.config['DOCS_FOLDER'],
                    pagure.APP.config['TICKETS_FOLDER'],
                    pagure.APP.config['REQUESTS_FOLDER'],
                )
                SESSION.commit()
                flag = False
            except SQLAlchemyError:
                SESSION.rollback()
                if TESTING:
                    print "Couldn't create project"

        # so now the pkg surely exists, make the owner, the owner of the repo
        # if (s)he is not
        if not flag:
            pkg_obj = pagure.lib.get_project(SESSION, name='rpms/%s' % pkg)

        if owner_obj not in pkg_obj.users and owner_obj is not pkg_obj.user:
            try:
                msg = pagure.lib.add_user_to_project(
                    SESSION,
                    pkg_obj,
                    owner_obj.user,
                    pkg_obj.user.user,
                )
                SESSION.commit()
            except SQLAlchemyError:
                SESSION.rollback()
                if TESTING:
                    print "Couldn't add user to project"


def update_groups_to_db(pkg, pkg_groups):
    ''' Adds groups to projects in pagure db '''

    for group in pkg_groups:
        # we have already created all the groups
        group_obj = pagure.lib.search_groups(SESSION, group_name=group)
        pkg_obj = pagure.lib.get_project(SESSION, name='rpms/%s' % pkg)

        # In case when there are only groups with commit access and no people
        # The below flag is for cutting out db queries later
        flag = True
        if not pkg_obj:
            pkgname = pkg
            try:
                msg = pagure.lib.new_project(
                    SESSION,
                    group_obj.creator.user,
                    'rpms/%s' % pkgname,
                    pagure.APP.config['BLACKLISTED_PROJECTS'],
                    pagure.APP.config['ALLOWED_PREFIX'],
                    pagure.APP.config['GIT_FOLDER'],
                    pagure.APP.config['DOCS_FOLDER'],
                    pagure.APP.config['TICKETS_FOLDER'],
                    pagure.APP.config['REQUESTS_FOLDER'],
                )
                SESSION.commit()
                flag = False
            except SQLAlchemyError:
                SESSION.rollback()
                if TESTING:
                    print "Couldn't create project"

        # for the case when the new project was just created by the above call
        if not flag:
            pkg_obj = pagure.lib.get_project(SESSION, name='rpms/%s' % pkg)

        # if the group was initially empty, it was not created in the db
        if not group_obj:
            continue

        # check if the group is added to project if not, add them
        if group_obj not in pkg_obj.groups:
            group_creator = group_obj.creator
            try:
                pagure.lib.add_group_to_project(
                    SESSION,
                    pkg_obj,
                    group,
                    pkg_obj.user.user,
                )
                SESSION.commit()
            except SQLAlchemyError as err:
                SESSION.rollback()
                if TESTING:
                    print "Adding a group to a project failed"


if __name__ == '__main__':

    TRUSTED = grp.getgrnam('cvsadmin')[3]
    ARM = grp.getgrnam('fedora-arm')[3]
    SPARC = grp.getgrnam('fedora-sparc')[3]
    IA64 = grp.getgrnam('fedora-ia64')[3]
    S390 = grp.getgrnam('fedora-s390')[3]
    PPC = grp.getgrnam('fedora-ppc')[3]
    PROVEN = grp.getgrnam('provenpackager')[3]

    # Set the active branches to create ACLs for
    # Give them the git branch eqiv until pkgdb follows suite
    ACTIVE = {
        'OLPC-2': 'olpc2', 'OLPC-3': 'olpc3', 'EL-4': 'el4',
        'EL-5': 'el5', 'el5': 'el5', 'el6': 'el6', 'EL-6': 'el6',
        'epel7': 'epel7',
        'F-11': 'f11', 'F-12': 'f12', 'F-13': 'f13', 'f14': 'f14', 'f15':
        'f15', 'f16': 'f16', 'f17': 'f17', 'f18': 'f18', 'f19': 'f19',
        'f20': 'f20', 'f21': 'f21', 'f22': 'f22', 'f23': 'f23', 'f24': 'f24',
        'f25': 'f25',
        'devel': 'master', 'master': 'master'}

    # Create a "regex"ish list 0f the reserved branches
    RESERVED = [
        'f[0-9][0-9]', 'epel[0-9]', 'epel[0-9][0-9]', 'el[0-9]',
        'olpc[0-9]']

    # print out our user groups
    print '@admins = %s' % ' '.join(TRUSTED)
    print '@provenpackager = %s' % ' '.join(PROVEN)
    print '@fedora-arm = %s' % ' '.join(ARM)
    print '@fedora-s390 = %s' % ' '.join(S390)
    print '@fedora-ppc = %s' % ' '.join(PPC)

    groups = {
        'admins': TRUSTED,
        'fedora-arm': ARM,
        'SPARC': SPARC,
        'IA65': IA64,
        'fedora-s390': S390,
        'fedora-ppc': PPC,
        'provenpackager': PROVEN
    }

    # Get a list of all the groups
    groups_ = requests.get(GRP_URL).json()
    for group in groups_['groups']:
        print '@%s = %s' % (group, ' '.join(grp.getgrnam(group)[3]))
        gmems = grp.getgrnam(group)[3]
        if group not in groups.keys():
            groups[group] = gmems
        elif groups[group] != gmems:
            groups[group] = gmems

    # <pagure db create groups>
    create_groups_in_db(groups)
    # </pagure db>

    data = requests.get(VCS_URL).json()

    # Give a little space before moving onto the permissions
    print ''
    # print our default permissions
    print 'repo @all'
    print '    -   VREF/update-block-push-origin = @all'
    print '    RWC = @admins @fedora-arm @fedora-s390 @fedora-ppc'
    print '    R = @all'
    #print '    RW  private-     = @all'
    # dont' enable the above until we prevent building for real from private-

    # XXX - Insert artificial namespaces into the set of namespaces returned
    # by pkgdb.  We want to create a mirror of rpms/PKG in test-rpms/PKG
    # This hack occurs in two places.  Here, and in the branch-creation script.
    # https://github.com/fedora-infra/pkgdb2/issues/329#issuecomment-207050233
    # And then, this got renamed from rpms-checks to test-rpms
    # https://pagure.io/fedora-infrastructure/issue/5570
    if 'rpms' in data:
        data['test-rpms'] = copy.copy(data['rpms'])
    # Also, modules are a thing
    # https://pagure.io/fedora-infrastructure/issue/5571
    if 'modules' in data:
        data['test-modules'] = copy.copy(data['modules'])
    if 'docker' in data:
        data['test-docker'] = copy.copy(data['docker'])

    # Get a list of all the packages
    for key in data:
        if key == 'title':
            continue

        acls = data[key]
        pkglist = data[key].keys()
        pkglist.sort()

        if key != 'packageAcls':
            key = '%s/' % key
        else:
            key = ''

        for pkg in pkglist:

            branchAcls = {}  # Check whether we need to set separate per branch acls
            buffer = []  # Buffer the output per package
            masters = []  # Folks that have commit to master
            writers = []  # Anybody that has write access

            # Examine each branch in the package
            branches = acls[pkg].keys()
            branches.sort()
            for branch in branches:
                if branch not in ACTIVE.keys():
                    continue
                if 'packager' in acls[pkg][branch]['commit']['groups']:
                    # If the packager group is defined, everyone has access
                    buffer.append('    RWC   %s = @all' % (ACTIVE[branch]))
                    branchAcls.setdefault('@all', []).append(
                        (pkg, ACTIVE[branch])
                    )
                    if branch == 'master':
                        masters.append('@all')
                    if '@all' not in writers:
                        writers.append('@all')
                else:
                    # Extract the owners
                    committers = []
                    owners = acls[pkg][branch]['commit']['people']
                    owners.sort()
                    for owner in owners:
                        committers.append(owner)
                    for group in acls[pkg][branch]['commit']['groups']:
                        committers.append('@%s' % group)
                    if branch == 'master':
                        masters.extend(committers)

                        # <pagure db update groups and owner>
                        pkg_groups = acls[pkg][branch]['commit']['groups']
                        update_owners_to_db(pkg, owners)
                        update_groups_to_db(pkg, pkg_groups)
                        # </pagure db>

                    # add all the committers to the top writers list
                    for committer in committers:
                        if committer not in writers:
                            writers.append(committer)

                    # Print the committers to the acl for this package-branch
                    committers = ' '.join(committers)
                    buffer.append(
                        '    RWC   %s = %s' % (ACTIVE[branch], committers))
                    branchAcls.setdefault(committers, []).append(
                        (pkg, ACTIVE[branch])
                    )

            print ''
            print 'repo %s%s' % (key, pkg)
            print '\n'.join(buffer)
            for reserved in RESERVED:
                print '    -    %s = @all' % reserved
            print '    RWC  refs/tags/ = %s' % ' '.join(writers)
            if masters:
                print '    RWC      = %s' % ' '.join(masters)

    sys.exit(0)
