<?php

declare(strict_types=1);

namespace Drupal\native_observability_dashboard\Service;

use Drupal\native_observability_dashboard\ValueObject\TimeRange;

/**
 * Builds the main request timeline section for the dashboard.
 */
final readonly class RequestTimelineSectionBuilder {

  /**
   * Creates a new request timeline 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 request timeline section payload.
   *
   * @param \Drupal\native_observability_dashboard\ValueObject\TimeRange|null $range
   *   The selected dashboard time range.
   *
   * @return array
   *   The request timeline section payload.
   */
  public function build(?TimeRange $range = NULL): array {
    $range ??= TimeRange::createDefault();

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

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

    $bucketSize = $this->resolveBucketSize($range);
    $requestCount = $this->traceMetricsReader->getTraceCount($range);
    $requestsPerSecondSeries = $this->traceMetricsReader->getRequestsPerSecondSeries($range, $bucketSize);
    $averageDurationSeries = $this->traceMetricsReader->getAverageDurationSeries($range, $bucketSize);
    $p95DurationSeries = $this->traceMetricsReader->getP95DurationSeries($range, $bucketSize);
    $errorRateSeries = $this->traceMetricsReader->getErrorRateSeries($range, $bucketSize);

    return [
      'enabled' => TRUE,
      'id' => 'request_activity',
      'title' => 'Request Activity',
      'subtitle' => 'Traffic, latency and failures across the selected time range.',
      'time_range' => $range->getLabel(),
      'chart_id' => 'native-observability-request-timeline',
      'bucket_size' => $bucketSize,
      'library_attached' => TRUE,
      'summary_title' => 'Request Summary',
      'summary_help' => 'Use this view to follow throughput, latency and reliability trends across the selected time range.',
      'switch_label' => 'Show AJAX details',
      'summary_cards' => [
        [
          'label' => 'Captured Requests',
          'value' => number_format($requestCount),
        ],
        [
          'label' => 'Peak Requests/sec',
          'value' => number_format($this->extractPeakValue($requestsPerSecondSeries), 2),
        ],
        [
          'label' => 'Average Latency',
          'value' => number_format($this->traceMetricsReader->getAverageDuration($range), 2) . ' ms',
        ],
        [
          'label' => 'P95 Latency',
          'value' => number_format($this->traceMetricsReader->getP95Duration($range), 2) . ' ms',
        ],
      ],
      'series' => [
        'requests_per_second' => $requestsPerSecondSeries,
        'average_duration' => $averageDurationSeries,
        'p95_duration' => $p95DurationSeries,
        '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;
  }

}
