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

from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils

from manilaclient import api_versions
from manilaclient.common._i18n import _


class QuotaSet(command.Command):
    """Set quotas for a project or project/user or project/share-type.

    It can be used to set the default class for all projects.
    """

    _description = _(
        "Set Quota for a project, or project/user or "
        "project/share-type or a class."
    )

    def get_parser(self, prog_name):
        parser = super().get_parser(prog_name)
        quota_type = parser.add_mutually_exclusive_group()
        parser.add_argument(
            'project',
            metavar='<project/class>',
            help=_("A project (name/ID) or a class (e.g.: default)."),
        )
        quota_type.add_argument(
            '--class',
            dest='quota_class',
            action='store_true',
            default=False,
            help=_(
                "Update class quota to all projects. "
                "Mutually exclusive with '--user' and '--share-type'."
            ),
        )
        quota_type.add_argument(
            '--user',
            metavar='<user>',
            default=None,
            help=_(
                "Name or ID of a user to set the quotas for. "
                "Mutually exclusive with '--share-type' and '--class'."
            ),
        )
        quota_type.add_argument(
            '--share-type',
            metavar='<share-type>',
            type=str,
            default=None,
            help=_(
                "Name or ID of a share type to set the quotas for. "
                "Mutually exclusive with '--user' and '--class'. "
                "Available only for microversion >= 2.39"
            ),
        )
        parser.add_argument(
            '--shares',
            metavar='<shares>',
            type=int,
            default=None,
            help=_('New value for the "shares" quota.'),
        )
        parser.add_argument(
            '--snapshots',
            metavar='<snapshots>',
            type=int,
            default=None,
            help=_('New value for the "snapshots" quota.'),
        )
        parser.add_argument(
            '--gigabytes',
            metavar='<gigabytes>',
            type=int,
            default=None,
            help=_('New value for the "gigabytes" quota.'),
        )
        parser.add_argument(
            '--snapshot-gigabytes',
            metavar='<snapshot-gigabytes>',
            type=int,
            default=None,
            help=_('New value for the "snapshot-gigabytes" quota.'),
        )
        parser.add_argument(
            '--share-networks',
            metavar='<share-networks>',
            type=int,
            default=None,
            help=_('New value for the "share-networks" quota.'),
        )
        parser.add_argument(
            '--share-groups',
            metavar='<share-groups>',
            type=int,
            default=None,
            help=_(
                'New value for the "share-groups" quota. '
                'Available only for microversion >= 2.40'
            ),
        )
        parser.add_argument(
            '--share-group-snapshots',
            metavar='<share-group-snapshots>',
            type=int,
            default=None,
            help=_(
                'New value for the "share-group-snapshots" quota. '
                'Available only for microversion >= 2.40'
            ),
        )
        parser.add_argument(
            '--share-replicas',
            metavar='<share-replicas>',
            type=int,
            default=None,
            help=_(
                "Number of share replicas. "
                "Available only for microversion >= 2.53"
            ),
        )
        parser.add_argument(
            '--replica-gigabytes',
            metavar='<replica-gigabytes>',
            type=int,
            default=None,
            help=_(
                "Capacity of share replicas in total. "
                "Available only for microversion >= 2.53"
            ),
        )
        parser.add_argument(
            '--per-share-gigabytes',
            metavar='<per-share-gigabytes>',
            type=int,
            default=None,
            help=_(
                'New value for the "per-share-gigabytes" quota. '
                'Available only for microversion >= 2.62'
            ),
        )
        parser.add_argument(
            '--encryption-keys',
            metavar='<encryption-keys>',
            type=int,
            default=None,
            help=_(
                'New value for the "encryption-keys" quota. '
                'Available only for microversion >= 2.90'
            ),
        )
        parser.add_argument(
            '--force',
            dest='force',
            action="store_true",
            default=None,
            help=_('Force update the quota. Not applicable for class update.'),
        )
        return parser

    def take_action(self, parsed_args):
        share_client = self.app.client_manager.share
        identity_client = self.app.client_manager.identity

        user_id = None
        if parsed_args.user:
            user_id = utils.find_resource(
                identity_client.users, parsed_args.user
            ).id

        kwargs = {
            "shares": parsed_args.shares,
            "snapshots": parsed_args.snapshots,
            "gigabytes": parsed_args.gigabytes,
            "snapshot_gigabytes": parsed_args.snapshot_gigabytes,
            "share_networks": parsed_args.share_networks,
        }

        if parsed_args.share_type is not None:
            if share_client.api_version < api_versions.APIVersion('2.39'):
                raise exceptions.CommandError(
                    _(
                        "'share type' quotas are available only starting with "
                        "'2.39' API microversion."
                    )
                )
            kwargs["share_type"] = parsed_args.share_type
        if parsed_args.share_groups is not None:
            if share_client.api_version < api_versions.APIVersion('2.40'):
                raise exceptions.CommandError(
                    _(
                        "'share group' quotas are available only "
                        "starting with '2.40' API microversion."
                    )
                )
            kwargs["share_groups"] = parsed_args.share_groups
        if parsed_args.share_group_snapshots is not None:
            if share_client.api_version < api_versions.APIVersion('2.40'):
                raise exceptions.CommandError(
                    _(
                        "'share group snapshots' quotas are available only "
                        "starting with '2.40' API microversion."
                    )
                )
            kwargs["share_group_snapshots"] = parsed_args.share_group_snapshots
        if parsed_args.share_replicas is not None:
            if share_client.api_version < api_versions.APIVersion('2.53'):
                raise exceptions.CommandError(
                    _(
                        "setting the number of 'share replicas' is available "
                        "only starting with API microversion '2.53'."
                    )
                )
            kwargs["share_replicas"] = parsed_args.share_replicas
        if parsed_args.replica_gigabytes is not None:
            if share_client.api_version < api_versions.APIVersion('2.53'):
                raise exceptions.CommandError(
                    _(
                        "setting the capacity of share replicas in total "
                        "is available only starting with API microversion "
                        "'2.53'."
                    )
                )
            kwargs["replica_gigabytes"] = parsed_args.replica_gigabytes
        if parsed_args.per_share_gigabytes is not None:
            if share_client.api_version < api_versions.APIVersion('2.62'):
                raise exceptions.CommandError(
                    _(
                        "'per share gigabytes' quotas are available only "
                        "starting with '2.62' API microversion."
                    )
                )
            kwargs["per_share_gigabytes"] = parsed_args.per_share_gigabytes
        if parsed_args.encryption_keys is not None:
            if share_client.api_version < api_versions.APIVersion('2.90'):
                raise exceptions.CommandError(
                    _(
                        "'encryption keys' quotas are available only "
                        "starting with '2.90' API microversion."
                    )
                )
            kwargs["encryption_keys"] = parsed_args.encryption_keys

        if all(value is None for value in kwargs.values()):
            raise exceptions.CommandError(
                _(
                    "Nothing to set. "
                    "New quota must be specified to at least one of the "
                    "following resources: 'shares', 'snapshots', 'gigabytes', "
                    "'snapshot-gigabytes', 'share-networks', 'share-type', "
                    "'share-groups', 'share-group-snapshots', "
                    "'share-replicas', 'replica-gigabytes', "
                    "'per-share-gigabytes', 'encryption_keys'"
                )
            )

        if parsed_args.quota_class:
            kwargs.update(
                {
                    "class_name": parsed_args.project,
                }
            )
            try:
                share_client.quota_classes.update(**kwargs)
            except Exception as e:
                msg = _(
                    "Failed to set quotas for class '%(project)s': '%(e)s'"
                )
                raise exceptions.CommandError(
                    msg % {'project': parsed_args.project, 'e': e}
                )
        else:
            project_id = utils.find_resource(
                identity_client.projects, parsed_args.project
            ).id

            kwargs.update(
                {
                    "tenant_id": project_id,
                    "force": parsed_args.force,
                    "user_id": user_id,
                }
            )

            try:
                share_client.quotas.update(**kwargs)
            except Exception as e:
                msg = _(
                    "Failed to set quotas for project '%(project)s': '%(e)s'"
                )
                raise exceptions.CommandError(
                    msg % {'project': parsed_args.project, 'e': e}
                )


class QuotaShow(command.ShowOne):
    """List the quotas for a project or project/user or project/share-type."""

    _description = _("Show Quota")

    def get_parser(self, prog_name):
        parser = super().get_parser(prog_name)
        quota_type = parser.add_mutually_exclusive_group()
        parser.add_argument(
            'project',
            metavar='<project>',
            help=_('Name or ID of the project to list quotas for.'),
        )
        quota_type.add_argument(
            '--user',
            metavar='<user>',
            default=None,
            help=_(
                "Name or ID of user to list the quotas for. Optional. "
                "Mutually exclusive with '--share-type'."
            ),
        )
        quota_type.add_argument(
            '--share-type',
            metavar='<share-type>',
            type=str,
            default=None,
            help=_(
                "Name or ID of a share type to list the quotas for. "
                "Optional. "
                "Mutually exclusive with '--user'. "
                "Available only for microversion >= 2.39"
            ),
        )
        parser.add_argument(
            '--detail',
            action='store_true',
            default=False,
            help=_(
                'Optional flag to indicate whether to show quota in detail.'
                ' Default false, available only for microversion >= 2.25.'
            ),
        )
        parser.add_argument(
            '--defaults',
            action='store_true',
            default=False,
            help=_('Show the default quotas for the project.'),
        )
        return parser

    def take_action(self, parsed_args):
        share_client = self.app.client_manager.share
        identity_client = self.app.client_manager.identity

        user_id = None
        if parsed_args.user:
            user_id = utils.find_resource(
                identity_client.users, parsed_args.user
            ).id

        project_id = utils.find_resource(
            identity_client.projects, parsed_args.project
        ).id

        quotas = {}
        if parsed_args.defaults:
            quotas = share_client.quotas.defaults(project_id)
        else:
            kwargs = {
                "tenant_id": project_id,
                "user_id": user_id,
                "detail": parsed_args.detail,
            }
            if parsed_args.share_type is not None:
                if share_client.api_version < api_versions.APIVersion("2.39"):
                    raise exceptions.CommandError(
                        _(
                            "'share type' quotas are available only "
                            "starting with '2.39' API microversion."
                        )
                    )
                kwargs["share_type"] = parsed_args.share_type

            quotas = share_client.quotas.get(**kwargs)

        printable_quotas = {}
        for quota_k, quota_v in sorted(quotas.to_dict().items()):
            if isinstance(quota_v, dict):
                quota_v = '\n'.join(
                    [f'{k} = {v}' for k, v in sorted(quota_v.items())]
                )
            printable_quotas[quota_k] = quota_v

        return self.dict2columns(printable_quotas)


class QuotaDelete(command.Command):
    """Delete quota for project/user or project/share-type.

    The quota will revert back to default.
    """

    _description = _("Delete Quota")

    def get_parser(self, prog_name):
        parser = super().get_parser(prog_name)
        quota_type = parser.add_mutually_exclusive_group()
        parser.add_argument(
            'project',
            metavar='<project>',
            help=_('Name or ID of the project to delete quotas for.'),
        )
        quota_type.add_argument(
            '--user',
            metavar='<user>',
            default=None,
            help=_(
                "Name or ID of user to delete the quotas for. Optional. "
                "Mutually exclusive with '--share-type'."
            ),
        )
        quota_type.add_argument(
            '--share-type',
            metavar='<share-type>',
            type=str,
            default=None,
            help=_(
                "Name or ID of a share type to delete the quotas for. "
                "Optional. "
                "Mutually exclusive with '--user'. "
                "Available only for microversion >= 2.39"
            ),
        )
        return parser

    def take_action(self, parsed_args):
        share_client = self.app.client_manager.share
        identity_client = self.app.client_manager.identity

        user_id = None
        if parsed_args.user:
            user_id = utils.find_resource(
                identity_client.users, parsed_args.user
            ).id

        project_id = utils.find_resource(
            identity_client.projects, parsed_args.project
        ).id

        kwargs = {"tenant_id": project_id, "user_id": user_id}
        if parsed_args.share_type:
            if share_client.api_version < api_versions.APIVersion("2.39"):
                raise exceptions.CommandError(
                    _(
                        "'share type' quotas are available only starting with "
                        "API microversion '2.39'."
                    )
                )
            kwargs["share_type"] = parsed_args.share_type

        share_client.quotas.delete(**kwargs)
