# -*- coding: utf-8 -*- #
# Copyright 2015 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.
"""Command for cache invalidation."""


from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.compute import batch_helper
from googlecloudsdk.api_lib.compute import utils
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute import exceptions as compute_exceptions
from googlecloudsdk.command_lib.compute import scope as compute_scope
from googlecloudsdk.command_lib.compute.url_maps import flags
from googlecloudsdk.command_lib.compute.url_maps import url_maps_utils
from googlecloudsdk.core import log


def _DetailedHelp():
  return {
      'brief':
          'Invalidate specified objects for a URL map in Cloud CDN caches.',
      'DESCRIPTION':
          """\
      *{command}* requests that Cloud CDN stop using cached content for
      resources at a particular URL path or set of URL paths.

      *{command}* may succeed even if no content is cached for some or all
      URLs with the given path.
      """,
  }


def _Args(parser):
  """Add invalidate-cdn-cache arguments to the parser."""

  parser.add_argument(
      '--path',
      required=False,
      help="""\
      A path specifying which objects to invalidate. PATH must start with
      ``/'' and the only place a ``*'' is allowed is at the end following a
      ``/''. It will be matched against URL paths, which do not include
      scheme, host, or any text after the first ``?'' or ``#'' (and those
      characters are not allowed here). For example, for the URL
      ``https://example.com/whatever/x.html?a=b'', the path is
      ``/whatever/x.html''.

      If PATH ends with ``*'', the preceding string is a prefix, and all URLs
      whose paths begin with it will be invalidated. If PATH doesn't end with
      ``*'', then only URLs with exactly that path will be invalidated.

      Examples:
      - ``'', ``*'', anything that doesn't start with ``/'': error
      - ``/'': just the root URL
      - ``/*'': everything
      - ``/x/y'': ``/x/y'' only (and not ``/x/y/'')
      - ``/x/y/'': ``/x/y/'' only (and not ``/x/y'')
      - ``/x/y/*'': ``/x/y/'' and everything under it
      """)

  parser.add_argument(
      '--host',
      required=False,
      default=None,
      help="""\
      If set, this invalidation will apply only to requests to the
      specified host.
      """)

  parser.add_argument(
      '--tags',
      required=False,
      default=None,
      help="""\
      A single tag or a comma-delimited list of tags. When multiple tags are
      specified, the invalidation applies them using boolean OR logic.

      Example:
      - ``--tags=abcd,user123''
      """)

  base.ASYNC_FLAG.AddToParser(parser)


def _CreateRequests(holder, args, url_map_arg):
  """Returns a list of requests necessary for cache invalidations."""
  url_map_ref = url_map_arg.ResolveAsResource(
      args, holder.resources, default_scope=compute_scope.ScopeEnum.GLOBAL)
  if url_maps_utils.IsRegionalUrlMapRef(url_map_ref):
    raise compute_exceptions.ArgumentError(
        'Invalid flag [--region]:'
        ' Regional URL maps do not support Cloud CDN caching.'
    )

  cache_invalidation_rule = holder.client.messages.CacheInvalidationRule(
      path=args.path)
  if args.host is not None:
    cache_invalidation_rule.host = args.host

  if hasattr(args, 'tags') and args.tags is not None:
    cache_invalidation_rule.cacheTags.extend(
        [tag.strip() for tag in args.tags.split(',')])

  if hasattr(cache_invalidation_rule, 'httpStatus'):
    if hasattr(args, 'http_status') and args.http_status is not None:
      if args.http_status < 200 or args.http_status > 599:
        raise compute_exceptions.ArgumentError(
            'Invalid value for [--http-status]: Must be between 200 and 599.'
        )
      cache_invalidation_rule.httpStatus = args.http_status

  messages = holder.client.messages
  request = messages.ComputeUrlMapsInvalidateCacheRequest(
      project=url_map_ref.project,
      urlMap=url_map_ref.Name(),
      cacheInvalidationRule=cache_invalidation_rule)
  collection = holder.client.apitools_client.urlMaps

  return [(collection, 'InvalidateCache', request)]


def _Run(args, holder, url_map_arg):
  """Issues requests necessary to invalidate a URL map cdn cache."""
  client = holder.client

  requests = _CreateRequests(holder, args, url_map_arg)
  if args.async_:
    resources, errors = batch_helper.MakeRequests(
        requests=requests,
        http=client.apitools_client.http,
        batch_url=client.batch_url)
    if not errors:
      for invalidation_operation in resources:
        log.status.write('Invalidation pending for [{0}]\n'.format(
            invalidation_operation.targetLink))
        log.status.write('Monitor its progress at [{0}]\n'.format(
            invalidation_operation.selfLink))
    else:
      utils.RaiseToolException(errors)
  else:
    # We want to run through the generator that MakeRequests returns in order
    # to actually make the requests.
    resources = client.MakeRequests(requests)

  return resources


@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.PREVIEW)
class InvalidateCdnCache(base.SilentCommand):
  """Invalidate specified objects for a URL map in Cloud CDN caches."""

  detailed_help = _DetailedHelp()
  URL_MAP_ARG = None

  @classmethod
  def Args(cls, parser):
    cls.URL_MAP_ARG = flags.GlobalUrlMapArgument()
    cls.URL_MAP_ARG.AddArgument(parser, cust_metavar='URLMAP')
    _Args(parser)
    if cls.ReleaseTrack() == base.ReleaseTrack.ALPHA:
      flags.AddHttpStatusArgToParser(parser)

  def Run(self, args):
    holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
    return _Run(args, holder, self.URL_MAP_ARG)


@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class InvalidateCdnCacheBeta(InvalidateCdnCache):
  pass


@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class InvalidateCdnCacheAlpha(InvalidateCdnCacheBeta):
  pass
