# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# 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.
"""Flags and helpers for general Cloud NetApp Files commands."""


from googlecloudsdk.api_lib.netapp import constants
from googlecloudsdk.api_lib.netapp import util as netapp_api_util

from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.calliope.concepts import deps
from googlecloudsdk.command_lib.util.apis import arg_utils
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import properties

## Attribute configs ##


def GetLocationAttributeConfig():
  """Return the Location Attribute Config for resources.

  Returns:
    ResourceParameterAttributeConfig for location.
  """
  fallthroughs = [
      ## if location is not set, use value from
      ## gcloud config get-value netapp/location or netapp/region
      deps.ArgFallthrough('--location'),
      deps.PropertyFallthrough(properties.VALUES.netapp.location),
  ]

  return concepts.ResourceParameterAttributeConfig(
      name='location',
      fallthroughs=fallthroughs,
      help_text='The location of the {resource}.')


def GetStoragePoolAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'storage_pool', 'The instance of the {resource}.')


def GetVolumeAttributeConfig(positional=True):
  """Return the Volume Attribute Config for resources.

  Args:
    positional: boolean value describing whether volume attribute is conditional

  Returns:
    volume resource parameter attribute config for resource specs

  """
  if positional:
    fallthroughs = []
  else:
    ## For when we need a volume attribute in Snapshot resource spec, we want
    ## to assign fallthrough flags
    fallthroughs = [deps.ArgFallthrough('--volume')]
  help_text = ('The instance of the {resource}' if positional else (
      'The volume of the {resource}'))
  return concepts.ResourceParameterAttributeConfig(
      name='volume', fallthroughs=fallthroughs, help_text=help_text)


def GetSnapshotAttributeConfig(positional=True):
  if positional:
    help_text = 'The instance of the {resource}'
  else:
    help_text = 'The snapshot of the {resource}'
  return concepts.ResourceParameterAttributeConfig(
      'snapshot', help_text=help_text)


def GetReplicationAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'replication', 'The instance of the {resource}')


def GetOperationAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'operation', 'The Cloud NetApp operation.')


def GetActiveDirectoryAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'active_directory', 'The instance of the {resource}.'
  )


def GetBackupVaultAttributeConfig(positional=True):
  fallthroughs = []
  if not positional:
    ## For when we need a Backup Vault attribute in Backup resource spec
    ## we want to assign fallthrough flags
    fallthroughs = [deps.ArgFallthrough('--backup-vault')]
  return concepts.ResourceParameterAttributeConfig(
      'backup_vault', 'The Backup Vault of the {resource}.',
      fallthroughs=fallthroughs
  )


def GetBackupAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'backup', 'The instance of the {resource}.')


def GetBackupPolicyAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'backup_policy', 'The instance of the {resource}.'
  )


def GetKmsConfigAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'kms_config', 'The instance of the {resource}')


def GetKeyRingAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'key_ring', 'The instance of the {resource}.'
  )


def GetCryptoKeyAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'crypto_key', 'The instance of the {resource}.'
  )


def GetQuotaRuleAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'quota_rule', 'The instance of the {resource}.'
  )


def GetHostGroupAttributeConfig():
  return concepts.ResourceParameterAttributeConfig(
      'host_group', 'The instance of the {resource}.'
  )


## Resource Specs ##


def GetLocationResourceSpec():
  location_attribute_config = GetLocationAttributeConfig()
  location_attribute_config.fallthroughs = []
  return concepts.ResourceSpec(
      constants.LOCATIONS_COLLECTION,
      resource_name='location',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=location_attribute_config)


def GetListingLocationResourceSpec():
  location_attribute_config = GetLocationAttributeConfig()
  location_attribute_config.fallthroughs.insert(
      0, deps.Fallthrough(lambda: '-', hint='uses all locations by default.'))
  return concepts.ResourceSpec(
      constants.LOCATIONS_COLLECTION,
      resource_name='location',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=location_attribute_config)


def GetOperationResourceSpec():
  return concepts.ResourceSpec(
      constants.OPERATIONS_COLLECTION,
      resource_name='operation',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      operationsId=GetOperationAttributeConfig())


def GetStoragePoolResourceSpec():
  return concepts.ResourceSpec(
      constants.STORAGEPOOLS_COLLECTION,
      resource_name='storage_pool',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      storagePoolsId=GetStoragePoolAttributeConfig())


def GetVolumeResourceSpec(positional=True):
  return concepts.ResourceSpec(
      constants.VOLUMES_COLLECTION,
      resource_name='volume',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      volumesId=GetVolumeAttributeConfig(positional=positional),
  )


def GetSnapshotResourceSpec(source_snapshot_op=False, positional=True):
  """Gets the Resource Spec for Snapshot.

  Args:
    source_snapshot_op: Boolean on whether operation uses snapshot as source or
      not.
    positional: Boolean on whether resource is positional arg ornot

  Returns:
    The Resource Spec for Snapshot
  """
  location_attribute_config = GetLocationAttributeConfig()
  volume_attribute_config = GetVolumeAttributeConfig(positional=False)
  if source_snapshot_op:
    # if revert op or backup op (create, update) that is a source snapshot op,
    # we don't want volume attribute to have any fallthroughs
    # (--volume arg) since volume is positional in revert.
    volume_attribute_config.fallthroughs = []
  if not positional:
    location_attribute_config.fallthroughs = [
        deps.PropertyFallthrough(properties.VALUES.netapp.location),
    ]
  return concepts.ResourceSpec(
      constants.SNAPSHOTS_COLLECTION,
      resource_name='snapshot',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=location_attribute_config,
      volumesId=volume_attribute_config,
      snapshotsId=GetSnapshotAttributeConfig(positional=positional))


def GetReplicationResourceSpec():
  location_attribute_config = GetLocationAttributeConfig()
  volume_attribute_config = GetVolumeAttributeConfig(positional=False)
  return concepts.ResourceSpec(
      constants.REPLICATIONS_COLLECTION,
      resource_name='replication',
      api_version=constants.BETA_API_VERSION,
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=location_attribute_config,
      volumesId=volume_attribute_config,
      replicationsId=GetReplicationAttributeConfig())


def GetActiveDirectoryResourceSpec():
  return concepts.ResourceSpec(
      constants.ACTIVEDIRECTORIES_COLLECTION,
      resource_name='active_directory',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      activeDirectoriesId=GetActiveDirectoryAttributeConfig())


def GetKmsConfigResourceSpec():
  return concepts.ResourceSpec(
      constants.KMSCONFIGS_COLLECTION,
      resource_name='kms_config',
      api_version=constants.BETA_API_VERSION,
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      kmsConfigsId=GetKmsConfigAttributeConfig())


def GetBackupVaultResourceSpec(positional=True):
  return concepts.ResourceSpec(
      constants.BACKUPVAULTS_COLLECTION,
      resource_name='backup_vault',
      api_version=constants.BETA_API_VERSION,
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      backupVaultsId=GetBackupVaultAttributeConfig(positional=positional),
  )


def GetBackupResourceSpec(positional=True):
  location_attribute_config = GetLocationAttributeConfig()
  backup_vault_attribute_config = GetBackupVaultAttributeConfig(
      positional=False
  )
  if not positional:
    location_attribute_config.fallthroughs = [
        deps.PropertyFallthrough(properties.VALUES.netapp.location),
    ]
  return concepts.ResourceSpec(
      constants.BACKUPS_COLLECTION,
      resource_name='backup',
      api_version=constants.BETA_API_VERSION,
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=location_attribute_config,
      backupVaultsId=backup_vault_attribute_config,
      backupsId=GetBackupAttributeConfig(),
  )


def GetBackupPolicyResourceSpec():
  return concepts.ResourceSpec(
      constants.BACKUPPOLICIES_COLLECTION,
      resource_name='backup_policy',
      api_version=constants.BETA_API_VERSION,
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      backupPoliciesId=GetBackupPolicyAttributeConfig())


def GetCryptoKeyResourceSpec():
  return concepts.ResourceSpec(
      'cloudkms.projects.locations.keyRings.cryptoKeys',
      resource_name='crypto_key',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      keyRingsId=GetKeyRingAttributeConfig(),
      cryptoKeysId=GetCryptoKeyAttributeConfig()
  )


def GetQuotaRuleResourceSpec():
  return concepts.ResourceSpec(
      constants.QUOTA_RULES_COLLECTION,
      resource_name='quota_rule',
      api_version=constants.BETA_API_VERSION,
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      volumesId=GetVolumeAttributeConfig(positional=False),
      quotaRulesId=GetQuotaRuleAttributeConfig(),
  )


def GetHostGroupResourceSpec():
  return concepts.ResourceSpec(
      constants.HOST_GROUPS_COLLECTION,
      resource_name='host_group',
      projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
      locationsId=GetLocationAttributeConfig(),
      hostGroupsId=GetHostGroupAttributeConfig(),
  )

## Presentation Specs ##


def GetLocationPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'location', GetLocationResourceSpec(), group_help, required=True)


def GetResourceListingLocationPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      '--location',
      GetListingLocationResourceSpec(),
      group_help)


def GetOperationPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'operation', GetOperationResourceSpec(), group_help, required=True)


def GetStoragePoolPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'storage_pool', GetStoragePoolResourceSpec(), group_help, required=True)


def GetVolumePresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'volume', GetVolumeResourceSpec(), group_help, required=True)


def GetSnapshotPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'snapshot',
      GetSnapshotResourceSpec(),
      group_help,
      required=True,
      flag_name_overrides={'volume': ''})


def GetReplicationPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'replication',
      GetReplicationResourceSpec(),
      group_help,
      required=True,
      flag_name_overrides={'volume': ''})


def GetActiveDirectoryPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'active_directory',
      GetActiveDirectoryResourceSpec(),
      group_help,
      required=True)


def GetKmsConfigPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'kms_config',
      GetKmsConfigResourceSpec(),
      group_help,
      required=True)


def GetBackupVaultPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'backup_vault',
      GetBackupVaultResourceSpec(),
      group_help,
      required=True)


def GetBackupPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'backup',
      GetBackupResourceSpec(),
      group_help,
      required=True,
      flag_name_overrides={'backup_vault': ''})


def GetQuotaRulePresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'quota_rule',
      GetQuotaRuleResourceSpec(),
      group_help,
      required=True,
      flag_name_overrides={'volume': ''})


def GetHostGroupPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'host_group',
      GetHostGroupResourceSpec(),
      group_help,
      required=True,
  )


def GetOntapModeStoragePoolPresentationSpec(
    group_help: str,
    required: bool = True,
) -> presentation_specs.ResourcePresentationSpec:
  """Returns the ResourcePresentationSpec for a ONTAP-mode Storage Pool.

  Args:
    group_help: The help text for the argument group.
    required: Whether the argument is required.

  Returns:
    A ResourcePresentationSpec for an ONTAP-mode Storage Pool.
  """

  return presentation_specs.ResourcePresentationSpec(
      'storage_pool',
      GetStoragePoolResourceSpec(),
      group_help,
      required=required,
      prefixes=False,
  )


# TODO(b/290375665): Add more unit tests to test Backup Poicy
# Presentation, resource specs and flags
def GetBackupPolicyPresentationSpec(group_help):
  return presentation_specs.ResourcePresentationSpec(
      'backup_policy',
      GetBackupPolicyResourceSpec(),
      group_help,
      required=True)

## Add args to arg parser ##


def AddResourceDescriptionArg(parser, resource_name):
  """Add Description arg to arg_parser for a resource called resource_name."""
  parser.add_argument(
      '--description',
      required=False,
      help='A description of the Cloud NetApp {}'.format(resource_name))


def AddResourceCapacityArg(parser, resource_name, required=True):
  """Add Capacity arg to arg_parser for a resource called resource_name."""
  parser.add_argument(
      '--capacity',
      type=arg_parsers.BinarySize(
          default_unit='GiB', suggested_binary_size_scales=['GiB', 'TiB']),
      required=required,
      help=('The desired capacity of the {} in GiB or TiB units.'
            'If no capacity unit is specified, GiB is assumed.'.format(
                resource_name)))


def AddResourceAsyncFlag(parser):
  help_text = """Return immediately, without waiting for the operation
  in progress to complete."""
  concepts.ResourceParameterAttributeConfig(name='async', help_text=help_text)
  base.ASYNC_FLAG.AddToParser(parser)


def AddResourcePeerClusterNameArg(parser, required=True):
  """Adds the Peer Cluster Name (--peer-cluster-name) arg to the given parser.

  Args:
    parser: Argparse parser.
    required: Required to establish both cluster and svm peering.
  """
  parser.add_argument(
      '--peer-cluster-name',
      type=str,
      required=required,
      help="""Name of the destination cluster to be peered
        with the source cluster.""",
  )


def AddResourcePeerSvmNameArg(parser, required=True):
  """Adds the Peer SVM Name (--peer-svm-name) arg to the given parser.

  Args:
    parser: Argparse parser.
    required: Required to establish both cluster and svm peering.
  """
  parser.add_argument(
      '--peer-svm-name',
      type=str,
      required=required,
      help="""Name of the local source vserver svm to be peered
        with the destination cluster.""",
  )


def AddResourcePeerVolumeNameArg(parser, required=True):
  """Adds the Peer Volume Name (--peer-volume-name) arg to the given parser.

  Args:
    parser: Argparse parser.
    required: Required to establish both cluster and svm peering.
  """
  parser.add_argument(
      '--peer-volume-name',
      type=str,
      required=required,
      help="""Name of the source volume to be peered
       with the destination volume.""",
  )


def AddResourcePeerIpAddressesArg(parser):
  """Adds the Peer IP Addresses (--peer-ip-addresses) arg to the given parser.

  Args:
    parser: Argparse parser.

  Not required for svm peering.
  """
  parser.add_argument(
      '--peer-ip-addresses',
      type=arg_parsers.ArgList(min_length=1, element_type=str),
      metavar='PEER_IP_ADDRESS',
      help=(
          'List of ip addresses to be used for peering. This is required for'
          ' cluster peering, not required for svm peering.'
      ),
  )


def AddEndTrialArgs(parser, release_track):
  """Adds arguments for ending a NetApp Trial."""
  concept_parsers.ConceptParser([
      presentation_specs.ResourcePresentationSpec(
          '--location',
          GetLocationResourceSpec(),
          'The location to end the trial. If not specified, '
          'an available location will be automatically selected.',
          required=False,
      )
  ]).AddToParser(parser)

  messages = netapp_api_util.GetMessagesModule(release_track=release_track)

  exit_reason_arg = arg_utils.ChoiceEnumMapper(
      '--exit-reason',
      messages.EndTrialRequest.ExitReasonValueValuesEnum,
      help_str='The reason for exiting the trial.',
      custom_mappings={
          'UPGRADED': 'upgraded',
          'OPT_OUT': 'opt-out',
      },
      required=False,
      default='opt-out',
  )
  exit_reason_arg.choice_arg.AddToParser(parser)

  parser.add_argument(
      '--opt-out-reasons',
      type=arg_parsers.ArgList(element_type=str),
      metavar='REASON',
      help=(
          'The reasons for opting out of the trial. This flag can only be used'
          ' if --exit-reason is opt-out.'
      ),
  )

  AddResourceAsyncFlag(parser)
