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

"""Create command for gcloud debug logpoints command group."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

from googlecloudsdk.api_lib.debug import debug
from googlecloudsdk.calliope import base
from googlecloudsdk.core import log
from googlecloudsdk.core import properties

DETAILED_HELP = {
    'brief':
        """\
        Add debug logpoints to a Cloud Debugger debug target (debuggee).
    """,
    'DESCRIPTION':
        """\
        *{command}* is used  to add a debug logpoint to a debug target
        (debuggee).  Logpoints add logging to your running service without
        changing your code or restarting your application. When you create a
        logpoint, the message you specify will be added to your logs whenever
        any instance of your service executes the specified line of code.

        The default lifetime of a logpoint is 24 hours from creation, and the
        output will go to the standard log for the programming language of the
        target (``java.logging'' for Java, ``logging'' for Python, etc.)
    """,
    'EXAMPLES':
        """\
        To add a logpoint with no conditions that will print the value of the
        variable 'name' at line 41 of file main.py of a debug target (debuggee),
        run:

          $ {command} main.py:41 "Variable name={name}" --target=<debuggee_id>

        To add a logpoint that will print the value of the variable 'name' at
        line 41 of file main.py on a debug target (debuggee) that will only
        trigger if the length of 'name' is greater than 3, run:

          $ {command} main.py:41 "Variable name={name}" --target=<debuggee_id> --condition="len(name) > 3"

        To add a logpoint with a log level of error at line 35 of file main.py
        on a debug target (debuggee), run:

          $ {command} main.py:35 "Unexpected path" --target=<debuggee_id> --log-level=error
    """
}


class Create(base.CreateCommand):
  """Create debug logpoints."""

  detailed_help = DETAILED_HELP

  @staticmethod
  def Args(parser):
    parser.add_argument(
        'location',
        help="""\
            The logpoint location. Locations are of
            the form FILE:LINE, where FILE can be simply the file name, or the
            file name preceded by enough path components to differentiate it
            from other files with the same name. It is an error to provide a
            file name that is not unique in the debug target.
        """)
    parser.add_argument(
        'log_format_string',
        help="""\
            A format string which will be logged every time the logpoint
            location is executed. If the string contains curly braces ('{' and
            '}'), any text within the curly braces will be interpreted as a
            run-time expression in the debug target's language, which will be
            evaluated when the logpoint is hit.

            The value of the expression will then replace the {} expression in
            the resulting log output. For example, if you specify the format
            string "a={a}, b={b}", and the logpoint is hit when local variable
            a is 1 and b is 2, the resulting log output would be "a=1, b=2".
        """)
    parser.add_argument(
        '--condition',
        help="""\
            A condition to restrict when the log output is generated. When the
            logpoint is hit, the condition will be evaluated, and the log output
            will be generated only if the condition is true.
        """)
    parser.add_argument(
        '--log-level', choices=['info', 'warning', 'error'],
        default='info',
        help='The logging level to use when producing the log message.')
    parser.add_argument(
        '--wait', default=10,
        help="""\
            The number of seconds to wait to ensure that no error is returned
            from a debugger agent when creating the logpoint. When a logpoint
            is created, there will be a delay before the agents see and apply
            the logpoint. Until at least one agent has attempted to
            enable the logpoint, it cannot be determined if the logpoint is
            valid.
        """)
    parser.display_info.AddFormat("""
          list(
            format("id: {0}", id),
            format("location: {0}", location),
            format("logLevel: {0}", logLevel),
            format("logMessageFormat: {0}", logMessageFormat),
            format("condition: {0}", condition),
            format("logViewUrl: {0}", logViewUrl),
            format("status: {0}", full_status())
          )
    """)

  def Run(self, args):
    """Run the create command."""
    project_id = properties.VALUES.core.project.Get(required=True)
    user_email = properties.VALUES.core.account.Get(required=True)
    debugger = debug.Debugger(project_id)
    debuggee = debugger.FindDebuggee(args.target)
    logpoint = debuggee.CreateLogpoint(
        location=args.location, log_level=args.log_level,
        log_format_string=args.log_format_string, condition=args.condition,
        user_email=user_email)
    # Wait a short time to see if the logpoint generates an error. Ideally,
    # we'd want to wait until we get a response that the logpoint was set
    # by at least one instance, but the API does not currently support that.
    final_logpoint = debuggee.WaitForBreakpointSet(logpoint.id, args.wait,
                                                   args.location)
    if args.location != final_logpoint.location:
      log.status.write(
          'The debugger adjusted the logpoint location to {0}'.format(
              final_logpoint.location))
    return final_logpoint or logpoint
