# -*- 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 Service Mesh Feature."""


from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.container.fleet import util
from googlecloudsdk.calliope import actions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.anthos.common import file_parsers
from googlecloudsdk.command_lib.container.fleet import resources
from googlecloudsdk.command_lib.container.fleet.features import base as features_base
from googlecloudsdk.command_lib.container.fleet.membershipfeatures import base as mf_base
from googlecloudsdk.command_lib.container.fleet.mesh import utils


# LINT.IfChange(update_v2)
def _RunUpdateV2(cmd, args):
  """Runs the update command implementation that is common across release tracks.

  For membership level spec update, we will use v2alpha API to directly update
  the membership feature resource.

  Args:
    cmd: the release track specific command
    args: the args passed to the command
  """

  is_fleet_update = args.fleet_default_member_config or getattr(
      args, 'modernization_compatibility', None
  )
  memberships = []
  resource = False
  update_mask = []

  # Deprecated non-resource arg
  if args.IsKnownAndSpecified('membership'):
    resource = False
    memberships = utils.ParseMemberships(args)
  elif not is_fleet_update:
    resource = True
    memberships = features_base.ParseMembershipsPlural(
        args, prompt=True, search=True
    )

  for membership in memberships:
    if not resource:
      membership = cmd.MembershipResourceName(membership)

    patch_v2 = cmd.messages_v2.FeatureSpec()

    try:
      existing_membership_feature = cmd.GetMembershipFeature(membership)
    except apitools_exceptions.HttpNotFoundError:
      existing_membership_feature = cmd.messages_v2.MembershipFeature()

    if existing_membership_feature.spec:
      patch_v2 = existing_membership_feature.spec

    if not patch_v2.servicemesh:
      patch_v2.servicemesh = cmd.messages_v2.ServiceMeshSpec()

    if hasattr(args, 'origin') and args.origin is not None:
      if args.origin == 'fleet':
        patch_v2.origin = cmd.messages_v2.Origin(
            type=cmd.messages_v2.Origin.TypeValueValuesEnum('FLEET')
        )

    if hasattr(args, 'management') and args.management is not None:
      management_v2 = (
          cmd.messages_v2.ServiceMeshSpec.ManagementValueValuesEnum(
              'MANAGEMENT_MANUAL'))
      if args.management == 'automatic':
        management_v2 = (
            cmd.messages_v2.ServiceMeshSpec.ManagementValueValuesEnum(
                'MANAGEMENT_AUTOMATIC'))
      if args.management == 'not-installed':
        management_v2 = (
            cmd.messages_v2.ServiceMeshSpec.ManagementValueValuesEnum(
                'MANAGEMENT_NOT_INSTALLED'))
      patch_v2.servicemesh.management = management_v2

    if args.control_plane is not None:
      control_plane_v2 = (
          cmd.messages_v2.ServiceMeshSpec.ControlPlaneValueValuesEnum(
              'MANUAL'))
      if args.control_plane == 'automatic':
        control_plane_v2 = (
            cmd.messages_v2.ServiceMeshSpec.ControlPlaneValueValuesEnum(
                'AUTOMATIC'))
      elif args.control_plane == 'unspecified':
        control_plane_v2 = (
            cmd.messages_v2.ServiceMeshSpec.ControlPlaneValueValuesEnum(
                'CONTROL_PLANE_MANAGEMENT_UNSPECIFIED'))
      patch_v2.servicemesh.controlPlane = control_plane_v2

    if hasattr(args, 'config_api') and args.config_api is not None:
      config_api = (
          cmd.messages_v2.ServiceMeshSpec.ConfigApiValueValuesEnum(
              'CONFIG_API_UNSPECIFIED'
          )
      )
      if args.config_api == 'istio':
        config_api = (
            cmd.messages_v2.ServiceMeshSpec.ConfigApiValueValuesEnum(
                'CONFIG_API_ISTIO'
            )
        )
      if args.config_api == 'gateway':
        config_api = (
            cmd.messages_v2.ServiceMeshSpec.ConfigApiValueValuesEnum(
                'CONFIG_API_GATEWAY'
            )
        )
      patch_v2.servicemesh.configApi = config_api

    cmd.UpdateV2(
        membership, ['spec'], cmd.messages_v2.MembershipFeature(spec=patch_v2)
    )

  f = cmd.messages.Feature()

  if args.fleet_default_member_config:
    # Load config YAML file.
    loaded_config = file_parsers.YamlConfigFile(
        file_path=args.fleet_default_member_config,
        item_type=utils.FleetDefaultMemberConfigObject,
    )

    # Create new service mesh feature spec.
    member_config = utils.ParseFleetDefaultMemberConfigV2(
        loaded_config, cmd.messages
    )
    f.fleetDefaultMemberConfig = (
        cmd.messages.CommonFleetDefaultMemberConfigSpec(mesh=member_config)
    )
    update_mask.append('fleet_default_member_config')
  elif getattr(args, 'modernization_compatibility', None):
    spec = cmd.messages.ServiceMeshFeatureSpec()
    mod_compat_enum = (
        cmd.messages.ServiceMeshFeatureSpec
        .ModernizationCompatibilityValueValuesEnum
    )
    if args.modernization_compatibility == 'validation-enabled':
      spec.modernizationCompatibility = mod_compat_enum.VALIDATION_ENABLED
    elif args.modernization_compatibility == 'validation-disabled':
      spec.modernizationCompatibility = mod_compat_enum.VALIDATION_DISABLED
    f.spec = cmd.messages.CommonFeatureSpec(mesh=spec)
    update_mask.append('spec.mesh.modernizationCompatibility')

  if update_mask:
    cmd.Update(update_mask, f)
# LINT.ThenChange(:update_v1)


# LINT.IfChange(update_v1)
def _RunUpdate(cmd, args):
  """Runs the update command implementation that is common across release tracks.

  Args:
    cmd: the release track specific command
    args: the args passed to the command
  """

  is_fleet_update = args.fleet_default_member_config or getattr(
      args, 'modernization_compatibility', None
  )
  memberships = []
  resource = False
  update_mask = []

  # Deprecated non-resource arg
  if args.IsKnownAndSpecified('membership'):
    resource = False
    memberships = utils.ParseMemberships(args)
    update_mask = ['membershipSpecs']
  elif not is_fleet_update:
    resource = True
    memberships = features_base.ParseMembershipsPlural(
        args, prompt=True, search=True
    )
    update_mask = ['membershipSpecs']

  f = cmd.GetFeature()
  membership_specs = {}
  for membership in memberships:
    if not resource:
      membership = cmd.MembershipResourceName(membership)
    patch = cmd.messages.MembershipFeatureSpec()

    for name, spec in cmd.hubclient.ToPyDict(f.membershipSpecs).items():
      if (
          util.MembershipShortname(name) == util.MembershipShortname(membership)
          and spec
      ):
        patch = spec
    if not patch.mesh:
      patch.mesh = cmd.messages.ServiceMeshMembershipSpec()

    if hasattr(args, 'origin') and args.origin is not None:
      if args.origin == 'fleet':
        patch.origin = cmd.messages.Origin(
            type=cmd.messages.Origin.TypeValueValuesEnum('FLEET')
        )

    if hasattr(args, 'management') and args.management is not None:
      management = (
          cmd.messages.ServiceMeshMembershipSpec.ManagementValueValuesEnum(
              'MANAGEMENT_MANUAL'
          )
      )
      if args.management == 'automatic':
        management = (
            cmd.messages.ServiceMeshMembershipSpec.ManagementValueValuesEnum(
                'MANAGEMENT_AUTOMATIC'
            )
        )
      if args.management == 'not-installed':
        management = (
            cmd.messages_v2.ServiceMeshSpec.ManagementValueValuesEnum(
                'MANAGEMENT_NOT_INSTALLED'))
      patch.mesh.management = management

    if args.control_plane is not None:
      control_plane = (
          cmd.messages.ServiceMeshMembershipSpec.ControlPlaneValueValuesEnum(
              'MANUAL'
          )
      )
      if args.control_plane == 'automatic':
        control_plane = (
            cmd.messages.ServiceMeshMembershipSpec.ControlPlaneValueValuesEnum(
                'AUTOMATIC'
            )
        )
      elif args.control_plane == 'unspecified':
        control_plane = (
            cmd.messages.ServiceMeshMembershipSpec.ControlPlaneValueValuesEnum(
                'CONTROL_PLANE_MANAGEMENT_UNSPECIFIED'
            )
        )
      patch.mesh.controlPlane = control_plane

    if hasattr(args, 'config_api') and args.config_api is not None:
      config_api = (
          cmd.messages.ServiceMeshMembershipSpec.ConfigApiValueValuesEnum(
              'CONFIG_API_UNSPECIFIED'
          )
      )
      if args.config_api == 'istio':
        config_api = (
            cmd.messages.ServiceMeshMembershipSpec.ConfigApiValueValuesEnum(
                'CONFIG_API_ISTIO'
            )
        )
      if args.config_api == 'gateway':
        config_api = (
            cmd.messages.ServiceMeshMembershipSpec.ConfigApiValueValuesEnum(
                'CONFIG_API_GATEWAY'
            )
        )
      patch.mesh.configApi = config_api

    membership_specs[membership] = patch

  f = cmd.messages.Feature(
      membershipSpecs=cmd.hubclient.ToMembershipSpecs(membership_specs)
  )

  if args.fleet_default_member_config:
    # Load config YAML file.
    loaded_config = file_parsers.YamlConfigFile(
        file_path=args.fleet_default_member_config,
        item_type=utils.FleetDefaultMemberConfigObject,
    )

    # Create new service mesh feature spec.
    member_config = utils.ParseFleetDefaultMemberConfig(
        loaded_config, cmd.messages
    )
    f.fleetDefaultMemberConfig = (
        cmd.messages.CommonFleetDefaultMemberConfigSpec(mesh=member_config)
    )
    update_mask.append('fleet_default_member_config')
  elif getattr(args, 'modernization_compatibility', None):
    spec = cmd.messages.ServiceMeshFeatureSpec()
    mod_compat_enum = (
        cmd.messages.ServiceMeshFeatureSpec
        .ModernizationCompatibilityValueValuesEnum
    )
    if args.modernization_compatibility == 'validation-enabled':
      spec.modernizationCompatibility = mod_compat_enum.VALIDATION_ENABLED
    elif args.modernization_compatibility == 'validation-disabled':
      spec.modernizationCompatibility = mod_compat_enum.VALIDATION_DISABLED
    f.spec = cmd.messages.CommonFeatureSpec(mesh=spec)
    update_mask.append('spec.mesh.modernizationCompatibility')

  cmd.Update(update_mask, f)
# LINT.ThenChange(:update_v2)


@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateAlpha(features_base.UpdateCommand, mf_base.UpdateCommand):
  """Update the configuration of the Service Mesh Feature.

  Update the Service Mesh Feature Spec of a membership.

  ## EXAMPLES

  To update the control plane management of comma separated memberships like
  `MEMBERSHIP1,MEMBERSHIP2`, run:

    $ {command} --memberships=MEMBERSHIP1,MEMBERSHIP2
      --control-plane=CONTROL_PLANE
  """

  feature_name = 'servicemesh'
  mf_name = 'servicemesh'

  @staticmethod
  def Args(parser):
    args_group = parser.add_mutually_exclusive_group(required=True)

    args_group.add_argument(
        '--fleet-default-member-config',
        type=str,
        help="""The path to a service-mesh.yaml configuration file.

        To enable the Service Mesh Feature with a fleet-level default
        membership configuration, run:

          $ {command} --fleet-default-member-config=/path/to/service-mesh.yaml""",
    )
    args_group.add_argument(
        '--modernization-compatibility',
        choices=['validation-enabled', 'validation-disabled'],
        help="""The modernization compatibility of the mesh.

        To set the modernization compatibility of the mesh, run:

          $ {command} --modernization-compatibility=validation-enabled""",
    )

    membership_group = args_group.add_group(
        'Component options',
    )

    membership_names_group = membership_group.add_mutually_exclusive_group()
    resources.AddMembershipResourceArg(
        membership_names_group,
        plural=True,
        membership_help=(
            'Membership names to update, separated by commas if '
            'multiple are supplied.'
        ),
    )
    membership_names_group.add_argument(
        '--membership',
        type=str,
        help='Membership name to update.',
        action=actions.DeprecationAction(
            '--membership',
            warn=(
                'The {flag_name} flag is now '
                'deprecated. Please use `--memberships` '
                'instead.'
            ),
        ),
    )

    membership_config_group = membership_group.add_group(required=True)
    membership_config_group.add_argument(
        '--origin',
        choices=['fleet'],
        help='Changing the origin of the membership.',
    )
    membership_controlplane_group = (
        membership_config_group.add_mutually_exclusive_group()
    )

    membership_controlplane_group.add_argument(
        '--config-api',
        choices=['istio', 'gateway'],
        help='The API to use for mesh configuration.',
    )
    membership_controlplane_group.add_argument(
        '--management',
        choices=['automatic', 'manual', 'not-installed'],
        help='The management mode to update to.',
    )
    membership_controlplane_group.add_argument(
        '--control-plane',
        choices=['automatic', 'manual', 'unspecified'],
        help='Control plane management to update to.',
        action=actions.DeprecationAction(
            '--control-plane',
            warn=(
                'The {flag_name} flag is now '
                'deprecated. Please use `--management` '
                'instead. '
                'See https://cloud.google.com/service-mesh/docs/managed/provision-managed-anthos-service-mesh'
            ),
        ),
    )

  def Run(self, args):
    # If the user is using the fleet default config, we will still use the v1
    # Feature API for the update.
    use_fleet_default_config = (
        hasattr(args, 'origin')
        and args.origin == 'fleet'
    )
    if not use_fleet_default_config:
      _RunUpdateV2(self, args)
    else:
      _RunUpdate(self, args)


@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class UpdateGA(features_base.UpdateCommand, mf_base.UpdateCommand):
  """Update the configuration of the Service Mesh Feature.

  Update the Service Mesh Feature Spec of a Membership.

  ## EXAMPLES

  To update the control plane management of comma separated Memberships like
  `membership1,membership2`, run:

    $ {command} --memberships=membership1,membership2
      --control-plane=CONTROL_PLANE
  """

  feature_name = 'servicemesh'
  mf_name = 'servicemesh'

  @staticmethod
  def Args(parser):
    args_group = parser.add_mutually_exclusive_group(required=True)

    args_group.add_argument(
        '--fleet-default-member-config',
        type=str,
        help="""The path to a service-mesh.yaml configuration file.

        To enable the Service Mesh Feature with a fleet-level default
        membership configuration, run:

          $ {command} --fleet-default-member-config=/path/to/service-mesh.yaml""",
    )

    membership_group = args_group.add_group()

    membership_names_group = membership_group.add_mutually_exclusive_group()
    resources.AddMembershipResourceArg(
        membership_names_group,
        plural=True,
        membership_help=(
            'Membership names to update, separated by commas if '
            'multiple are supplied.'
        ),
    )

    membership_configs_group = membership_group.add_group(required=True)
    membership_configs_group.add_argument(
        '--origin',
        choices=['fleet'],
        help='Changing the origin of the membership.',
    )
    membership_controlplane_group = (
        membership_configs_group.add_mutually_exclusive_group()
    )

    membership_controlplane_group.add_argument(
        '--config-api',
        choices=['istio', 'gateway'],
        help='The API to use for mesh configuration.',
    )
    membership_controlplane_group.add_argument(
        '--management',
        choices=['automatic', 'manual'],
        help='The management mode to update to.',
    )
    membership_controlplane_group.add_argument(
        '--control-plane',
        choices=['automatic', 'manual', 'unspecified'],
        help='Control plane management to update to.',
        action=actions.DeprecationAction(
            '--control-plane',
            warn=(
                'The {flag_name} flag is now '
                'deprecated. Please use `--management` '
                'instead. '
                'See https://cloud.google.com/service-mesh/docs/managed/provision-managed-anthos-service-mesh'
            ),
        ),
    )

  def Run(self, args):
    # If the user is using the fleet default config, we will still use the v1
    # Feature API for the update.
    use_fleet_default_config = (
        hasattr(args, 'origin')
        and args.origin == 'fleet'
    )
    if not use_fleet_default_config:
      _RunUpdateV2(self, args)
    else:
      _RunUpdate(self, args)
