#    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.

# This is referred from Redfish standard schema.
# http://redfish.dmtf.org/schemas/v1/SecureBoot.v1_1_0.json

import logging

from sushy import exceptions
from sushy.resources import base
from sushy.resources import common
from sushy.resources.system import constants
from sushy.resources.system import secure_boot_database
from sushy import utils

LOG = logging.getLogger(__name__)


class ResetKeysActionField(common.ActionField):

    allowed_values = base.Field('ResetKeysType@Redfish.AllowableValues',
                                adapter=list)


class ActionsField(base.CompositeField):

    reset_keys = ResetKeysActionField('#SecureBoot.ResetKeys')
    """Action that resets the UEFI Secure Boot keys."""


class SecureBoot(base.ResourceBase):

    identity = base.Field('Id', required=True)
    """The Bios resource identity string"""

    name = base.Field('Name')
    """The name of the resource"""

    description = base.Field('Description')
    """Human-readable description of the BIOS resource"""

    current_boot = base.MappedField('SecureBootCurrentBoot',
                                    constants.SecureBootCurrentBoot)
    """The UEFI Secure Boot state during the current boot cycle."""

    enabled = base.Field('SecureBootEnable')
    """Whether the UEFI Secure Boot takes effect on next boot.

    This property can be enabled in UEFI boot mode only.
    """

    mode = base.MappedField('SecureBootMode', constants.SecureBootMode)
    """The current UEFI Secure Boot Mode."""

    # TODO(dtantsur): SecureBootDatabases

    _actions = ActionsField('Actions')

    def __init__(self, connector, path, redfish_version=None, registries=None,
                 root=None):
        """A class representing secure boot settings.

        :param connector: A Connector instance
        :param path: Sub-URI path to the SecureBoot resource
        :param registries: Dict of message registries to be used when
            parsing messages of attribute update status
        :param root: Sushy root object. Empty for Sushy root itself.
        """
        super().__init__(connector, path, redfish_version=redfish_version,
                         registries=registries, root=root)

    @property
    @utils.cache_it
    def databases(self):
        """A collection of secure boot databases.

        It is set once when the first time it is queried. On refresh,
        this property is marked as stale (greedy-refresh not done).
        Here the actual refresh of the sub-resource happens, if stale.

        :raises: MissingAttributeError if 'SecureBootDatabases/@odata.id' field
            is missing.
        :returns: `SimpleStorageCollection` instance
        """
        return secure_boot_database.SecureBootDatabaseCollection(
            self._conn, utils.get_sub_resource_path_by(
                self, "SecureBootDatabases"),
            redfish_version=self.redfish_version,
            registries=self.registries, root=self.root)

    def _get_reset_action_element(self):
        reset_action = self._actions.reset_keys
        if not reset_action:
            raise exceptions.MissingActionError(action='#SecureBoot.ResetKeys',
                                                resource=self._path)
        return reset_action

    def get_allowed_reset_keys_values(self):
        """Get the allowed values for resetting the keys.

        :returns: A set with the allowed values.
        """
        reset_action = self._get_reset_action_element()

        if not reset_action.allowed_values:
            LOG.warning('Could not figure out the allowed values for the '
                        'reset keys action for %s', self.identity)
            return set(constants.SecureBootResetKeysType)

        return {v for v in constants.SecureBootResetKeysType
                if v.value in reset_action.allowed_values}

    def reset_keys(self, reset_type):
        """Reset secure boot keys.

        :param reset_type: Reset type, one of `SECURE_BOOT_RESET_KEYS_*`
            constants.
        """
        valid_resets = self.get_allowed_reset_keys_values()
        if reset_type not in valid_resets:
            raise exceptions.InvalidParameterValueError(
                parameter='reset_type', value=reset_type,
                valid_values=valid_resets)

        reset_type = constants.SecureBootResetKeysType(reset_type).value
        target_uri = self._get_reset_action_element().target_uri
        self._conn.post(target_uri, data={'ResetKeysType': reset_type})

    def set_enabled(self, enabled):
        """Enable/disable secure boot.

        :param enabled: True, if secure boot is enabled for next boot.
        """
        if not isinstance(enabled, bool):
            raise exceptions.InvalidParameterValueError(
                f"Expected a boolean for 'enabled', got {enabled}")

        etag = self._get_etag()
        self._conn.patch(self.path, data={'SecureBootEnable': enabled},
                         etag=etag)
