<?php

declare(strict_types=1);

namespace Drupal\native_observability_metrics\Service;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Database\Connection;

/**
 * Stores aggregated route metrics in the database.
 */
final class DatabaseMetricsStorage {

  /**
   * Metrics table name.
   */
  private const TABLE = 'native_observability_route_metric';

  /**
   * Constructs a new DatabaseMetricsStorage instance.
   *
   * @param \Drupal\Core\Database\Connection $database
   *   The database connection.
   * @param \Drupal\Component\Datetime\TimeInterface $time
   *   The time service.
   */
  public function __construct(
    private readonly Connection $database,
    private readonly TimeInterface $time,
  ) {
  }

  /**
   * Replaces metrics for a specific aggregation window.
   *
   * @param int $window_start
   *   The aggregation window start timestamp.
   * @param int $window_end
   *   The aggregation window end timestamp.
   * @param array $metrics
   *   Aggregated metric rows.
   */
  public function replaceWindowMetrics(int $window_start, int $window_end, array $metrics): void {
    $this->database->delete(self::TABLE)
      ->condition('window_start', $window_start)
      ->condition('window_end', $window_end)
      ->execute();

    $created_at = $this->time->getCurrentTime();

    foreach ($metrics as $metric) {
      $this->database->insert(self::TABLE)
        ->fields([
          'route_key' => (string) ($metric['route_key'] ?? 'unknown'),
          'route_name' => !empty($metric['route_name']) ? (string) $metric['route_name'] : NULL,
          'window_start' => $window_start,
          'window_end' => $window_end,
          'request_count' => (int) ($metric['request_count'] ?? 0),
          'throughput_per_minute' => (float) ($metric['throughput_per_minute'] ?? 0),
          'avg_duration_ms' => (int) ($metric['avg_duration_ms'] ?? 0),
          'p95_duration_ms' => (int) ($metric['p95_duration_ms'] ?? 0),
          'p99_duration_ms' => (int) ($metric['p99_duration_ms'] ?? 0),
          'created_at' => $created_at,
        ])
        ->execute();
    }
  }

  /**
   * Returns the latest aggregated metrics snapshot from storage.
   *
   * @param int $limit
   *   Maximum number of rows to return.
   *
   * @return array
   *   The latest aggregated metric rows.
   */
  public function getLatestSnapshot(int $limit = 100): array {
    $query = $this->database->select(self::TABLE, 'm');
    $query->addExpression('MAX(m.window_end)', 'latest_window_end');

    $latest_window_end = $query->execute()->fetchField();

    if (!$latest_window_end) {
      return [];
    }

    $query = $this->database->select(self::TABLE, 'm')
      ->fields('m', [
        'id',
        'route_key',
        'route_name',
        'window_start',
        'window_end',
        'request_count',
        'throughput_per_minute',
        'avg_duration_ms',
        'p95_duration_ms',
        'p99_duration_ms',
        'created_at',
      ])
      ->condition('window_end', (int) $latest_window_end)
      ->orderBy('request_count', 'DESC')
      ->range(0, $limit);

    $result = $query->execute()->fetchAllAssoc('id');

    if (!$result) {
      return [];
    }

    return array_map(
      static fn(object $row): array => (array) $row,
      $result
    );
  }

}
