# -*- coding: utf-8 -*- #
# Copyright 2014 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 deprecating images."""


import datetime

from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.compute.images import flags


def _ResolveTime(absolute, relative_sec, current_time):
  """Get the RFC 3339 time string for a provided absolute or relative time."""
  if absolute:
    # TODO(b/36057353): It's unfortunate that datetime.datetime cannot
    # parse from RFC 3339, but it can output to it. It would be
    # super cool if we could verify the validity of the user's
    # input here and fail fast if an invalid date/time is given.
    # For now, I assume that the user's input is valid.
    return absolute
  elif relative_sec:
    return (
        current_time + datetime.timedelta(seconds=relative_sec)
    ).replace(microsecond=0).isoformat()
  else:
    return None


class DeprecateImages(base.SilentCommand):
  """Manage deprecation status of Compute Engine images.

  *{command}* is used to deprecate images.
  """

  @staticmethod
  def Args(parser):
    DeprecateImages.DISK_IMAGE_ARG = flags.MakeDiskImageArg()
    DeprecateImages.DISK_IMAGE_ARG.AddArgument(parser)
    flags.REPLACEMENT_DISK_IMAGE_ARG.AddArgument(parser)

    deprecation_statuses = {
        'ACTIVE': 'The image is currently supported.',
        'DELETED': (
            'New uses result in an error. Setting this state will not '
            'automatically delete the image. You must still make a request to '
            'delete the image to remove it from the image list.'),
        'DEPRECATED': (
            'Operations which create a new *DEPRECATED* resource return '
            'successfully, but with a warning indicating that the image is '
            'deprecated and recommending its replacement.'),
        'OBSOLETE': 'New uses result in an error.',
    }

    parser.add_argument(
        '--state',
        choices=deprecation_statuses,
        default='ACTIVE',
        type=lambda x: x.upper(),
        required=True,
        help='The deprecation state to set on the image.')

    deprecate_group = parser.add_mutually_exclusive_group()

    deprecate_group.add_argument(
        '--deprecate-on',
        help="""\
        Specifies a date when the image should be marked as DEPRECATED.

        Note: This is only informational and the image will not be deprecated unless you manually deprecate it.

        This flag is mutually exclusive with *--deprecate-in*.

        The date and time specified must be valid RFC 3339 full-date or date-time.
        For times in UTC, this looks like ``YYYY-MM-DDTHH:MM:SSZ''.
        For example: 2020-01-02T00:00:00Z for midnight on January 2, 2020 in UTC.
        """)

    deprecate_group.add_argument(
        '--deprecate-in',
        type=arg_parsers.Duration(),
        help="""\
        Specifies a time duration in which the image should be marked as ``DEPRECATED''.

        Note: This is only informational and the image will not be deprecated unless you manually deprecate it.

        This flag is mutually exclusive with *--deprecate-on*.

        For example, specifying ``30d'' sets the planned ``DEPRECATED'' date to 30 days from the current system time,
        but does not deprecate the image. You must manually deprecate the image in 30 days.
        See $ gcloud topic datetimes for information on duration formats.

       """)

    delete_group = parser.add_mutually_exclusive_group()

    delete_group.add_argument(
        '--delete-on',
        help="""\
        Specifies a date when the image should be marked as ``DELETED''.

        Note: This is only informational and the image will not be deleted unless you manually delete it.

        This flag is mutually exclusive with *--delete-in*.

        The date and time specified must be valid RFC 3339 full-date or date-time.
        For times in UTC, this looks like ``YYYY-MM-DDTHH:MM:SSZ''.
        For example: 2020-01-02T00:00:00Z for midnight on January 2, 2020 in UTC.

        """)

    delete_group.add_argument(
        '--delete-in',
        type=arg_parsers.Duration(),
        help="""\
        Specifies a time duration in which the image should be marked as ``DELETED''.

        Note: This is only informational and the image will not be deleted unless you manually delete it.

        For example, specifying ``30d'' sets the planned ``DELETED'' time to 30 days from the current system time,
        but does not delete the image. You must manually delete the image in 30 days.
        See $ gcloud topic datetimes for information on duration formats.

        This flag is mutually exclusive with *--delete-on*.
       """)

    obsolete_group = parser.add_mutually_exclusive_group()

    obsolete_group.add_argument(
        '--obsolete-on',
        help="""\
        Specifies a date when the image should be marked as ``OBSOLETE''.

        Note: This is only informational and the image will not be obsoleted unless you manually obsolete it.

        This flag is mutually exclusive with *--obsolete-in*.

        The date and time specified must be valid RFC 3339 full-date or date-time.
        For times in UTC, this looks like ``YYYY-MM-DDTHH:MM:SSZ''.
        For example: 2020-01-02T00:00:00Z for midnight on January 2, 2020 in UTC.
       """)

    obsolete_group.add_argument(
        '--obsolete-in',
        type=arg_parsers.Duration(),
        help="""\
        Specifies a time duration in which the image should be marked as ``OBSOLETE''.

        Note: This is only informational and the image will not be obsoleted unless you manually obsolete it.

        This flag is mutually exclusive with *--obsolete-on*.

        For example, specifying ``30d'' sets the planned ``OBSOLETE'' time to 30 days from the current system time,
        but does not obsolete the image. You must manually obsolete the image in 30 days.
        See $ gcloud topic datetimes for information on duration formats.
        """)

  def Run(self, args):
    """Invokes requests necessary for deprecating images."""
    # TODO(b/13695932): Note that currently there is a bug in the backend
    # whereby any request other than a completely empty request or a request
    # with state set to something other than ACTIVE will fail.
    # GCloud will be able to be made more permissive w.r.t. the checks
    # below when the API changes.

    holder = base_classes.ComputeApiHolder(self.ReleaseTrack())
    client = holder.client

    # Determine the date and time to deprecate for each flag set.
    current_time = datetime.datetime.now()
    delete_time = _ResolveTime(args.delete_on, args.delete_in, current_time)
    obsolete_time = _ResolveTime(
        args.obsolete_on, args.obsolete_in, current_time)
    deprecate_time = _ResolveTime(
        args.deprecate_on, args.deprecate_in, current_time)

    state = client.messages.DeprecationStatus.StateValueValuesEnum(args.state)

    replacement_ref = flags.REPLACEMENT_DISK_IMAGE_ARG.ResolveAsResource(
        args, holder.resources)
    if replacement_ref:
      replacement_uri = replacement_ref.SelfLink()
    else:
      replacement_uri = None

    image_ref = DeprecateImages.DISK_IMAGE_ARG.ResolveAsResource(
        args, holder.resources)

    request = client.messages.ComputeImagesDeprecateRequest(
        deprecationStatus=client.messages.DeprecationStatus(
            state=state,
            deleted=delete_time,
            obsolete=obsolete_time,
            deprecated=deprecate_time,
            replacement=replacement_uri),
        image=image_ref.Name(),
        project=image_ref.project)

    return client.MakeRequests([(client.apitools_client.images,
                                 'Deprecate', request)])


DeprecateImages.detailed_help = {
    'EXAMPLES': """
To deprecate an image called 'IMAGE' immediately, mark it as
obsolete in one day, and mark it as deleted in two days, use:

  $ {command} IMAGE --state=DEPRECATED --obsolete-in=1d --delete-in=2d

To un-deprecate an image called 'IMAGE' and clear times for deprecated,
obsoleted, and deleted, use:

  $ {command} IMAGE --state=ACTIVE
""",
}
