<?php

declare(strict_types=1);

namespace Drupal\native_observability_dashboard\Service;

use Drupal\native_observability_dashboard\ValueObject\TimeRange;

/**
 * Builds the AJAX activity section for the dashboard.
 */
final readonly class AjaxActivitySectionBuilder {

  /**
   * Creates a new AJAX activity section builder instance.
   *
   * @param DashboardAvailabilityResolver $availabilityResolver
   *   The dashboard availability resolver.
   * @param DashboardLibraryAvailabilityResolver $libraryAvailabilityResolver
   *   The frontend library availability resolver.
   * @param TraceMetricsReader $traceMetricsReader
   *   The trace metrics reader service.
   */
  public function __construct(
    private DashboardAvailabilityResolver $availabilityResolver,
    private DashboardLibraryAvailabilityResolver $libraryAvailabilityResolver,
    private TraceMetricsReader $traceMetricsReader,
  ) {
  }

  /**
   * Builds the AJAX activity section payload.
   *
   * @param \Drupal\native_observability_dashboard\ValueObject\TimeRange|null $range
   *   The selected dashboard time range.
   *
   * @return array
   *   The AJAX activity section payload.
   */
  public function build(?TimeRange $range = NULL): array {
    $range ??= TimeRange::createDefault();

    if (!$this->availabilityResolver->hasTraceSupport()) {
      return [
        'enabled' => FALSE,
        'title' => 'AJAX Activity',
        'reason' => 'The trace storage layer is not currently available.',
      ];
    }

    if (!$this->libraryAvailabilityResolver->hasEchartsLibrary()) {
      return [
        'enabled' => FALSE,
        'title' => 'AJAX Activity',
        'reason' => 'Apache ECharts is not installed. Install the frontend library to enable this chart section.',
      ];
    }

    $bucketSize = $this->resolveBucketSize($range);
    $ajaxTraceCount = $this->traceMetricsReader->getAjaxTraceCount($range);
    $requestsPerSecondSeries = $this->traceMetricsReader->getAjaxRequestsPerSecondSeries($range, $bucketSize);
    $averageDurationSeries = $this->traceMetricsReader->getAjaxAverageDurationSeries($range, $bucketSize);
    $errorRateSeries = $this->traceMetricsReader->getAjaxErrorRateSeries($range, $bucketSize);

    return [
      'enabled' => TRUE,
      'id' => 'ajax_activity',
      'title' => 'AJAX Activity',
      'subtitle' => 'Asynchronous traffic, latency and failures across the selected time range.',
      'time_range' => $range->getLabel(),
      'chart_id' => 'native-observability-ajax-timeline',
      'bucket_size' => $bucketSize,
      'summary_title' => 'AJAX Summary',
      'summary_help' => $ajaxTraceCount > 0
        ? 'Compare asynchronous traffic with the primary request timeline to identify frontend-driven load patterns.'
        : 'No AJAX requests were recorded in the selected range. This panel remains visible so the absence of AJAX traffic is explicit and not confused with a rendering problem.',
      'switch_label' => 'Show request details',
      'summary_cards' => [
        [
          'label' => 'AJAX Requests',
          'value' => number_format($ajaxTraceCount),
        ],
        [
          'label' => 'Peak AJAX/sec',
          'value' => number_format($this->extractPeakValue($requestsPerSecondSeries), 2),
        ],
        [
          'label' => 'Average Latency',
          'value' => number_format($this->traceMetricsReader->getAverageDuration($range, TRUE), 2) . ' ms',
        ],
        [
          'label' => 'Error Rate',
          'value' => number_format($this->traceMetricsReader->getAjaxErrorRate($range), 2) . '%',
        ],
      ],
      'empty_state' => $ajaxTraceCount > 0 ? NULL : [
        'title' => 'No AJAX activity captured',
        'description' => 'This platform may rely mainly on server-rendered navigation for the selected time range. Keeping the summary visible clarifies that asynchronous traffic is simply absent.',
      ],
      'series' => [
        'requests_per_second' => $requestsPerSecondSeries,
        'average_duration' => $averageDurationSeries,
        'error_rate' => $errorRateSeries,
      ],
    ];
  }

  /**
   * Resolves the chart bucket size for the selected range.
   *
   * @param \Drupal\native_observability_dashboard\ValueObject\TimeRange $range
   *   The selected dashboard time range.
   *
   * @return int
   *   The bucket size in seconds.
   */
  private function resolveBucketSize(TimeRange $range): int {
    return match ($range->getKey()) {
      '15m' => 60,
      '30m' => 120,
      '1h' => 300,
      '6h' => 900,
      '24h' => 1800,
      '7d' => 7200,
      default => 300,
    };
  }

  /**
   * Extracts the highest numeric value from a timeline series.
   *
   * @param array $series
   *   The chart series.
   *
   * @return float
   *   The maximum series value.
   */
  private function extractPeakValue(array $series): float {
    $peak = 0.0;

    foreach ($series as $point) {
      $peak = max($peak, (float) ($point['value'] ?? 0.0));
    }

    return $peak;
  }

}
