# -*- coding: utf-8 -*- #
# Copyright 2022 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 spanner samples workload."""


import os
import textwrap

from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import samples
from googlecloudsdk.core import execution_utils


def _get_popen_jar(appname):
  if appname not in samples.APPS:
    raise ValueError("Unknown sample app '{}'".format(appname))
  return os.path.join(
      samples.get_local_bin_path(appname), samples.APPS[appname].workload_bin)


def run_workload(appname, port=None, capture_logs=False):
  """Run the workload generator executable for the given sample app.

  Args:
    appname: str, Name of the sample app.
    port: int, Port to run the service on.
    capture_logs: bool, Whether to save logs to disk or print to stdout.

  Returns:
    subprocess.Popen or execution_utils.SubprocessTimeoutWrapper, The running
      subprocess.
  """
  proc_args = ['java', '-jar', _get_popen_jar(appname)]
  if port is not None:
    proc_args.append('--port={}'.format(port))
  capture_logs_fn = (
      os.path.join(samples.SAMPLES_LOG_PATH, '{}-workload.log'.format(appname))
      if capture_logs else None)
  return samples.run_proc(proc_args, capture_logs_fn)


class Workload(base.Command):
  """Generate gRPC traffic for a given sample app's backend service.

  Before sending traffic to the backend service, create the database and
  start the service with:

      $ {parent_command} init APPNAME --instance-id=INSTANCE_ID
      $ {parent_command} backend APPNAME --instance-id=INSTANCE_ID

  To run all three steps together, use:

      $ {parent_command} run APPNAME --instance-id=INSTANCE_ID
  """

  detailed_help = {
      'EXAMPLES':
          textwrap.dedent("""\
          To generate traffic for the 'finance' sample app, run:

          $ {command} finance
        """),
  }

  @staticmethod
  def Args(parser):
    """Args is called by calliope to gather arguments for this command.

    Args:
      parser: An argparse parser that you can use to add arguments that go on
        the command line after this command. Positional arguments are allowed.
    """
    parser.add_argument('appname', help='The sample app name, e.g. "finance".')

    parser.add_argument(
        '--duration',
        default='1h',
        type=arg_parsers.Duration(),
        help=('Duration of time allowed to run before stopping the workload.'))
    parser.add_argument(
        '--port', type=int, help=('Port of the running backend service.'))
    parser.add_argument(
        '--target-qps', type=int, help=('Target requests per second.'))

  def Run(self, args):
    """This is what gets called when the user runs this command.

    Args:
      args: an argparse namespace. All the arguments that were provided to this
        command invocation.

    Returns:
      Some value that we want to have printed later.
    """
    proc = run_workload(args.appname, args.port)
    try:
      with execution_utils.RaisesKeyboardInterrupt():
        return proc.wait(args.duration)
    except KeyboardInterrupt:
      proc.terminate()
      return 'Workload generator killed'
    except execution_utils.TIMEOUT_EXPIRED_ERR:
      proc.terminate()
      return 'Workload generator killed after {duration}s'.format(
          duration=args.duration)
    return
