# -*- coding: utf-8 -*- #
# Copyright 2021 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 for the deploy command group."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import textwrap

from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base

_SOURCE_HELP_TEXT = """
The location of the source that contains skaffold.yaml. The location can be a directory on a local disk or a gzipped archive file (.tar.gz) in Google Cloud Storage.
 If the source is a local directory, this command skips the files specified in the --ignore-file. If --ignore-file is not specified, use.gcloudignore file. If a .gcloudignore file is absent and a .gitignore file is present in the local source directory, gcloud will use a generated Git-compatible .gcloudignore file that respects your .gitignored files.
 The global .gitignore is not respected. For more information on .gcloudignore, see gcloud topic gcloudignore.
"""


def AddGcsSourceStagingDirFlag(parser, hidden=False):
  """Adds a Google Cloud Storage directory flag for staging the build."""
  parser.add_argument(
      '--gcs-source-staging-dir',
      hidden=hidden,
      help=(
          'A directory in Google Cloud Storage to copy the source used for '
          'staging the build. If the specified bucket does not exist, Cloud '
          "Deploy will create one. If you don't set this field, "
          '```gs://[DELIVERY_PIPELINE_ID]_clouddeploy/source``` is used.'
      ),
  )


def AddIgnoreFileFlag(parser, hidden=False):
  """Adds an ignore file flag."""
  parser.add_argument(
      '--ignore-file',
      hidden=hidden,
      help=(
          'Override the `.gcloudignore` file and use the specified file '
          'instead.'
      ),
  )


def AddToTargetFlag(parser, hidden=False):
  """Adds to-target flag."""
  parser.add_argument(
      '--to-target',
      hidden=hidden,
      help='Specifies a target to deliver into upon release creation',
  )


def AddImagesGroup(parser, hidden=False):
  """Adds Images flag."""
  images_group = parser.add_mutually_exclusive_group()
  images_group.add_argument(
      '--images',
      metavar='NAME=TAG',
      type=arg_parsers.ArgDict(),
      hidden=hidden,
      help=textwrap.dedent("""\
      Reference to a collection of individual image name to image full path replacements.

      For example:

          $ gcloud deploy releases create foo \\
              --images image1=path/to/image1:v1@sha256:45db24
      """),
  )
  images_group.add_argument(
      '--build-artifacts',
      hidden=hidden,
      help=(
          'Reference to a Skaffold build artifacts output file from skaffold'
          " build --file-output=BUILD_ARTIFACTS. If you aren't using Skaffold,"
          ' use the --images flag below to specify the'
          ' image-names-to-tagged-image references.'
      ),
  )


def AddConfigFile(parser, hidden=False):
  """Adds config flag."""
  parser.add_argument(
      '--file',
      hidden=hidden,
      required=True,
      help=(
          'Path to yaml file containing Delivery Pipeline(s), Target(s)'
          ' declarative definitions.'
      ),
  )


def AddToTarget(parser, hidden=False):
  """Adds to-target flag."""
  parser.add_argument(
      '--to-target', hidden=hidden, help='Destination target to promote into.'
  )


def AddRolloutID(parser, hidden=False):
  """Adds rollout-id flag."""
  parser.add_argument(
      '--rollout-id',
      hidden=hidden,
      help='ID to assign to the generated rollout for promotion.',
  )


def AddRelease(parser, help_text, hidden=False):
  """Adds release flag."""
  parser.add_argument('--release', hidden=hidden, help=help_text)


def AddForce(parser, help_text, hidden=False):
  """Adds force flag."""
  parser.add_argument(
      '--force',
      hidden=hidden,
      action='store_true',
      help=help_text,
  )


def AddDescription(parser, help_text, name='--description'):
  """Adds description related flag."""
  parser.add_argument(
      name,
      help=help_text,
  )


def AddDeliveryPipeline(parser, required=True):
  """Adds delivery pipeline flag."""
  parser.add_argument(
      '--delivery-pipeline',
      help='The name of the Cloud Deploy delivery pipeline',
      required=required,
  )


def AddAnnotationsFlag(parser, resource_type):
  """Adds --annotations flag."""
  help_text = textwrap.dedent("""\
  Annotations to apply to the %s. Annotations take the form of key/value string pairs.

  Examples:

  Add annotations:

    $ {command} --annotations="from_target=test,status=stable"

  """) % (resource_type)

  parser.add_argument(
      '--annotations',
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
      help=help_text,
  )


def AddLabelsFlag(parser, resource_type):
  """Add --labels flag."""
  help_text = textwrap.dedent("""\
  Labels to apply to the %s. Labels take the form of key/value string pairs.

  Examples:

  Add labels:

    $ {command} --labels="commit=abc123,author=foo"

""") % (resource_type)

  parser.add_argument(
      '--labels',
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
      help=help_text,
  )


def AddDockerVersion(parser):
  """Adds docker version flag."""
  parser.add_argument(
      '--docker-version',
      help='Version of the Docker binary.',
      hidden=True,
      type=str,
  )


def AddHelmVersion(parser):
  """Adds helm version flag."""
  parser.add_argument(
      '--helm-version',
      help='Version of the Helm binary.',
      hidden=True,
      type=str,
  )


def AddKptVersion(parser):
  """Adds kpt version flag."""
  parser.add_argument(
      '--kpt-version',
      help='Version of the Kpt binary.',
      hidden=True,
      type=str,
  )


def AddKubectlVersion(parser):
  """Adds kubectl version flag."""
  parser.add_argument(
      '--kubectl-version',
      help='Version of the Kubectl binary.',
      hidden=True,
      type=str,
  )


def AddKustomizeVersion(parser):
  """Adds kustomize version flag."""
  parser.add_argument(
      '--kustomize-version',
      help='Version of the Kustomize binary.',
      hidden=True,
      type=str,
  )


def AddSkaffoldVersion(parser):
  """Adds skaffold version flag."""
  parser.add_argument(
      '--skaffold-version', help='Version of the Skaffold binary.', type=str
  )


def AddSkaffoldFileFlag():
  """Add --skaffold-file flag."""
  help_text = textwrap.dedent("""\
  Path of the skaffold file absolute or relative to the source directory.

  Examples:

  Use Skaffold file with relative path:
  The current working directory is expected to be some part of the skaffold path (e.g. the current working directory could be /home/user)

    $ {command} --source=/home/user/source --skaffold-file=config/skaffold.yaml

  The skaffold file absolute file path is expected to be:
  /home/user/source/config/skaffold.yaml


  Use Skaffold file with absolute path and with or without source argument:


    $ {command} --source=/home/user/source --skaffold-file=/home/user/source/config/skaffold.yaml

    $ {command} --skaffold-file=/home/user/source/config/skaffold.yaml

  """)
  return base.Argument('--skaffold-file', help=help_text)


def AddDeployConfigFileFlag(hidden=True):
  """Add --deploy-config-file flag."""
  help_text = textwrap.dedent("""\
  Path of the deploy config file absolute or relative to the source directory.

  Examples:

  Use deploy config file with relative path:
  The current working directory is expected to be some part of the deploy config path (e.g. the current working directory could be /home/user)

    $ {command} --source=/home/user/source --deploy-config-file=config/deploy-config.yaml

  The deploy config file absolute file path is expected to be:
  /home/user/source/config/deploy-config.yaml


  Use deploy config file with absolute path and with or without source argument:


    $ {command} --source=/home/user/source --deploy-config-file=/home/user/source/config/deploy-config.yaml

    $ {command} --deploy-config-file=/home/user/source/config/deploy-config.yaml

  """)
  return base.Argument(
      '--deploy-config-file', help=help_text, hidden=hidden
  )


def AddSourceFlag():
  """Adds source flag."""
  return base.Argument(
      '--source', help=_SOURCE_HELP_TEXT, default='.'
  )  # By default, the current directory is used.


def AddKubernetesFileFlag():
  return base.Argument(
      '--from-k8s-manifest',
      help=(
          'The path to a Kubernetes manifest, which Cloud Deploy will use to '
          'generate a skaffold.yaml file for you (for example, '
          'foo/bar/k8.yaml). The generated Skaffold file will be available in '
          'the Google Cloud Storage source staging directory (see '
          '--gcs-source-staging-dir flag) after the release is complete.'
      ),
  )


def AddCloudRunFileFlag():
  return base.Argument(
      '--from-run-manifest',
      help=(
          'The path to a Cloud Run manifest, which Cloud Deploy will use to'
          ' generate a skaffold.yaml file for you (for example,'
          ' foo/bar/service.yaml). The generated Skaffold file will be'
          ' available in the Google Cloud Storage source staging directory (see'
          ' --gcs-source-staging-dir flag) after the release is complete.'
      ),
  )


def AddConfigSourcesGroup(parser):
  """Add config sources."""
  config_group = parser.add_mutually_exclusive_group()
  AddKubernetesFileFlag().AddToParser(config_group)
  AddCloudRunFileFlag().AddToParser(config_group)

  source_group = config_group.add_group(mutex=False)
  AddSourceFlag().AddToParser(source_group)

  config_file_group = source_group.add_group(mutex=True)
  AddSkaffoldFileFlag().AddToParser(config_file_group)
  AddDeployConfigFileFlag().AddToParser(config_file_group)


def AddDescriptionFlag(parser):
  """Add --description flag."""
  parser.add_argument(
      '--description',
      help='Description of rollout created during a rollback.',
      hidden=False,
      default=None,
      required=False,
  )


def AddListAllPipelines(parser):
  """Add --list-all-pipelines flag."""
  help_text = textwrap.dedent("""\
  List all Delivery Pipelines associated with a target.

  Usage:

    $ {command} --list-all-pipelines

""")

  parser.add_argument(
      '--list-all-pipelines', action='store_true', default=None, help=help_text
  )


def AddSkipPipelineLookup(parser):
  """Add --skip-pipeline-lookup flag."""
  help_text = textwrap.dedent("""\
  If set, skip fetching details of associated pipelines when describing a target.

  Usage:

    $ {command} --skip-pipeline-lookup

""")

  parser.add_argument(
      '--skip-pipeline-lookup',
      action='store_true',
      default=False,
      help=help_text,
  )


def AddRollbackOfRollout(parser):
  """Add --rollback-of-rollout flag."""
  help_text = textwrap.dedent("""\
  If set, this validates whether the rollout name specified by the flag matches
  the rollout on the target.

  Examples:

  Validate that `test-rollout` is the rollout to rollback on the target.

    $ {command} --rollback-of-rollout=projects/test-project/locations/us-central1/deliveryPipelines/test-pipeline/releases/test-release/rollouts/test-rollout

  """)

  parser.add_argument(
      '--rollback-of-rollout',
      help=help_text,
      hidden=False,
      # By default, None is used.
      default=None,
      required=False,
  )


def AddStartingPhaseId(parser):
  """Add --starting-phase-id flag."""
  help_text = textwrap.dedent("""\
  If set, starts the created rollout at the specified phase.

  Start rollout at `stable` phase:

    $ {command} --starting-phase-id=stable

  """)

  parser.add_argument(
      '--starting-phase-id',
      help=help_text,
      hidden=False,
      # By default, None is used.
      default=None,
      required=False,
  )


def AddInitialRolloutLabelsFlag():
  """Add --initial-rollout-labels flag."""
  help_text = textwrap.dedent("""\
  Labels to apply to the initial rollout when creating the release. Labels take
  the form of key/value string pairs.

  Examples:

  Add labels:

    $ {command} initial-rollout-labels="commit=abc123,author=foo"

""")
  return base.Argument(
      '--initial-rollout-labels',
      help=help_text,
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
  )


def AddInitialRolloutAnnotationsFlag():
  """Adds --initial-rollout-annotations flag."""
  help_text = textwrap.dedent("""\
  Annotations to apply to the initial rollout when creating the release.
  Annotations take the form of key/value string pairs.

  Examples:

  Add annotations:

    $ {command} --initial-rollout-annotations="from_target=test,status=stable"

  """)

  return base.Argument(
      '--initial-rollout-annotations',
      help=help_text,
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
  )


def AddInitialRolloutPhaseIDFlag():
  """Adds --initial-rollout-phase-id flag."""
  help_text = textwrap.dedent("""\
  The phase to start the initial rollout at when creating the release.
  The phase ID must be a valid phase on the rollout. If not specified, then the
  rollout will start at the first phase.

  Examples:

  Start rollout at `stable` phase:

    $ {command} --initial-rollout-phase-id=stable

  """)

  return base.Argument(
      '--initial-rollout-phase-id',
      help=help_text,
      hidden=False,
      # By default, None is used.
      default=None,
      required=False,
  )


def AddEnableInitialRolloutFlag():
  """Adds --enable-initial-rollout flag."""

  return base.Argument(
      '--enable-initial-rollout',
      action='store_const',
      help=(
          'Creates a rollout in the first target defined in the delivery'
          ' pipeline. This is the default behavior.'
      ),
      const=True,
  )


def AddDisableInitialRolloutFlag():
  """Adds --disable-initial-rollout flag."""

  return base.Argument(
      '--disable-initial-rollout',
      action='store_const',
      help=(
          'Skips creating a rollout in the first target defined in the delivery'
          ' pipeline.'
      ),
      const=True,
  )


def AddInitialRolloutGroup(parser):
  """Adds initial-rollout flag group."""
  group = parser.add_mutually_exclusive_group()
  # Create a group that contains the flags to enable an initial rollout and add
  # labels and annotations to that rollout. The group itself is mutually
  # exclusive of the disable initial rollout group.
  enable_initial_rollout_group = group.add_group(mutex=False)
  AddInitialRolloutLabelsFlag().AddToParser(enable_initial_rollout_group)
  AddInitialRolloutAnnotationsFlag().AddToParser(enable_initial_rollout_group)
  AddInitialRolloutPhaseIDFlag().AddToParser(enable_initial_rollout_group)
  AddEnableInitialRolloutFlag().AddToParser(enable_initial_rollout_group)
  # Add the disable initial rollout flag to the mutex group.
  AddDisableInitialRolloutFlag().AddToParser(group)


def AddJobId(parser, hidden=False):
  """Adds job-id flag."""
  parser.add_argument(
      '--job-id',
      hidden=hidden,
      help='Job ID on a rollout resource',
      required=True,
  )


def AddPhaseId(parser, required=True, hidden=False):
  """Adds phase-id flag."""
  parser.add_argument(
      '--phase-id',
      hidden=hidden,
      help='Phase ID on a rollout resource',
      required=required,
  )


def AddDeployParametersFlag(parser, hidden=False):
  """Add --deploy-parameters flag."""
  help_text = textwrap.dedent("""\
  Deployment parameters to apply to the release. Deployment parameters take the form of key/value string pairs.

  Examples:

  Add deployment parameters:

    $ {command} --deploy-parameters="key1=value1,key2=value2"

""")

  parser.add_argument(
      '--deploy-parameters',
      metavar='KEY=VALUE',
      type=arg_parsers.ArgDict(),
      hidden=hidden,
      help=help_text,
  )


def AddOverrideDeployPolicies(parser, hidden=False):
  """Adds override-deploy-policies flag."""
  parser.add_argument(
      '--override-deploy-policies',
      metavar='POLICY',
      hidden=hidden,
      type=arg_parsers.ArgList(),
      help='Deploy policies to override',
  )
