# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

# Implement the "Immutable" resource option
from keystone.common.resource_options import core as ro_core
from keystone.common.validation import parameter_types
from keystone import exception

IMMUTABLE_OPT = ro_core.ResourceOption(
    option_id='IMMU',
    option_name='immutable',
    validator=ro_core.boolean_validator,
    json_schema_validation=parameter_types.boolean,
)


def check_resource_immutable(resource_ref):
    """Check to see if a resource is immutable.

    :param resource_ref: a dict reference of a resource to inspect
    """
    return resource_ref.get('options', {}).get(
        IMMUTABLE_OPT.option_name, False
    )


def check_immutable_update(
    original_resource_ref, new_resource_ref, type, resource_id
):
    """Check if an update is allowed to an immutable resource.

    Valid cases where an update is allowed:

        * Resource is not immutable
        * Resource is immutable, and update to set immutable to False or None

    :param original_resource_ref: a dict resource reference representing
                                  the current resource
    :param new_resource_ref: a dict reference of the updates to perform
    :param type: the resource type, e.g. 'project'
    :param resource_id: the id of the resource (e.g. project['id']),
                        usually a UUID
    :raises: ResourceUpdateForbidden
    """
    immutable = check_resource_immutable(original_resource_ref)
    if immutable:
        new_options = new_resource_ref.get('options', {})
        if type == "domain":
            if (
                new_resource_ref.get("is_domain", False) == True
                and not new_resource_ref.get("domain_id")
                and not new_resource_ref.get("parent_id")
            ):
                # To keep next check happy - reject certain props for the domain set by default in
                # `get_project_from_domain` if those ARE default
                new_resource_ref.pop("is_domain")
                new_resource_ref.pop("domain_id")
                new_resource_ref.pop("parent_id")
        # If resource is currently immutable - raise error in attempt to
        # update more then 1 property while making resource mutable
        # (first make mutable then update rest)
        if (
            (len(new_resource_ref.keys()) > 1)
            or (IMMUTABLE_OPT.option_name not in new_options)
            or (new_options[IMMUTABLE_OPT.option_name] not in (False, None))
        ):
            raise exception.ResourceUpdateForbidden(
                type=type, resource_id=resource_id
            )


def check_immutable_delete(resource_ref, resource_type, resource_id):
    """Check if a delete is allowed on a resource.

    :param resource_ref: dict reference of the resource
    :param resource_type: resource type (str) e.g. 'project'
    :param resource_id: id of the resource (str) e.g. project['id']
    :raises: ResourceDeleteForbidden
    """
    if check_resource_immutable(resource_ref):
        raise exception.ResourceDeleteForbidden(
            type=resource_type, resource_id=resource_id
        )
