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

import argparse
from collections.abc import Iterable, Sequence
from typing import Any

from openstack.block_storage.v3 import cluster as _cluster
from openstack import utils as sdk_utils
from osc_lib import exceptions
from osc_lib import utils

from openstackclient import command
from openstackclient.i18n import _


def _format_cluster(
    cluster: Any, detailed: bool = False
) -> tuple[Sequence[str], Iterable[Any]]:
    columns: tuple[str, ...] = (
        'name',
        'binary',
        'state',
        'status',
    )
    column_headers: tuple[str, ...] = (
        'Name',
        'Binary',
        'State',
        'Status',
    )

    if detailed:
        columns += (
            'disabled_reason',
            'num_hosts',
            'num_down_hosts',
            'last_heartbeat',
            'created_at',
            'updated_at',
            # optional columns, depending on whether replication is enabled
            'replication_status',
            'frozen',
            'active_backend_id',
        )
        column_headers += (
            'Disabled Reason',
            'Hosts',
            'Down Hosts',
            'Last Heartbeat',
            'Created At',
            'Updated At',
            # optional columns, depending on whether replication is enabled
            'Replication Status',
            'Frozen',
            'Active Backend ID',
        )

    return (
        column_headers,
        utils.get_item_properties(
            cluster,
            columns,
        ),
    )


class ListBlockStorageCluster(command.Lister):
    """List block storage clusters.

    This command requires ``--os-volume-api-version`` 3.7 or greater.
    """

    def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
        parser = super().get_parser(prog_name)
        parser.add_argument(
            '--cluster',
            metavar='<name>',
            default=None,
            help=_(
                'Filter by cluster name, without backend will list '
                'all clustered services from the same cluster.'
            ),
        )
        parser.add_argument(
            '--binary',
            metavar='<binary>',
            help=_('Cluster binary.'),
        )
        parser.add_argument(
            '--up',
            action='store_true',
            dest='is_up',
            default=None,
            help=_('Filter by up status.'),
        )
        parser.add_argument(
            '--down',
            action='store_false',
            dest='is_up',
            help=_('Filter by down status.'),
        )
        parser.add_argument(
            '--disabled',
            action='store_true',
            dest='is_disabled',
            default=None,
            help=_('Filter by disabled status.'),
        )
        parser.add_argument(
            '--enabled',
            action='store_false',
            dest='is_disabled',
            help=_('Filter by enabled status.'),
        )
        parser.add_argument(
            '--num-hosts',
            metavar='<hosts>',
            type=int,
            default=None,
            help=_('Filter by number of hosts in the cluster.'),
        )
        parser.add_argument(
            '--num-down-hosts',
            metavar='<hosts>',
            type=int,
            default=None,
            help=_('Filter by number of hosts that are down.'),
        )
        parser.add_argument(
            '--long',
            action='store_true',
            default=False,
            help=_("List additional fields in output"),
        )
        return parser

    def take_action(
        self, parsed_args: argparse.Namespace
    ) -> tuple[tuple[str, ...], Iterable[tuple[Any, ...]]]:
        volume_client = sdk_utils.ensure_service_version(
            self.app.client_manager.volume, '3'
        )

        if not sdk_utils.supports_microversion(volume_client, '3.7'):
            msg = _(
                "--os-volume-api-version 3.7 or greater is required to "
                "support the 'block storage cluster list' command"
            )
            raise exceptions.CommandError(msg)

        columns: tuple[str, ...] = ('Name', 'Binary', 'State', 'Status')
        if parsed_args.long:
            columns += (
                'Num Hosts',
                'Num Down Hosts',
                'Last Heartbeat',
                'Disabled Reason',
                'Created At',
                'Updated At',
            )

        data = volume_client.clusters(
            name=parsed_args.cluster,
            binary=parsed_args.binary,
            is_up=parsed_args.is_up,
            disabled=parsed_args.is_disabled,
            num_hosts=parsed_args.num_hosts,
            num_down_hosts=parsed_args.num_down_hosts,
            details=parsed_args.long,
        )

        return (
            columns,
            (utils.get_item_properties(s, columns) for s in data),
        )


class SetBlockStorageCluster(command.Command):
    """Set block storage cluster properties.

    This command requires ``--os-volume-api-version`` 3.7 or greater.
    """

    def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
        parser = super().get_parser(prog_name)
        parser.add_argument(
            'cluster',
            metavar='<cluster>',
            help=_('Name of block storage cluster to update (name only)'),
        )
        parser.add_argument(
            '--binary',
            metavar='<binary>',
            default='cinder-volume',
            help=_(
                "Name of binary to filter by; defaults to 'cinder-volume' "
                "(optional)"
            ),
        )
        enabled_group = parser.add_mutually_exclusive_group()
        enabled_group.add_argument(
            '--enable',
            action='store_false',
            dest='disabled',
            default=None,
            help=_('Enable cluster'),
        )
        enabled_group.add_argument(
            '--disable',
            action='store_true',
            dest='disabled',
            help=_('Disable cluster'),
        )
        parser.add_argument(
            '--disable-reason',
            metavar='<reason>',
            dest='disabled_reason',
            help=_(
                'Reason for disabling the cluster '
                '(should be used with --disable option)'
            ),
        )
        return parser

    def take_action(
        self, parsed_args: argparse.Namespace
    ) -> tuple[Sequence[str], Iterable[Any]]:
        volume_client = sdk_utils.ensure_service_version(
            self.app.client_manager.volume, '3'
        )

        if not sdk_utils.supports_microversion(volume_client, '3.7'):
            msg = _(
                "--os-volume-api-version 3.7 or greater is required to "
                "support the 'block storage cluster set' command"
            )
            raise exceptions.CommandError(msg)

        if parsed_args.disabled_reason and not parsed_args.disabled:
            msg = _("Cannot specify --disable-reason without --disable")
            raise exceptions.CommandError(msg)

        cluster_obj = _cluster.Cluster(
            name=parsed_args.cluster,
            binary=parsed_args.binary,
        )

        if parsed_args.disabled:
            cluster = volume_client.disable_cluster(
                cluster_obj,
                reason=parsed_args.disabled_reason,
            )
        else:
            cluster = volume_client.enable_cluster(cluster_obj)

        return _format_cluster(cluster, detailed=True)


class ShowBlockStorageCluster(command.ShowOne):
    """Show detailed information for a block storage cluster.

    This command requires ``--os-volume-api-version`` 3.7 or greater.
    """

    def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
        parser = super().get_parser(prog_name)
        parser.add_argument(
            'cluster',
            metavar='<cluster>',
            help=_('Name of block storage cluster.'),
        )
        parser.add_argument(
            '--binary',
            metavar='<binary>',
            help=_('Service binary.'),
        )
        return parser

    def take_action(
        self, parsed_args: argparse.Namespace
    ) -> tuple[Sequence[str], Iterable[Any]]:
        volume_client = sdk_utils.ensure_service_version(
            self.app.client_manager.volume, '3'
        )

        if not sdk_utils.supports_microversion(volume_client, '3.7'):
            msg = _(
                "--os-volume-api-version 3.7 or greater is required to "
                "support the 'block storage cluster show' command"
            )
            raise exceptions.CommandError(msg)

        cluster = volume_client.get_cluster(parsed_args.cluster)

        return _format_cluster(cluster, detailed=True)
