#!/usr/bin/env vpython3
#
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A script to run a command on all available emulators."""

import argparse
import logging
import os
import re
import subprocess
import sys
import time

_SRC_ROOT = os.path.abspath(
  os.path.join(os.path.dirname(__file__), '..', '..', '..')
)
sys.path.append(os.path.join(_SRC_ROOT, 'third_party', 'catapult', 'devil'))
from devil.utils import logging_common

sys.path.append(os.path.join(_SRC_ROOT, 'tools', 'android', 'avd'))
from avd import get_avd_configs


def get_emulators(all_emulators):
  """Gets the list of emulators, optionally filtering by API level."""
  emulators = []
  for config in get_avd_configs():
    if not all_emulators and not config.IsAvailable():
      continue

    path = config.avd_proto_path
    basename = os.path.basename(path)
    api_level = 0
    # Try to extract API level like android_31_...
    match = re.search(r'android_(\d+)', basename)
    if match:
      api_level = int(match.group(1))
    else:
      # Try to extract API level like generic_android27.textpb
      match = re.search(r'generic_android(\d+)', basename)
      if match:
        api_level = int(match.group(1))

    # If we found an API level and it's 28 or less, skip it.
    if api_level and api_level <= 28:
      logging.info(
        'Skipping emulator %s due to old API level (%d).', path, api_level
      )
      continue
    emulators.append(path)
  return emulators


def wait_for_emulator(serial):
  """Waits for the emulator to be online."""
  logging.info('Waiting for emulator %s to be online...', serial)
  for _ in range(60):
    try:
      output = subprocess.check_output(['adb', 'devices'], encoding='utf-8')
      if re.search(r'^%s\s+device$' % re.escape(serial), output, re.MULTILINE):
        logging.info('Emulator %s is online.', serial)
        return True
    except subprocess.CalledProcessError:
      pass
    time.sleep(1)
  logging.error('Emulator %s did not come online.', serial)
  return False


def main():
  """Starts each emulator, runs a command, and stops it."""
  parser = argparse.ArgumentParser(
    description='Run a command on all available emulators.'
  )
  parser.add_argument(
    '--cmd', required=True, help='The command to run on the emulator.'
  )
  parser.add_argument(
    '--all',
    action='store_true',
    help='Run on all emulators, not just installed ones.',
  )
  parser.add_argument('--output-file', help='File to write the output to.')
  logging_common.AddLoggingArguments(parser)
  args = parser.parse_args()

  logging_common.InitializeLogging(args)

  if args.output_file:
    output_dir = os.path.dirname(args.output_file)
    if not os.path.exists(output_dir):
      os.makedirs(output_dir)
    # Clear the file if it exists
    open(args.output_file, 'w').close()

  emulators = get_emulators(args.all)
  if not emulators:
    logging.warning('No available emulators found.')
    return

  for emulator_config in emulators:
    logging.info('Starting emulator: %s', emulator_config)
    try:
      start_process = subprocess.Popen(
        ['tools/android/avd/avd.py', 'start', '--avd-config', emulator_config],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding='utf-8',
      )
      stdout, stderr = start_process.communicate()
      if start_process.returncode != 0:
        logging.error(
          'Failed to start emulator %s: %s', emulator_config, stderr
        )
        continue

      # The serial number is expected to be in the output of the start command.
      match = re.search(r'emulator-\d+', stdout)
      if not match:
        logging.error(
          'Could not determine serial for emulator %s', emulator_config
        )
        continue
      serial = match.group(0)

      if wait_for_emulator(serial):
        logging.info('Running command on %s: %s', serial, args.cmd)
        try:
          cmd_output = subprocess.check_output(
            args.cmd, shell=True, encoding='utf-8'
          )
          if args.output_file:
            with open(args.output_file, 'a') as f:
              f.write(f'{emulator_config}: {cmd_output.strip()}\n')
          else:
            print(f'Output for {emulator_config}:\n{cmd_output}')
        except subprocess.CalledProcessError as e:
          logging.error('Command failed on %s: %s', emulator_config, e)

      logging.info('Stopping emulator: %s', emulator_config)
      subprocess.run(
        ['tools/android/avd/avd.py', 'stop', '--avd-config', emulator_config],
        check=False,
      )
    except Exception as e:
      logging.error(
        'An error occurred with emulator %s: %s', emulator_config, e
      )
      # Ensure the emulator is stopped even if an error occurs.
      subprocess.run(
        ['tools/android/avd/avd.py', 'stop', '--avd-config', emulator_config],
        check=False,
      )


if __name__ == '__main__':
  main()
