<?php

declare(strict_types=1);

namespace Drupal\native_observability_spans\Drush\Commands;

use Drush\Attributes as CLI;
use Drush\Commands\AutowireTrait;
use Drush\Commands\DrushCommands;
use GuzzleHttp\ClientInterface;

/**
 * Executes outbound HTTP requests through Drupal's global HTTP client.
 *
 * This command performs a series of HTTP calls to known endpoints in order to
 * validate that the Native Observability HTTP span instrumentation is working
 * correctly.
 *
 * When the spans module decorates Drupal's http_client service, each outbound
 * request should generate one or more HTTP spans. This command is intended
 * purely as a diagnostic and validation tool for developers.
 *
 * The command reports:
 * - HTTP status codes
 * - Connection or DNS errors
 * - A truncated response body
 *
 * The output is formatted as a table to quickly verify that span generation
 * works correctly during development or troubleshooting.
 */
final class NativeObservabilitySpansCommands extends DrushCommands {
  use AutowireTrait;

  /**
   * Constructs the Drush commands service.
   *
   * @param \GuzzleHttp\ClientInterface $httpClient
   *   The global Drupal HTTP client.
   */
  public function __construct(
    private readonly ClientInterface $httpClient,
  ) {
    parent::__construct();
  }

  /**
   * Runs outbound HTTP requests to validate HTTP span tracing.
   *
   * This command uses Drupal's global http_client service. When the spans
   * module decorates the client correctly, each outbound request should
   * generate one or more HTTP spans.
   *
   * @param array $options
   *   Command options.
   *
   * @return int
   *   The command exit code.
   */
  #[CLI\Command(name: 'native-observability-span:http-test', aliases: ['nos:http-test'])]
  #[CLI\Usage(name: 'drush native-observability-span:http-test', description: 'Run outbound HTTP requests through Drupal to validate HTTP span generation.')]
  #[CLI\Option(name: 'timeout', description: 'Request timeout in seconds.')]
  #[CLI\Option(name: 'connect-timeout', description: 'Connection timeout in seconds.')]
  public function httpTest(
    array $options = [
      'timeout' => 8,
      'connect-timeout' => 4,
    ],
  ): int {
    $timeout = (int) ($options['timeout'] ?? 8);
    $connectTimeout = (int) ($options['connect-timeout'] ?? 4);

    $targets = [
      [
        'label' => 'Example.com (200)',
        'method' => 'GET',
        'url' => 'https://example.com',
      ],
      [
        'label' => 'Google generate_204 (204)',
        'method' => 'GET',
        'url' => 'https://www.google.com/generate_204',
      ],
      [
        'label' => 'httpbin 200',
        'method' => 'GET',
        'url' => 'https://httpbin.org/status/200',
      ],
      [
        'label' => 'httpbin 503',
        'method' => 'GET',
        'url' => 'https://httpbin.org/status/503',
      ],
      [
        'label' => 'DNS failure (expected error)',
        'method' => 'GET',
        'url' => 'https://this-domain-should-not-exist.native-observability.invalid/',
      ],
    ];

    $rows = [];

    foreach ($targets as $target) {
      $status = '';
      $error = '';
      $body = '';

      try {
        $response = $this->httpClient->request($target['method'], $target['url'], [
          'timeout' => $timeout,
          'connect_timeout' => $connectTimeout,
          'http_errors' => FALSE,
          'curl' => [
            \CURLOPT_IPRESOLVE => \CURL_IPRESOLVE_V4,
          ],
        ]);

        $status = (string) $response->getStatusCode();
        $body = $this->truncate((string) $response->getBody(), 250);
      }
      catch (\Throwable $exception) {
        $error = $exception::class . ': ' . $this->truncate($exception->getMessage(), 200);
      }

      $rows[] = [
        $target['label'],
        $target['method'],
        $target['url'],
        $status !== '' ? $status : '-',
        $error !== '' ? $error : '-',
        $body !== '' ? $body : '-',
      ];
    }

    $this->io()->title('Native Observability HTTP span test');
    $this->io()->table(
      ['Test', 'Method', 'URL', 'Status', 'Error', 'Body'],
      $rows,
    );
    $this->io()->writeln('');
    $this->io()->success('HTTP span test completed.');

    return self::EXIT_SUCCESS;
  }

  /**
   * Truncates a string to a maximum length.
   *
   * @param string $value
   *   The string to truncate.
   * @param int $maxLength
   *   The maximum allowed length.
   *
   * @return string
   *   The truncated string.
   */
  private function truncate(string $value, int $maxLength): string {
    if (mb_strlen($value) <= $maxLength) {
      return $value;
    }

    return mb_substr($value, 0, $maxLength) . '...';
  }

}
