# -*- 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 describe the status of the Config Management Feature."""


from googlecloudsdk.api_lib.container.fleet import util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.fleet import resources
from googlecloudsdk.command_lib.container.fleet.config_management import utils
from googlecloudsdk.command_lib.container.fleet.features import base as features_base
from googlecloudsdk.core import log
from googlecloudsdk.core import yaml
from googlecloudsdk.core.util import semver


@base.Deprecate(
    is_removed=False,
    # TODO(b/435530306): Include beta release track in warning message.
    warning=(
        'This command is deprecated and will be removed in an upcoming release.'
        " Please use the new `describe` command's `--view=config` flag in the"
        " alpha release track instead."
    )
)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@base.DefaultUniverseOnly
class Fetch(features_base.DescribeCommand):
  """Prints the Config Management configuration applied to the given membership.

  The `fetch-for-apply` output uses the format of the apply command. Any fields
  not configured display default values.

  ## EXAMPLES

  To fetch the applied Config Management configuration, run:

    $ {command}
  """

  feature_name = 'configmanagement'

  @classmethod
  def Args(cls, parser):
    resources.AddMembershipResourceArg(parser)

  def Run(self, args):
    membership = features_base.ParseMembership(
        args, prompt=True, autoselect=True, search=True
    )

    f = self.GetFeature()
    membership_spec = None
    version, = utils.get_backfill_versions_from_feature(f, [membership])
    for full_name, spec in self.hubclient.ToPyDict(f.membershipSpecs).items():
      if (
          util.MembershipPartialName(full_name)
          == util.MembershipPartialName(membership)
          and spec is not None
      ):
        membership_spec = spec.configmanagement
    if membership_spec is None:
      log.status.Print('Membership {} not initialized'.format(membership))

    # load the config template and merge with config has been applied to the
    # feature spec
    template = yaml.load(utils.APPLY_SPEC_VERSION_1)
    full_config = template['spec']
    merge_config_sync(membership_spec, full_config, version)
    merge_policy_controller(membership_spec, full_config, version)
    merge_hierarchy_controller(membership_spec, full_config)

    return template


def merge_config_sync(spec, config, version):
  """Merge configSync set in feature spec with the config template.

  ConfigSync has nested object structs need to be flatten.

  Args:
    spec: the ConfigManagementMembershipSpec message
    config: the dict loaded from full config template
    version: the version string of the membership
  """
  if not spec or not spec.configSync:
    return
  cs = config[utils.CONFIG_SYNC]
  git = spec.configSync.git
  oci = spec.configSync.oci
  if spec.configSync.enabled is not None:
    cs['enabled'] = spec.configSync.enabled
  else:
    # when enabled is no set in feature spec, it's determined by syncRepo
    if (git and git.syncRepo) or (oci and oci.syncRepo):
      cs['enabled'] = True
  if spec.configSync.sourceFormat:
    cs['sourceFormat'] = spec.configSync.sourceFormat
  if not version or semver.SemVer(version) >= semver.SemVer(
      utils.PREVENT_DRIFT_VERSION
  ):
    if spec.configSync.preventDrift:
      cs['preventDrift'] = spec.configSync.preventDrift
  else:
    del cs['preventDrift']

  if not git and not oci:
    return
  # Update sourceType if version >= 1.12.0
  if not version or semver.SemVer(version) >= semver.SemVer(
      utils.OCI_SUPPORT_VERSION
  ):
    if git:
      cs['sourceType'] = 'git'
    elif oci:
      cs['sourceType'] = 'oci'
  else:
    del cs['sourceType']

  if cs['sourceType'] and cs['sourceType'] == 'oci':
    if oci.syncWaitSecs:
      cs['syncWait'] = oci.syncWaitSecs
    for field in [
        'policyDir',
        'secretType',
        'syncRepo',
        'gcpServiceAccountEmail',
    ]:
      if hasattr(oci, field) and getattr(oci, field) is not None:
        cs[field] = getattr(oci, field)
  else:
    if git.syncWaitSecs:
      cs['syncWait'] = git.syncWaitSecs
    for field in [
        'policyDir',
        'httpsProxy',
        'secretType',
        'syncBranch',
        'syncRepo',
        'syncRev',
        'gcpServiceAccountEmail',
    ]:
      if hasattr(git, field) and getattr(git, field) is not None:
        cs[field] = getattr(git, field)


def merge_policy_controller(spec, config, version):
  """Merge configSync set in feature spec with the config template.

  ConfigSync has nested object structs need to be flatten.

  Args:
    spec: the ConfigManagementMembershipSpec message
    config: the dict loaded from full config template
    version: the version string of the membership
  """
  if not spec or not spec.policyController:
    return
  c = config[utils.POLICY_CONTROLLER]
  for field in list(config[utils.POLICY_CONTROLLER]):
    if (
        hasattr(spec.policyController, field)
        and getattr(spec.policyController, field) is not None
    ):
      c[field] = getattr(spec.policyController, field)

  valid_version = not version or semver.SemVer(version) >= semver.SemVer(
      utils.MONITORING_VERSION
  )
  spec_monitoring = spec.policyController.monitoring
  if not valid_version:
    c.pop('monitoring', None)
  elif spec_monitoring:
    c['monitoring'] = spec_monitoring


def merge_hierarchy_controller(spec, config):
  if not spec or not spec.hierarchyController:
    return
  c = config[utils.HNC]
  for field in list(config[utils.HNC]):
    if (
        hasattr(spec.hierarchyController, field)
        and getattr(spec.hierarchyController, field) is not None
    ):
      c[field] = getattr(spec.hierarchyController, field)
