# -*- 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.
"""gcloud service-extensions wasm-actions create command."""


import textwrap

from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.command_lib.service_extensions import flags
from googlecloudsdk.command_lib.service_extensions import util
from googlecloudsdk.command_lib.util.apis import yaml_data
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import log
from googlecloudsdk.core import resources


def _GetPossibleValuesOfSupportedEvents():
  """Returns the possible values of the --supported-events flag.

  Returns:
    List of strings.
  """
  return ['request-headers', 'response-headers']


def _ConvertStringSupportedEventToEnum(messages, supported_event):
  """Converts the text representation of an event to enum.

  Args:
    messages: module containing the definitions of messages for the API.
    supported_event: string, for example 'request_headers'.

  Returns:
    a value of messages.WasmAction.SupportedEventsValueListEntryValuesEnum,
    for example
    messages.WasmAction.SupportedEventsValueListEntryValuesEnum.REQUEST_HEADERS
  """
  uppercase_event = supported_event.upper().replace('-', '_')
  if not hasattr(messages.WasmAction.SupportedEventsValueListEntryValuesEnum,
                 uppercase_event):
    # This is theoretically possible if the list of allowed values of
    # --supported-events (returned by _GetPossibleValuesOfSupportedEvents())
    # contains values from the v1alpha1 version that are not yet visible
    # in v1.
    raise ValueError('Unsupported value: ' + supported_event)
  return getattr(messages.WasmAction.SupportedEventsValueListEntryValuesEnum,
                 uppercase_event)


@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Create(base.CreateCommand):
  """Create a `WasmAction` resource."""

  detailed_help = {
      'DESCRIPTION':
          textwrap.dedent("""\
          Create a `WasmAction` resource that uses the specified plugin.
          """),
      'EXAMPLES':
          textwrap.dedent("""\
          To create a `WasmAction` called `my-action` using the
          `my-plugin` plugin, run:

          $ {command} my-action --wasm-plugin=my-plugin

          You may also specify the full resource path to a plugin, for example,
          `projects/my-project/locations/global/wasmPlugins/my-plugin`
          """)
  }

  @classmethod
  def Args(cls, parser):
    wasm_action_data = yaml_data.ResourceYAMLData.FromPath(
        'service_extensions.wasmAction')
    wasm_plugin_data = yaml_data.ResourceYAMLData.FromPath(
        'service_extensions.wasmPlugin')

    # Register the wasm_action and --wasm-plugin resource args. They
    # both provide a --location flag, so to avoid a conflict, we
    # configure --wasm-plugin to use wasm_action's location flag.
    concept_parsers.ConceptParser(
        [
            presentation_specs.ResourcePresentationSpec(
                'wasm_action',
                concepts.ResourceSpec.FromYaml(
                    wasm_action_data.GetData(),
                    api_version=util.GetApiVersion(cls.ReleaseTrack())),
                'The ID of the `WasmAction`.',
                required=True),
            presentation_specs.ResourcePresentationSpec(
                '--wasm-plugin',
                concepts.ResourceSpec.FromYaml(
                    wasm_plugin_data.GetData(),
                    api_version=util.GetApiVersion(cls.ReleaseTrack())),
                'ID of the `WasmPlugin` to use for this action.',
                flag_name_overrides={'location': ''},
                required=True)
        ],
        command_level_fallthroughs={
            '--wasm-plugin.location': ['wasm_action.location']
        }).AddToParser(parser)

    parser.add_argument(
        '--supported-events',
        type=arg_parsers.ArgList(choices=_GetPossibleValuesOfSupportedEvents()),
        required=False,
        metavar='EVENT',
        default=[],
        help=textwrap.dedent("""\
          Specify the portion of the request/response payload to be processed by
          the plugin."""),
    )
    base.ASYNC_FLAG.AddToParser(parser)
    labels_util.AddCreateLabelsFlags(parser)
    flags.AddDescriptionFlag(parser)

  def Run(self, args):
    api_version = util.GetApiVersion(self.ReleaseTrack())
    messages = apis.GetMessagesModule('networkservices', api_version)

    wasm_action_ref = args.CONCEPTS.wasm_action.Parse()
    wasm_plugin_ref = args.CONCEPTS.wasm_plugin.Parse()
    labels = labels_util.ParseCreateArgs(args, messages.WasmAction.LabelsValue)

    converted_events = [
        _ConvertStringSupportedEventToEnum(messages, event)
        for event in args.supported_events]

    request = messages.NetworkservicesProjectsLocationsWasmActionsCreateRequest(
        parent=wasm_action_ref.Parent().RelativeName(),
        wasmActionId=wasm_action_ref.Name(),
        wasmAction=messages.WasmAction(
            wasmPlugin=wasm_plugin_ref.RelativeName(),
            description=args.description,
            labels=labels,
            supportedEvents=converted_events,
        ),
    )

    # Issue the create request, which returns an operation.
    client = apis.GetClientInstance('networkservices', api_version)
    op_ref = client.projects_locations_wasmActions.Create(request)

    log.status.Print('Create request issued for: [{}]'.format(
        wasm_action_ref.Name()))

    if args.async_:
      log.status.Print('Check operation [{}] for status.'.format(op_ref.name))
      return op_ref

    # Wait for the operation to complete.
    op_resource = resources.REGISTRY.ParseRelativeName(
        op_ref.name,
        collection='networkservices.projects.locations.operations',
        api_version=api_version)
    poller = waiter.CloudOperationPoller(client.projects_locations_wasmActions,
                                         client.projects_locations_operations)
    result = waiter.WaitFor(
        poller, op_resource,
        'Waiting for operation [{}] to complete'.format(op_ref.name))

    log.status.Print('Created WasmAction [{}].'.format(wasm_action_ref.Name()))

    return result
