<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\State\StateInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Resolves runtime authorization policy for public health probes.
 *
 * @internal
 */
final class HealthProbeAccessManager implements HealthProbeAccessManagerInterface {

  /**
   * Header name for optional health probe token authentication.
   */
  private const string HEALTH_PROBE_TOKEN_HEADER = 'X-FFmpeg-Health-Token';

  /**
   * State key for the non-exportable health probe token.
   */
  private const string HEALTH_PROBE_TOKEN_STATE_KEY = 'ffmpeg_media.health_probe_token';

  /**
   * Maximum denial log entries per PHP process to prevent log flooding.
   */
  private const int DENIAL_LOG_BURST_LIMIT = 5;

  /**
   * Tracks denial log count within the current process.
   */
  private static int $denialLogCount = 0;

  /**
   * Constructs a HealthProbeAccessManager object.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The config factory.
   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
   *   The request stack.
   * @param \Drupal\Core\State\StateInterface $state
   *   The state storage for non-exportable secrets.
   * @param \Psr\Log\LoggerInterface $logger
   *   The FFmpeg Media logger.
   */
  public function __construct(
    protected readonly ConfigFactoryInterface $configFactory,
    protected readonly RequestStack $requestStack,
    protected readonly StateInterface $state,
    protected readonly LoggerInterface $logger,
  ) {}

  /**
   * {@inheritdoc}
   */
  public function isAccessAuthorized(?Request $request = NULL): bool {
    ['required' => $tokenRequired, 'token' => $configuredToken] = $this->getProbeAccessSettings();

    if (!$tokenRequired) {
      return TRUE;
    }

    if ($configuredToken === '') {
      return FALSE;
    }

    $request ??= $this->requestStack->getCurrentRequest();
    if ($request === NULL) {
      return FALSE;
    }

    // Accept token via header only to avoid leaking credentials in URLs/logs.
    $providedToken = trim((string) ($request->headers->get(self::HEALTH_PROBE_TOKEN_HEADER) ?? ''));

    return $providedToken !== '' && hash_equals($configuredToken, $providedToken);
  }

  /**
   * {@inheritdoc}
   */
  public function buildAccessDeniedResponse(?Request $request = NULL): JsonResponse {
    $request ??= $this->requestStack->getCurrentRequest();

    if (self::$denialLogCount < self::DENIAL_LOG_BURST_LIMIT) {
      $this->logger->notice('Health probe access denied: invalid or missing token from IP @ip', [
        '@ip' => $request?->getClientIp() ?? 'unknown',
      ]);
      self::$denialLogCount++;
    }

    return new JsonResponse([
      'status' => 'forbidden',
      'timestamp' => date('c'),
    ], 403, [
      'Content-Type' => 'application/json',
      'Cache-Control' => 'no-cache, no-store, must-revalidate',
    ]);
  }

  /**
   * Resolves effective probe security settings with env overrides.
   *
   * Runtime precedence is:
   * 1. APP_ENV=prod|production enables token requirement by default.
   * 2. FFMPEG_HEALTH_PROBE_REQUIRED explicitly overrides requirement.
   * 3. FFMPEG_HEALTH_PROBE_TOKEN explicitly overrides configured token.
   * 4. State API provides the non-exportable fallback token.
   *
   * @return array{required: bool, token: string}
   *   Effective requirement flag and shared token.
   */
  private function getProbeAccessSettings(): array {
    $config = $this->configFactory->get('ffmpeg_media.settings');
    $tokenRequired = (bool) ($config->get('health_probe_token_required') ?? FALSE);
    $configuredToken = trim((string) ($this->state->get(self::HEALTH_PROBE_TOKEN_STATE_KEY) ?? ''));

    $appEnv = strtolower(trim((string) (getenv('APP_ENV') ?: '')));
    if (in_array($appEnv, ['prod', 'production'], TRUE)) {
      $tokenRequired = TRUE;
    }

    $envRequired = getenv('FFMPEG_HEALTH_PROBE_REQUIRED');
    if ($envRequired !== FALSE) {
      $parsed = filter_var($envRequired, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
      if ($parsed !== NULL) {
        $tokenRequired = $parsed;
      }
    }

    $envToken = getenv('FFMPEG_HEALTH_PROBE_TOKEN');
    if ($envToken !== FALSE) {
      $envToken = trim($envToken);
      if ($envToken !== '') {
        $configuredToken = $envToken;
      }
    }

    return [
      'required' => $tokenRequired,
      'token' => $configuredToken,
    ];
  }

}
