"""Updates the contributors-with-label-access team.

This team exists because we need a team to manage triage access to repos;
GitHub doesn't allow the org to be set to triage access, only read/write. It
will be updated to include all members of the carbon-language organization.
"""

__copyright__ = """
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""

import argparse

# https://github.com/PyGithub/PyGithub
# GraphQL is preferred, but falling back to pygithub for unsupported mutations.
import github

from carbon.github_tools import github_helpers

# The organization to mirror members from.
_ORG = "carbon-language"

# The team to mirror to.
_TEAM = "contributors"

# Accounts in the org to skip mirroring.
_IGNORE_ACCOUNTS = ("CarbonLangInfra", "google-admin", "googlebot")

# Queries organization members.
_ORG_MEMBER_QUERY = """
query {
  organization(login: "%s") {
    membersWithRole(first: 100%%(cursor)s) {
      nodes {
        login
      }
      %%(pagination)s
    }
  }
}
"""

# The path for nodes in _ORG_MEMBER_QUERY.
_ORG_MEMBER_PATH = ("organization", "membersWithRole")

# Queries team members.
_TEAM_MEMBER_QUERY = """
query {
  organization(login: "%s") {
    team(slug: "%s") {
      members(first: 100%%(cursor)s) {
        nodes {
          login
        }
        %%(pagination)s
      }
    }
  }
}
"""

# The path for nodes in _TEAM_MEMBER_QUERY.
_TEAM_MEMBER_PATH = ("organization", "team", "members")


def _parse_args(args=None):
    """Parses command-line arguments and flags."""
    parser = argparse.ArgumentParser(description=__doc__)
    github_helpers.add_access_token_arg(parser, "admin:org, repo")
    return parser.parse_args(args=args)


def _load_org_members(client):
    """Loads org members."""
    print("Loading %s..." % _ORG)
    org_members = set()
    ignored = set()
    for node in client.execute_and_paginate(
        _ORG_MEMBER_QUERY % _ORG, _ORG_MEMBER_PATH
    ):
        login = node["login"]
        if login not in _IGNORE_ACCOUNTS:
            org_members.add(login)
        else:
            ignored.add(login)
    print(
        "%s has %d non-ignored members, and %d ignored."
        % (_ORG, len(org_members), len(ignored))
    )
    unignored = set(_IGNORE_ACCOUNTS) - ignored
    assert not unignored, "Missing ignored accounts: %s" % unignored
    return org_members


def _load_team_members(client):
    """Load team members."""
    print("Loading %s..." % _TEAM)
    team_members = set()
    for node in client.execute_and_paginate(
        _TEAM_MEMBER_QUERY % (_ORG, _TEAM), _TEAM_MEMBER_PATH
    ):
        team_members.add(node["login"])
    print("%s has %d members." % (_ORG, len(team_members)))
    return team_members


def _update_team(gh, org_members, team_members):
    """Updates the team if needed.

    This switches to pygithub because GraphQL lacks equivalent mutation support.
    """
    gh_team = gh.get_organization(_ORG).get_team_by_slug(_TEAM)
    add_members = org_members - team_members
    if add_members:
        print("Adding members: %s" % ", ".join(add_members))
        for member in add_members:
            gh_team.add_membership(gh.get_user(member))

    remove_members = team_members - org_members
    if remove_members:
        print("Removing members: %s" % ", ".join(remove_members))
        for member in remove_members:
            gh_team.remove_membership(gh.get_user(member))


def main():
    parsed_args = _parse_args()
    print("Connecting...")
    client = github_helpers.Client(parsed_args)

    org_members = _load_org_members(client)
    team_members = _load_team_members(client)
    if org_members != team_members:
        gh = github.Github(parsed_args.access_token)
        _update_team(gh, org_members, team_members)
    print("Done!")


if __name__ == "__main__":
    main()
