# -*- coding: utf-8 -*- #
# Copyright 2024 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.

"""Utility functions for `gcloud pam` commands."""


import re

from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.core import log
from googlecloudsdk.core import yaml


def SetForceFieldInDeleteEntitlementRequest(unused_ref, unused_args, req):
  """Modify request hook to set the force field in delete entitlement requests to true."""
  req.force = True
  return req


def ParseEntitlementNameIntoCreateEntitlementRequest(unused_ref, args, req):
  """Modify request hook to parse the entitlement name into a CreateEntitlementRequest."""
  entitlement = args.CONCEPTS.entitlement.Parse()
  req.parent = entitlement.result.Parent().RelativeName()
  req.entitlementId = entitlement.result.Name()
  return req


def SetUpdateMaskInUpdateEntitlementRequest(unused_ref, unused_args, req):
  """Modify request hook to set the update mask field in update entitlement requests to '*'."""
  req.updateMask = '*'
  return req


def FormatWithdrawResponse(response, unused_args):
  """Formats the response of the withdraw command."""
  modified_response = {}
  if response.name:
    modified_response['name'] = response.name
  if not response.metadata:
    return modified_response
  modified_response['metadata'] = {}
  properties = response.metadata.additionalProperties
  for prop in properties:
    if prop.key in ('apiVersion', 'createTime', 'target'):
      modified_response['metadata'][prop.key] = prop.value.string_value

  log.status.Print(
      'Grant withdrawal initiated. The operation will complete in some time. To'
      ' track its status, run:\n`gcloud pam operations wait {}`\nNote that'
      ' the wait command requires you to have the'
      ' `privilegedaccessmanager.operations.get` permission on the resource.'
      .format(response.name)
  )
  return modified_response


def GetApiVersionFromArgs(args):
  """Return API version based on args.

  Args:
    args: The argparse namespace.

  Returns:
    API version (e.g. v1alpha or v1beta).
  """
  release_track = args.calliope_command.ReleaseTrack()
  if release_track == base.ReleaseTrack.ALPHA:
    return 'v1alpha'
  if release_track == base.ReleaseTrack.BETA:
    return 'v1beta'
  if release_track == base.ReleaseTrack.GA:
    return 'v1'


def SetRequestedPrivilegedAccessInCreateGrantRequest(unused_ref, args, req):
  """Modify request hook to populate the requestedPrivilegedAccess field in create grant requests."""
  if not args.requested_resources:
    return req
  messages = apis.GetMessagesModule(
      'privilegedaccessmanager', GetApiVersionFromArgs(args)
  )
  if len(args.requested_resources) > 1:
    raise arg_parsers.ArgumentTypeError(
        'Only one resource is supported for grant scope.'
    )
  for resource in args.requested_resources:
    resource = resource.strip()
    pattern = r'^(projects|organizations|folders)\/.+'
    components = resource.split('/')
    if not re.match(pattern, resource) or len(components) != 2:
      raise arg_parsers.ArgumentTypeError(
          'Invalid resource name: {}. Resource name must be of the form'
          ' (projects|organizations|folders)/<id>.'.format(resource)
      )
    resource_type = (
        'cloudresourcemanager.googleapis.com/' + components[0].capitalize()[:-1]
    )
    full_name = '//cloudresourcemanager.googleapis.com/' + resource
    requested_privileged_access = messages.RequestedPrivilegedAccess()
    requested_privileged_access.gcpIamAccess = (
        messages.RequestedPrivilegedAccessGcpIamAccess()
    )
    requested_privileged_access.gcpIamAccess.resourceType = resource_type
    requested_privileged_access.gcpIamAccess.resource = full_name
    req.grant.requestedPrivilegedAccess.append(requested_privileged_access)

  return req


def LoadGrantScopeFromYaml(stream):
  """Loads a YAML document from a stream.

  This function takes a stream (expected to be a list with a single string
  element) and parses it as a YAML document. It returns the loaded YAML data as
  a Python object (typically a list or dictionary).

  Args:
    stream: The stream to load from.

  Returns:
   The loaded YAML data.
  """
  if not stream or not stream[0]:
    # Return an empty list if no file is provided or the file is empty.
    return []
  return yaml.load(stream[0])


# TODO(b/261183749): Remove modify_request_hook when singleton resource args
# are enabled in declarative.
def UpdateSettingsResource(unused_ref, unused_args, req):
  """Modify request hook to update the resource field in settings requests."""
  req.name = req.name + '/settings'
  return req


def SetUpdateMaskInUpdateSettingsRequest(unused_ref, unused_args, req):
  """Modify request hook to set the update mask field in update settings requests to '*'."""
  req.updateMask = '*'
  return req
