# -*- 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.
"""The command to update Config Management Feature."""


import apitools
from googlecloudsdk import api_lib
from googlecloudsdk import core
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.fleet import resources
from googlecloudsdk.command_lib.container.fleet.config_management import command
from googlecloudsdk.command_lib.container.fleet.config_management import utils
from googlecloudsdk.command_lib.container.fleet.features import base as fleet_base
from googlecloudsdk.command_lib.container.fleet.membershipfeatures import base as mf_base
from googlecloudsdk.command_lib.container.fleet.membershipfeatures import convert

# Pull out the example text so the example command can be one line without the
# py linter complaining. The docgen tool properly breaks it into multiple lines.
EXAMPLES = r"""
    To apply the [fleet-default membership configuration](https://cloud.google.com/kubernetes-engine/fleet-management/docs/manage-features)
    to `MEMBERSHIP_NAME`, run:

    $ {command} --membership=MEMBERSHIP_NAME --origin=FLEET

    To apply a membership configuration as a YAML file, prepare
    [apply-spec.yaml](https://cloud.google.com/anthos-config-management/docs/reference/gcloud-apply-fields#example_gcloud_apply_spec) then run:

      $ {command} --membership=MEMBERSHIP_NAME --config=APPLY-SPEC.YAML --version=VERSION
"""


# TODO(b/433355766): Add disclaimer once update command is in beta.
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
@base.DefaultUniverseOnly
class Apply(fleet_base.UpdateCommand, mf_base.UpdateCommand, command.Common):
  """Update a Config Management feature spec.

  Update a membership configuration for the Config Management feature in a
  fleet. This command errors if the Config Management feature is not enabled on
  the fleet.
  """

  detailed_help = {'EXAMPLES': EXAMPLES}

  feature_name = utils.CONFIG_MANAGEMENT_FEATURE_NAME
  mf_name = utils.CONFIG_MANAGEMENT_FEATURE_NAME

  @classmethod
  def Args(cls, parser):
    resources.AddMembershipResourceArg(parser)
    spec_group = parser.add_group(
        required=True,
        mutex=True,
        help=('Update the membership configuration either to the [fleet-default'
              ' membership configuration]('
              'https://cloud.google.com/kubernetes-engine/fleet-management/docs/manage-features)'
              ' with `--origin` or to a user-provided configuration with'
              ' `--config` and `--version`.'),
    )
    spec_group.add_argument(
        '--origin',
        choices=['FLEET'],
        help=('Updates the configuration of the target membership to the'
              ' current [fleet-default membership configuration]('
              'https://cloud.google.com/kubernetes-engine/fleet-management/docs/manage-features).'
              ' Errors if fleet-default membership configuration is not'
              ' enabled; see the `enable` command for more details.'),
    )
    config_group = spec_group.add_group(
        help=('Provide the entire membership configuration to update with'
              ' `--config` and `--version`.')
    )
    config_group.add_argument(
        '--config',
        required=True,
        help=('Path to YAML file that contains the configuration to update the'
              ' target membership to.'
              ' The file accepts the [following fields]('
              'https://cloud.google.com/anthos-config-management/docs/reference'
              '/gcloud-apply-fields).'),
    )
    config_group.add_argument(
        '--version',
        help=('Version of Config Management.'
              ' Equivalent to the [`spec.version`]('
              'https://cloud.google.com/anthos-config-management/docs/reference'
              '/gcloud-apply-fields#common)'
              ' field in the `--config` file.'
              ' Provides `--config` with a version in the absence of'
              ' `spec.version`.'
              ' Cannot specify this flag without `--config`; cannot set both'
              ' this flag and `spec.version`.'
              ' See [`spec.version`]('
              'https://cloud.google.com/anthos-config-management/docs/reference'
              '/gcloud-apply-fields#common)'
              ' for more details.')
    )

  def Run(self, args):
    # Initialize and defend against more than 1 call to Run.
    self.__feature_cache = None

    self.membership = fleet_base.ParseMembership(
        args, prompt=True, autoselect=True, search=True
    )
    feature_spec = self.messages.MembershipFeatureSpec()
    if args.origin:
      feature_spec.origin = self.messages.Origin(
          type=self.messages.Origin.TypeValueValuesEnum.FLEET
      )
    else:
      cm = self.parse_config_management(args.config)
      if cm.version and args.version:
        raise core.exceptions.Error(
            'Cannot set version in multiple flags: --version={} and the version'
            ' field in --config has value {}'.format(args.version, cm.version)
        )
      if args.version:
        cm.version = args.version
      if (not cm.version and
          cm.management !=
          self.messages.ConfigManagementMembershipSpec.ManagementValueValuesEnum.MANAGEMENT_AUTOMATIC):
        cm.version = self._get_backfill_version(self.membership)
      feature_spec.configmanagement = cm
    self._update_membership(feature_spec)

  # Not strictly necessary yet, but helps communicate that we only GetFeature
  # once per execution.
  def _get_feature_cache(self):
    """Gets the Config Management feature at most once per command execution.

    Returns:
      Cached Config Management feature.
    """
    if self.__feature_cache is None:
      # Raises a comprehensible error if feature not enabled.
      self.__feature_cache = self.GetFeature()
    return self.__feature_cache

  def _get_backfill_version(self, membership):
    """Get the value the version field in FeatureSpec should be set to.

    Args:
      membership: The full membership  name whose Spec will be backfilled.

    Returns:
      version: A string denoting the version field in MembershipConfig
    Raises: Error, if retrieving FeatureSpec of FeatureState fails
    """
    f = self._get_feature_cache()
    return utils.get_backfill_versions_from_feature(f, [membership])[0]

  def _update_membership(self, feature_spec):
    """Update the spec of the target membership to feature_spec.

    Args:
      feature_spec: gkehub API MembershipFeatureSpec to update to.

    Returns:
      Updated feature or membership feature, for projects migrated to v2 by Hub.
    """
    try:
      if not feature_spec.origin:
        membershipfeature = convert.ToV2MembershipFeature(
            self, self.membership, self.mf_name, feature_spec
        )
        return self.UpdateV2(self.membership, ['spec'], membershipfeature)
      else:
        return self.Update(['membership_specs'], self.messages.Feature(
            membershipSpecs=self.hubclient.ToMembershipSpecs({
                self.membership: feature_spec
            })
        ))
    except apitools.base.py.exceptions.HttpError as e:
      raise api_lib.util.exceptions.HttpException(e, '{message}')
