# -*- coding: utf-8 -*- #
# Copyright 2023 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 the config-manager command group."""


from googlecloudsdk.api_lib.functions.v1 import util as functions_api_util
from googlecloudsdk.api_lib.infra_manager import configmanager_util
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base


def AddLabelsFlag(parser, help_text):
  """Add --labels flag."""
  parser.add_argument(
      '--labels',
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
      help=help_text,
  )


def AddAnnotationsFlag(parser, help_text):
  """Add --annotations flag."""
  parser.add_argument(
      '--annotations',
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
      help=help_text,
  )


def AddAsyncFlag(parser):
  """Add --async flag."""
  base.ASYNC_FLAG.AddToParser(parser)


def AddTerraformBlueprintFlag(parser):
  """Add TerraformBlueprint related flags."""

  input_values_help_text = """\
Input variable values for the Terraform blueprint. It only
      accepts (key, value) pairs where value is a scalar value.

Examples:

Pass input values on command line:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --gcs-source="gs://my-bucket" --input-values=projects=p1,region=r
"""

  inputs_file_help_text = """\
A .tfvars file containing terraform variable values. --inputs-file flag is supported for python version 3.6 and above.

Examples:

Pass input values on the command line:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --gcs-source="gs://my-bucket" --inputs-file=path-to-tfvar-file.tfvar
"""

  gcs_source_help_text = """\
URI of an object in Google Cloud Storage.
      e.g. `gs://{bucket}/{object}`

Examples:

Create a deployment from a storage my-bucket:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --gcs-source="gs://my-bucket"
"""

  git_source_repo_help = """\
Repository URL.
Example: 'https://github.com/examples/repository.git'

Use in conjunction with `--git-source-directory` and `--git-source_ref`

Examples:

Create a deployment from the "https://github.com/examples/repository.git" repo, "staging/compute" folder, "mainline" branch:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --git-source-repo="https://github.com/examples/repository.git"
    --git-source-directory="staging/compute" --git-source-ref="mainline"
"""

  git_source_directory_help = """\
Subdirectory inside the repository.
Example: 'staging/my-package'

Use in conjunction with `--git-source-repo` and `--git-source-ref`

Examples:

Create a deployment from the "https://github.com/examples/repository.git" repo, "staging/compute" folder, "mainline" branch:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --git-source-repo="https://github.com/examples/repository.git"
    --git-source-directory="staging/compute" --git-source-ref="mainline"
"""

  git_source_ref_help = """\
Subdirectory inside the repository.
Example: 'staging/my-package'

Use in conjunction with `--git-source-repo` and `--git-source-directory`

Examples:

Create a deployment from the "https://github.com/examples/repository.git" repo, "staging/compute" folder, "mainline" branch:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --git-source-repo="https://github.com/examples/repository.git"
    --git-source-directory="staging/compute" --git-source-ref="mainline"
"""

  local_source_help = """\
Local storage path where config files are stored. When using this option, Terraform config file references outside this storage path is not supported.
      e.g. `./path/to/blueprint`

Examples:

Create a deployment from a local storage path `./path/to/blueprint`:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --local-source="./path/to/blueprint"
"""

  stage_bucket_help = """\
Use in conjunction with `--local-source` to specify a destination storage bucket for
uploading local files.

If unspecified, the bucket defaults to `gs://PROJECT_NAME_blueprints`. Uploaded
content will appear in the `source` object under a name comprised of the
timestamp and a UUID. The final output destination looks like this:
`gs://_BUCKET_/source/1615850562.234312-044e784992744951b0cd71c0b011edce/`

Examples:

Create a deployment from a local storage path `./path/to/blueprint` and stage-bucket `gs://my-bucket`:

  $ {command} projects/p1/location/us-central1/deployments/my-deployment --local-source="./path/to/blueprint" --stage-bucket="gs://my-bucket"
"""

  source_group = parser.add_group(mutex=False)

  input_values = source_group.add_mutually_exclusive_group()
  input_values.add_argument(
      '--input-values',
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
      help=input_values_help_text,
  )

  input_values.add_argument(
      '--inputs-file',
      help=inputs_file_help_text,
  )

  source_details = source_group.add_mutually_exclusive_group()

  source_details.add_argument(
      '--gcs-source',
      help=gcs_source_help_text,
  )

  git_source_group = source_details.add_group(mutex=False)

  git_source_group.add_argument(
      '--git-source-repo',
      help=git_source_repo_help,
  )

  git_source_group.add_argument(
      '--git-source-directory',
      help=git_source_directory_help,
  )

  git_source_group.add_argument(
      '--git-source-ref',
      help=git_source_ref_help,
  )

  local_source_group = source_details.add_group(mutex=False)

  local_source_group.add_argument(
      '--local-source',
      help=local_source_help,
  )

  local_source_group.add_argument(
      '--ignore-file',
      help=(
          'Override the `.gcloudignore` file and use the specified file '
          'instead. See `gcloud topic gcloudignore` for more information.'
      ),
  )

  # Note: we cannot specify a default here since the default value we would WANT
  # to use is dynamic; it includes the project ID.
  local_source_group.add_argument(
      '--stage-bucket',
      help=stage_bucket_help,
      hidden=True,
      # This will ensure that "--stage-bucket" takes on the form
      # "gs://my-bucket/".
      type=functions_api_util.ValidateAndStandarizeBucketUriOrRaise,
  )


def AddServiceAccountFlag(parser, hidden=False):
  """Add --service-account flag."""
  parser.add_argument(
      '--service-account',
      hidden=hidden,
      help=(
          'User-specified Service Account (SA) to be used as credential to'
          ' manage resources. Format:'
          ' `projects/{projectID}/serviceAccounts/{serviceAccount}`'
      ),
  )


def AddImportExistingResourcesFlag(parser, hidden=False):
  """Add --import-existing-resources flag."""
  parser.add_argument(
      '--import-existing-resources',
      hidden=hidden,
      action='store_true',
      help=(
          'By default, Infrastructure Manager will return a failure when'
          ' Terraform encounters a 409 code (resource conflict error) during'
          ' actuation. If this flag is set to true, Infrastructure Manager will'
          ' instead attempt to automatically import the resource into the'
          ' Terraform state (for supported resource types) and continue'
          ' actuation.'
      ),
  )


def AddWorkerPoolFlag(parser, hidden=False):
  """Add --worker-pool flag."""
  parser.add_argument(
      '--worker-pool',
      hidden=hidden,
      help=(
          'User-specified Worker Pool resource in which the Cloud Build job '
          'will execute. Format: '
          'projects/{project}/locations/{location}/workerPools/{workerPoolId}'
      ),
  )


def AddArtifactsGCSBucketFlag(parser, hidden=False):
  """Add --artifacts-gcs-bucket flag."""
  parser.add_argument(
      '--artifacts-gcs-bucket',
      hidden=hidden,
      help=(
          'user-defined location of Cloud Build logs, artifacts, and Terraform'
          ' state files in Google Cloud Storage. Format:'
          ' `gs://{bucket}/{folder}` A default bucket will be bootstrapped if'
          ' the field is not set or empty'
      ),
  )


def AddDraftFlag(parser, hidden=False):
  """Add --draft flag."""
  parser.add_argument(
      '--draft',
      hidden=hidden,
      help=(
          'If this flag is set to true, the exported deployment state file will'
          ' be the draft state'
      ),
      action='store_true',
  )


def AddLockFlag(parser, hidden=False):
  """Add --lock-id flag."""
  parser.add_argument(
      '--lock-id',
      required=True,
      hidden=hidden,
      help='Lock ID of the lock file to verify person importing owns lock.',
  )


def AddDeploymentFlag(parser, hidden=False):
  """Add --deployment flag."""
  parser.add_argument(
      '--deployment',
      hidden=hidden,
      help='Deployment reference for preview.',
  )


def AddPreviewModeFlag(parser, hidden=False):
  """Add --preview-mode flag."""
  parser.add_argument(
      '--preview-mode',
      hidden=hidden,
      help='Preview mode to set it to either default or delete.',
  )


def AddFileFlag(parser, help_text, hidden=False):
  """Add --file flag."""
  parser.add_argument(
      '--file',
      hidden=hidden,
      help=help_text,
  )


def AddTFVersionConstraintFlag(parser, hidden=False):
  """Add --tf-version-constraint flag."""
  parser.add_argument(
      '--tf-version-constraint',
      hidden=hidden,
      help=(
          'User-specified Terraform version constraint, for example "=1.3.10".'
      ),
  )


def AddQuotaValidationFlag(parser, hidden=False):
  """Add --quota-validation flag."""

  parser.add_argument(
      '--quota-validation',
      hidden=hidden,
      help=(
          'Input to control quota checks for resources in terraform'
          ' configuration files. There are limited resources on which quota'
          ' validation applies. Supported values are'
          ' QUOTA_VALIDATION_UNSPECIFIED, ENABLED, ENFORCED'
      ),
      type=QuotaValidationEnum,
  )


def QuotaValidationEnum(quota_validation):
  """Checks if a quota validation provided by user is valid and returns corresponding enum type.

  Args:
    quota_validation: value for quota validation.

  Returns:
    quota validation enum
  Raises:
    ArgumentTypeError: If the value provided by user is not valid.
  """
  messages = configmanager_util.GetMessagesModule()

  quota_validation_enum_dict = {
      'QUOTA_VALIDATION_UNSPECIFIED': (
          messages.Deployment.QuotaValidationValueValuesEnum.QUOTA_VALIDATION_UNSPECIFIED
      ),
      'ENABLED': messages.Deployment.QuotaValidationValueValuesEnum.ENABLED,
      'ENFORCED': messages.Deployment.QuotaValidationValueValuesEnum.ENFORCED,
  }
  if quota_validation is None:
    return
  if quota_validation not in quota_validation_enum_dict:
    raise arg_parsers.ArgumentTypeError(
        "quota validation does not support: '{0}', supported values are: {1}"
        .format(quota_validation, list(quota_validation_enum_dict))
    )
  return quota_validation_enum_dict[quota_validation]


# TODO: b/433318303 - Remove `hidden` annotation to make dark site
# commands public.
def AddProviderSourceFlag(parser, hidden=True):
  """Add --provider-source flag."""
  parser.add_argument(
      '--provider-source',
      hidden=hidden,
      help=(
          'Input to control from where to fetch providers. Supported values are'
          'PROVIDER_SOURCE_UNSPECIFIED, SERVICE_MAINTAINED'
      ),
      type=ProviderSourceEnum,
  )


def ProviderSourceEnum(provider_source):
  """Checks if a provider config provided by user is valid and returns corresponding enum type.

  Args:
    provider_source: value for provider source.

  Returns:
    provider source enum

  Raises:
    ArgumentTypeError: If the value provided by user is not valid.
  """
  messages = configmanager_util.GetMessagesModule()
  provider_source_enum_dict = {
      'PROVIDER_SOURCE_UNSPECIFIED': (
          messages.ProviderConfig.SourceTypeValueValuesEnum.PROVIDER_SOURCE_UNSPECIFIED
      ),
      'SERVICE_MAINTAINED': (
          messages.ProviderConfig.SourceTypeValueValuesEnum.SERVICE_MAINTAINED
      ),
  }
  if provider_source is None:
    return
  if provider_source not in provider_source_enum_dict:
    raise arg_parsers.ArgumentTypeError(
        "provider config does not support: '{0}', supported values are: {1}"
        .format(provider_source, list(provider_source_enum_dict))
    )
  return provider_source_enum_dict[provider_source]
