"""
Manage repository permissions.
"""

import logging

from flask import request

from endpoints.api import (
    resource,
    nickname,
    require_repo_admin,
    RepositoryParamResource,
    log_action,
    request_error,
    validate_json_request,
    path_param,
)
from endpoints.exception import NotFound
from .permission_models_pre_oci import pre_oci_model as model
from .permission_models_interface import DeleteException, SaveException

logger = logging.getLogger(__name__)


@resource("/v1/repository/<apirepopath:repository>/permissions/team/")
@path_param("repository", "The full path of the repository. e.g. namespace/name")
class RepositoryTeamPermissionList(RepositoryParamResource):
    """
    Resource for repository team permissions.
    """

    @require_repo_admin
    @nickname("listRepoTeamPermissions")
    def get(self, namespace_name, repository_name):
        """
        List all team permission.
        """
        repo_perms = model.get_repo_permissions_by_team(namespace_name, repository_name)

        return {
            "permissions": {repo_perm.team_name: repo_perm.to_dict() for repo_perm in repo_perms}
        }


@resource("/v1/repository/<apirepopath:repository>/permissions/user/")
@path_param("repository", "The full path of the repository. e.g. namespace/name")
class RepositoryUserPermissionList(RepositoryParamResource):
    """
    Resource for repository user permissions.
    """

    @require_repo_admin
    @nickname("listRepoUserPermissions")
    def get(self, namespace_name, repository_name):
        """
        List all user permissions.
        """
        perms = model.get_repo_permissions_by_user(namespace_name, repository_name)
        return {"permissions": {p.username: p.to_dict() for p in perms}}


@resource("/v1/repository/<apirepopath:repository>/permissions/user/<username>/transitive")
@path_param("repository", "The full path of the repository. e.g. namespace/name")
@path_param("username", "The username of the user to which the permissions apply")
class RepositoryUserTransitivePermission(RepositoryParamResource):
    """
    Resource for retrieving whether a user has access to a repository, either directly or via a
    team.
    """

    @require_repo_admin
    @nickname("getUserTransitivePermission")
    def get(self, namespace_name, repository_name, username):
        """
        Get the fetch the permission for the specified user.
        """

        roles = model.get_repo_roles(username, namespace_name, repository_name)

        if not roles:
            raise NotFound

        return {"permissions": [r.to_dict() for r in roles]}


@resource("/v1/repository/<apirepopath:repository>/permissions/user/<username>")
@path_param("repository", "The full path of the repository. e.g. namespace/name")
@path_param("username", "The username of the user to which the permission applies")
class RepositoryUserPermission(RepositoryParamResource):
    """
    Resource for managing individual user permissions.
    """

    schemas = {
        "UserPermission": {
            "type": "object",
            "description": "Description of a user permission.",
            "required": [
                "role",
            ],
            "properties": {
                "role": {
                    "type": "string",
                    "description": "Role to use for the user",
                    "enum": [
                        "read",
                        "write",
                        "admin",
                    ],
                },
            },
        },
    }

    @require_repo_admin
    @nickname("getUserPermissions")
    def get(self, namespace_name, repository_name, username):
        """
        Get the permission for the specified user.
        """
        logger.debug(
            "Get repo: %s/%s permissions for user %s", namespace_name, repository_name, username
        )
        perm = model.get_repo_permission_for_user(username, namespace_name, repository_name)
        return perm.to_dict()

    @require_repo_admin
    @nickname("changeUserPermissions")
    @validate_json_request("UserPermission")
    def put(self, namespace_name, repository_name, username):  # Also needs to respond to post
        """
        Update the perimssions for an existing repository.
        """
        new_permission = request.get_json()

        logger.debug("Setting permission to: %s for user %s", new_permission["role"], username)

        try:
            perm = model.set_repo_permission_for_user(
                username, namespace_name, repository_name, new_permission["role"]
            )
            resp = perm.to_dict()
        except SaveException as ex:
            raise request_error(exception=ex)

        log_action(
            "change_repo_permission",
            namespace_name,
            {
                "username": username,
                "repo": repository_name,
                "namespace": namespace_name,
                "role": new_permission["role"],
            },
            repo_name=repository_name,
        )

        return resp, 200

    @require_repo_admin
    @nickname("deleteUserPermissions")
    def delete(self, namespace_name, repository_name, username):
        """
        Delete the permission for the user.
        """
        try:
            model.delete_repo_permission_for_user(username, namespace_name, repository_name)
        except DeleteException as ex:
            raise request_error(exception=ex)

        log_action(
            "delete_repo_permission",
            namespace_name,
            {"username": username, "repo": repository_name, "namespace": namespace_name},
            repo_name=repository_name,
        )

        return "", 204


@resource("/v1/repository/<apirepopath:repository>/permissions/team/<teamname>")
@path_param("repository", "The full path of the repository. e.g. namespace/name")
@path_param("teamname", "The name of the team to which the permission applies")
class RepositoryTeamPermission(RepositoryParamResource):
    """
    Resource for managing individual team permissions.
    """

    schemas = {
        "TeamPermission": {
            "type": "object",
            "description": "Description of a team permission.",
            "required": [
                "role",
            ],
            "properties": {
                "role": {
                    "type": "string",
                    "description": "Role to use for the team",
                    "enum": [
                        "read",
                        "write",
                        "admin",
                    ],
                },
            },
        },
    }

    @require_repo_admin
    @nickname("getTeamPermissions")
    def get(self, namespace_name, repository_name, teamname):
        """
        Fetch the permission for the specified team.
        """
        logger.debug(
            "Get repo: %s/%s permissions for team %s", namespace_name, repository_name, teamname
        )
        role = model.get_repo_role_for_team(teamname, namespace_name, repository_name)
        return role.to_dict()

    @require_repo_admin
    @nickname("changeTeamPermissions")
    @validate_json_request("TeamPermission")
    def put(self, namespace_name, repository_name, teamname):
        """
        Update the existing team permission.
        """
        new_permission = request.get_json()

        logger.debug("Setting permission to: %s for team %s", new_permission["role"], teamname)

        try:
            perm = model.set_repo_permission_for_team(
                teamname, namespace_name, repository_name, new_permission["role"]
            )
            resp = perm.to_dict()
        except SaveException as ex:
            raise request_error(exception=ex)

        log_action(
            "change_repo_permission",
            namespace_name,
            {"team": teamname, "repo": repository_name, "role": new_permission["role"]},
            repo_name=repository_name,
        )
        return resp, 200

    @require_repo_admin
    @nickname("deleteTeamPermissions")
    def delete(self, namespace_name, repository_name, teamname):
        """
        Delete the permission for the specified team.
        """
        try:
            model.delete_repo_permission_for_team(teamname, namespace_name, repository_name)
        except DeleteException as ex:
            raise request_error(exception=ex)

        log_action(
            "delete_repo_permission",
            namespace_name,
            {"team": teamname, "repo": repository_name},
            repo_name=repository_name,
        )

        return "", 204
