#!/usr/bin/env python3

# Copyright (C) 2020-2021  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

import logging

from keycloak import KeycloakAdmin

SERVER_URL = "http://localhost:8080/keycloak/auth/"
REALM_NAME = "SoftwareHeritage"

CLIENT_WEBAPP_NAME = "swh-web"
CLIENT_DEPOSIT_NAME = "swh-deposit"

ADMIN = {"username": "admin", "password": "admin"}


logger = logging.getLogger(__name__)


def assign_client_base_url(keycloak_admin, client_name, base_url):
    client_data = {"baseUrl": base_url, "clientId": client_name}
    client_id = keycloak_admin.get_client_id(client_name)
    keycloak_admin.update_client(client_id, client_data)


def assign_client_role_to_user(keycloak_admin, client_name, client_role, username):
    client_id = keycloak_admin.get_client_id(client_name)
    user_role = keycloak_admin.get_client_role(client_id, client_role)
    user_id = keycloak_admin.get_user_id(username)
    keycloak_admin.assign_client_role(user_id, client_id, user_role)


def assign_realm_roles_to_user(keycloak_admin, realm_roles, username):
    roles = []
    for realm_role in realm_roles:
        roles.append(keycloak_admin.get_realm_role(realm_role))
    user_id = keycloak_admin.get_user_id(username)
    # due to a design bug in python-keycloak API, client_id parameter must
    # be provided while it is not used
    keycloak_admin.assign_realm_roles(user_id, client_id="", roles=roles)


def assign_client_roles_to_user(keycloak_admin, client_name, client_roles, username):
    for client_role in client_roles:
        assign_client_role_to_user(keycloak_admin, client_name, client_role, username)


def create_user(keycloak_admin, user_data):
    try:
        keycloak_admin.create_user(user_data)
    except Exception as e:
        logger.warning(f"User already created: {e}, skipping.")


def create_client_roles(keycloak_admin, client_name, client_roles):
    for client_role in client_roles:
        try:
            keycloak_admin.create_client_role(
                client_name, payload={"name": client_role}
            )
        except Exception as e:
            logger.warning(f"Client role already created: {e}, skipping.")


def create_realm_roles(keycloak_admin, realm_roles):
    for realm_role in realm_roles:
        try:
            keycloak_admin.create_realm_role(payload={"name": realm_role})
        except Exception as e:
            logger.warning(f"Realm role already created: {e}, skipping.")


# login as admin in master realm
KEYCLOAK_ADMIN = KeycloakAdmin(SERVER_URL, ADMIN["username"], ADMIN["password"])

# update master realm clients base urls as we use a reverse proxy
assign_client_base_url(
    KEYCLOAK_ADMIN, "account", "/keycloak/auth/realms/master/account"
)

assign_client_base_url(
    KEYCLOAK_ADMIN,
    "security-admin-console",
    "/keycloak/auth/admin/master/console/index.html",
)

KEYCLOAK_ADMIN.update_realm(
    "master", payload={"loginTheme": "swh", "accountTheme": "swh", "adminTheme": "swh",}
)

# create swh realm
KEYCLOAK_ADMIN.create_realm(
    payload={
        "id": REALM_NAME,
        "realm": REALM_NAME,
        "displayName": "Software Heritage",
        "rememberMe": True,
        "attributes": {"frontendUrl": "http://localhost:5080/keycloak/auth/"},
        "enabled": True,
        "loginTheme": "swh",
        "accountTheme": "swh",
        "adminTheme": "swh",
    },
    skip_exists=True,
)

# set swh realm name in order to create users in it
KEYCLOAK_ADMIN.realm_name = REALM_NAME

# update swh realm clients base urls as we use a reverse proxy
assign_client_base_url(
    KEYCLOAK_ADMIN, "account", f"/keycloak/auth/realms/{REALM_NAME}/account"
)

assign_client_base_url(
    KEYCLOAK_ADMIN,
    "security-admin-console",
    f"/keycloak/auth/admin/{REALM_NAME}/console/index.html",
)

# create an admin user in the swh realm
user_data = {
    "email": "admin@example.org",
    "username": ADMIN["username"],
    "firstName": ADMIN["username"],
    "lastName": ADMIN["username"],
    "credentials": [
        {"value": ADMIN["password"], "type": "password", "temporary": False}
    ],
    "enabled": True,
    "emailVerified": True,
}

create_user(KEYCLOAK_ADMIN, user_data)

# assign realm admin roles to created user
realm_management_roles = [
    "view-users",
    "view-events",
    "view-identity-providers",
    "manage-identity-providers",
    "create-client",
    "query-clients",
    "query-realms",
    "manage-events",
    "view-clients",
    "manage-realm",
    "impersonation",
    "manage-clients",
    "view-authorization",
    "query-users",
    "view-realm",
    "manage-authorization",
    "manage-users",
    "query-groups",
]

assign_client_roles_to_user(
    KEYCLOAK_ADMIN, "realm-management", realm_management_roles, ADMIN["username"]
)


# login as admin in swh realm
KEYCLOAK_ADMIN = KeycloakAdmin(
    SERVER_URL, ADMIN["username"], ADMIN["password"], REALM_NAME
)

for (client_name, redirect_uris) in [
    (CLIENT_WEBAPP_NAME, ["http://localhost:5004/*", "http://localhost:5080/*"]),
    (CLIENT_DEPOSIT_NAME, ["http://localhost:5006/*"]),
]:
    # create swh-web public client
    KEYCLOAK_ADMIN.create_client(
        payload={
            "id": client_name,
            "clientId": client_name,
            "surrogateAuthRequired": False,
            "enabled": True,
            "redirectUris": redirect_uris,
            "bearerOnly": False,
            "consentRequired": False,
            "standardFlowEnabled": True,
            "implicitFlowEnabled": False,
            "directAccessGrantsEnabled": True,
            "serviceAccountsEnabled": False,
            "publicClient": True,
            "frontchannelLogout": False,
            "protocol": "openid-connect",
            "fullScopeAllowed": True,
            "protocolMappers": [
                {
                    "name": "user groups",
                    "protocol": "openid-connect",
                    "protocolMapper": "oidc-group-membership-mapper",
                    "consentRequired": False,
                    "config": {
                        "full.path": True,
                        "userinfo.token.claim": True,
                        "id.token.claim": True,
                        "access.token.claim": True,
                        "claim.name": "groups",
                        "jsonType.label": "String",
                    },
                },
                {
                    "name": "audience",
                    "protocol": "openid-connect",
                    "protocolMapper": "oidc-audience-mapper",
                    "consentRequired": False,
                    "config": {
                        "included.client.audience": client_name,
                        "id.token.claim": True,
                        "access.token.claim": True,
                    },
                },
            ],
        },
        skip_exists=True,
    )

# create staff group
KEYCLOAK_ADMIN.create_group(payload={"name": "staff",}, skip_exists=True)

GROUPS = KEYCLOAK_ADMIN.get_groups()

ADMIN_USER_ID = KEYCLOAK_ADMIN.get_user_id(username=ADMIN["username"])

for GROUP in GROUPS:
    if GROUP["name"] == "staff":
        KEYCLOAK_ADMIN.group_user_add(ADMIN_USER_ID, GROUP["id"])
        break

# create webapp client roles
create_client_roles(
    KEYCLOAK_ADMIN,
    CLIENT_WEBAPP_NAME,
    ["swh.web.api.throttling_exempted", "swh.web.api.graph"],
)

DEPOSIT_API_ROLE_NAME = "swh.deposit.api"

# create deposit client roles
create_client_roles(
    KEYCLOAK_ADMIN, CLIENT_DEPOSIT_NAME, [DEPOSIT_API_ROLE_NAME],
)

# create some test users
for user_data in [
    {
        "email": "john.doe@example.org",
        "username": "johndoe",
        "firstName": "John",
        "lastName": "Doe",
        "credentials": [
            {"value": "johndoe-swh", "type": "password", "temporary": False}
        ],
        "enabled": True,
        "emailVerified": True,
    },
    {
        "email": "jane.doe@example.org",
        "username": "janedoe",
        "firstName": "Jane",
        "lastName": "Doe",
        "credentials": [
            {"value": "janedoe-swh", "type": "password", "temporary": False}
        ],
        "enabled": True,
        "emailVerified": True,
    },
    {
        "email": "test@swh.org",
        "username": "test",
        "firstName": "Test",
        "lastName": "aibot",
        "credentials": [{"value": "test", "type": "password", "temporary": False}],
        "enabled": True,
        "emailVerified": True,
    },
    {
        "email": "ambassador@swh.org",
        "username": "ambassador",
        "firstName": "ambassador",
        "lastName": "ambassador",
        "credentials": [
            {"value": "ambassador", "type": "password", "temporary": False}
        ],
        "enabled": True,
        "emailVerified": True,
    },
]:
    create_user(KEYCLOAK_ADMIN, user_data)


assign_client_roles_to_user(
    KEYCLOAK_ADMIN, CLIENT_DEPOSIT_NAME, [DEPOSIT_API_ROLE_NAME], "test"
)

AMBASSADOR_ROLE_NAME = "swh.ambassador"

# create SoftwareHeritage realm roles
create_realm_roles(
    KEYCLOAK_ADMIN, [AMBASSADOR_ROLE_NAME],
)

assign_realm_roles_to_user(KEYCLOAK_ADMIN, [AMBASSADOR_ROLE_NAME], "ambassador")
