<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\ffmpeg_media\Service\MetricsServiceInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Controller for exposing Prometheus metrics.
 *
 * Provides a metrics endpoint compatible with Prometheus scraping.
 * Access is controlled via the 'view ffmpeg media metrics' permission.
 */
final class MetricsController extends ControllerBase {

  /**
   * Prometheus content type.
   */
  protected const string CONTENT_TYPE = 'text/plain; version=0.0.4; charset=utf-8';

  /**
   * Constructs a MetricsController object.
   *
   * @param \Drupal\ffmpeg_media\Service\MetricsServiceInterface $metricsService
   *   Metrics service.
   */
  public function __construct(
    #[Autowire(service: MetricsServiceInterface::class)]
    protected readonly MetricsServiceInterface $metricsService,
  ) {}

  /**
   * Returns metrics in Prometheus format.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   Response with metrics in Prometheus text format.
   */
  public function metrics(Request $request): Response {
    // Extract custom labels from query parameters.
    $labels = $this->extractLabels($request);

    // Generate metrics output.
    $content = $this->metricsService->getPrometheusMetrics($labels);

    return new Response(
      $content,
      Response::HTTP_OK,
      [
        'Content-Type' => self::CONTENT_TYPE,
        'Cache-Control' => 'no-cache, no-store, must-revalidate',
        'Pragma' => 'no-cache',
        'Expires' => '0',
      ],
    );
  }

  /**
   * Returns a JSON summary of metrics.
   *
   * Provides a human-readable JSON format for debugging and dashboards.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   JSON response with metrics summary.
   */
  public function summary(): Response {
    $data = [
      'timestamp' => time(),
      'conversion' => $this->metricsService->getConversionMetrics(),
      'queue' => $this->metricsService->getQueueMetrics(),
      'hardware' => $this->metricsService->getHardwareMetrics(),
      'system' => $this->metricsService->getSystemMetrics(),
    ];

    $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

    return new Response(
      $json !== FALSE ? $json : '{"error":"JSON encoding failed"}',
      Response::HTTP_OK,
      [
        'Content-Type' => 'application/json',
        'Cache-Control' => 'no-cache, no-store, must-revalidate',
      ],
    );
  }

  /**
   * Extracts custom labels from request query parameters.
   *
   * Labels can be passed as query parameters with the 'label_' prefix.
   * Example: ?label_environment=production&label_instance=server1
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return array<string, string>
   *   Extracted labels.
   */
  protected function extractLabels(Request $request): array {
    $labels = [];

    foreach ($request->query->all() as $key => $value) {
      if (str_starts_with($key, 'label_') && is_string($value)) {
        $labelName = substr($key, 6);
        // Validate label name (Prometheus format).
        if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $labelName)
          && $this->isValidLabelValue($value)) {
          $labels[$labelName] = $value;
        }
      }
    }

    return $labels;
  }

  /**
   * Validates a Prometheus label value.
   *
   * Ensures the value contains only safe characters to prevent malformed
   * Prometheus metric output or injection attacks.
   *
   * @param string $value
   *   The label value to validate.
   *
   * @return bool
   *   TRUE if the value is valid for Prometheus labels.
   */
  protected function isValidLabelValue(string $value): bool {
    if ($value === '') {
      return FALSE;
    }

    return (bool) preg_match('/^[a-zA-Z0-9_\-.]+$/', $value);
  }

}
