<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Controller;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\ffmpeg_media\Service\QueueManagerInterface;
use Drupal\ffmpeg_media\ValueObject\HealthCheckDependencies;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
 * Controller for health check API endpoints.
 *
 * Provides liveness, readiness, and full status endpoints for monitoring.
 */
final class HealthCheckController extends ControllerBase {

  /**
   * Number of bytes in one MiB.
   */
  private const int BYTES_PER_MIB = 1_048_576;

  /**
   * Queue count threshold for degraded status.
   */
  private const int QUEUE_DEGRADED_THRESHOLD = 50;

  /**
   * Queue count threshold for critical status.
   */
  private const int QUEUE_CRITICAL_THRESHOLD = 100;

  /**
   * Active conversions threshold for busy status.
   */
  private const int ACTIVE_BUSY_THRESHOLD = 5;

  /**
   * Constructs a HealthCheckController object.
   *
   * @param \Drupal\ffmpeg_media\ValueObject\HealthCheckDependencies $deps
   *   Bundled health check dependencies.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config factory.
   * @param \Psr\Log\LoggerInterface $logger
   *   Logger.
   */
  public function __construct(
    #[Autowire(service: HealthCheckDependencies::class)]
    protected readonly HealthCheckDependencies $deps,
    #[Autowire(service: 'config.factory')]
    ConfigFactoryInterface $config_factory,
    #[Autowire(service: 'logger.channel.ffmpeg_media')]
    protected readonly LoggerInterface $logger,
  ) {
    $this->configFactory = $config_factory;
  }

  /**
   * Returns full health check status as JSON.
   *
   * This endpoint provides comprehensive system status information including
   * FFmpeg availability, queue status, active conversions, and 24h statistics.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with detailed health status.
   */
  public function status(): JsonResponse {
    $config = $this->configFactory->get('ffmpeg_media.settings');
    $now = $this->deps->time->getRequestTime();

    // Check FFmpeg availability.
    $ffmpegAvailable = $this->deps->ffmpegExecutor->isAvailable();
    $ffmpegVersion = $ffmpegAvailable ? $this->deps->ffmpegExecutor->getVersion() : NULL;

    // Get queue status from both sources:
    // - Dedup table (authoritative for genuinely queued items)
    // - Drupal queue backend (informational, may include orphaned items)
    $normalQueue = $this->deps->queueFactory->get(QueueManagerInterface::QUEUE_NAME);
    $largeQueue = $this->deps->queueFactory->get(QueueManagerInterface::QUEUE_LARGE_NAME);
    $normalCount = $normalQueue->numberOfItems();
    $largeCount = $largeQueue->numberOfItems();
    $queuedMediaCount = $this->deps->queueManager->getQueuedMediaCount();

    // Get active conversions.
    $activeCount = $this->deps->progressTracker->getActiveCount();

    // Get 24h statistics.
    $stats24h = $this->deps->statsService->get24HourStats();

    // Check if module is enabled.
    $isEnabled = (bool) $config->get('enabled');

    // Get hardware capabilities.
    $capabilities = $this->deps->hardwareDetector->getCapabilities();

    // Get available encoders.
    $encoders = $this->getAvailableEncoders();

    // Use dedup count for health determination — immune to orphaned
    // Drupal queue items that inflate numberOfItems() counts.
    $overallStatus = $this->determineOverallStatus(
      $ffmpegAvailable,
      $isEnabled,
      $queuedMediaCount,
      $stats24h['success_rate'],
      $stats24h['total'],
    );

    $maxFileSizeBytes = (int) ($config->get('max_file_size') ?? 524288000);

    $response = [
      'status' => $overallStatus,
      'timestamp' => date('c', $now),
      'checks' => [
        'ffmpeg' => [
          'status' => $ffmpegAvailable ? 'ok' : 'error',
          'version' => $ffmpegVersion,
        ],
        'enabled' => [
          'status' => $isEnabled ? 'ok' : 'disabled',
        ],
        'queue' => [
          'status' => $this->getQueueStatus($queuedMediaCount),
          'queued_media' => $queuedMediaCount,
          'normal_count' => $normalCount,
          'large_count' => $largeCount,
          'total_count' => $normalCount + $largeCount,
        ],
        'active_conversions' => [
          'status' => $activeCount < self::ACTIVE_BUSY_THRESHOLD ? 'ok' : 'busy',
          'count' => $activeCount,
          'limit_reached' => $this->deps->queueManager->isLimitReached(),
        ],
      ],
      'stats_24h' => $stats24h,
      'hardware' => [
        'gpu' => $capabilities['gpu_name'] ?? NULL,
        'hw_encoders' => $capabilities['available_encoders'] ?? [],
      ],
      'encoders' => $encoders,
      'config' => [
        'max_file_size_mb' => (int) round($maxFileSizeBytes / self::BYTES_PER_MIB),
        'ffmpeg_timeout_sec' => (int) ($config->get('ffmpeg_timeout') ?? 1800),
        'max_concurrent' => (int) ($config->get('max_concurrent') ?? 2),
      ],
    ];

    $httpStatus = $overallStatus === 'healthy' ? 200 : 503;

    return new JsonResponse($response, $httpStatus, [
      'Content-Type' => 'application/json',
      'Cache-Control' => 'no-cache, no-store, must-revalidate',
    ]);
  }

  /**
   * Returns a simple liveness probe response.
   *
   * This is a lightweight endpoint that only checks if the service is alive.
   * Suitable for Kubernetes liveness probes.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   Simple JSON response indicating the service is alive.
   */
  public function liveness(): JsonResponse {
    return new JsonResponse([
      'status' => 'alive',
      'timestamp' => date('c'),
    ], 200, [
      'Content-Type' => 'application/json',
      'Cache-Control' => 'no-cache, no-store, must-revalidate',
    ]);
  }

  /**
   * Returns a readiness probe response.
   *
   * Checks if the service is ready to accept and process conversion requests.
   * Suitable for Kubernetes readiness probes.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response indicating if the service is ready.
   */
  public function readiness(): JsonResponse {
    $config = $this->configFactory->get('ffmpeg_media.settings');
    $ffmpegAvailable = $this->deps->ffmpegExecutor->isAvailable();
    $isEnabled = (bool) $config->get('enabled');
    $limitReached = $this->deps->queueManager->isLimitReached();

    $isReady = $ffmpegAvailable && $isEnabled && !$limitReached;

    $response = [
      'status' => $isReady ? 'ready' : 'not_ready',
      'checks' => [
        'ffmpeg_available' => $ffmpegAvailable,
        'enabled' => $isEnabled,
        'capacity_available' => !$limitReached,
      ],
      'timestamp' => date('c'),
    ];

    $httpStatus = $isReady ? 200 : 503;

    return new JsonResponse($response, $httpStatus, [
      'Content-Type' => 'application/json',
      'Cache-Control' => 'no-cache, no-store, must-revalidate',
    ]);
  }

  /**
   * Returns a simplified health check response.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with health status.
   */
  public function check(): JsonResponse {
    $ffmpegAvailable = $this->deps->ffmpegExecutor->isAvailable();
    $ffmpegVersion = $this->deps->ffmpegExecutor->getVersion();
    $queuedMediaCount = $this->deps->queueManager->getQueuedMediaCount();
    $activeCount = $this->deps->progressTracker->getActiveCount();
    $capabilities = $this->deps->hardwareDetector->getCapabilities();

    $status = $ffmpegAvailable ? 'healthy' : 'degraded';

    $encoders = $this->getAvailableEncoders();

    return new JsonResponse([
      'status' => $status,
      'timestamp' => time(),
      'ffmpeg' => [
        'available' => $ffmpegAvailable,
        'version' => $ffmpegVersion,
      ],
      'encoders' => $encoders,
      'hardware' => [
        'gpu' => $capabilities['gpu_name'] ?? NULL,
        'hw_encoders' => $capabilities['available_encoders'] ?? [],
      ],
      'queue' => [
        'total' => $queuedMediaCount,
        'queued_media' => $queuedMediaCount,
        'pending' => max(0, $queuedMediaCount - $activeCount),
        'processing' => $activeCount,
      ],
      'conversions' => [
        'active' => $activeCount,
        'limit_reached' => $this->deps->queueManager->isLimitReached(),
      ],
    ], 200, [
      'Content-Type' => 'application/json',
      'Cache-Control' => 'no-cache, no-store, must-revalidate',
    ]);
  }

  /**
   * Gets available encoder status.
   *
   * @return array<string, bool>
   *   Map of encoder names to availability.
   */
  protected function getAvailableEncoders(): array {
    return [
      'libx264' => $this->deps->ffmpegExecutor->hasEncoder('libx264'),
      'libx265' => $this->deps->ffmpegExecutor->hasEncoder('libx265'),
      'libvpx-vp9' => $this->deps->ffmpegExecutor->hasEncoder('libvpx-vp9'),
      'libsvtav1' => $this->deps->ffmpegExecutor->hasEncoder('libsvtav1'),
      'libaom-av1' => $this->deps->ffmpegExecutor->hasEncoder('libaom-av1'),
    ];
  }

  /**
   * Determines the queue status based on item count.
   *
   * @param int $count
   *   Total queue item count.
   *
   * @return string
   *   Status: 'ok', 'warning', or 'critical'.
   */
  protected function getQueueStatus(int $count): string {
    if ($count < self::QUEUE_DEGRADED_THRESHOLD) {
      return 'ok';
    }

    if ($count < self::QUEUE_CRITICAL_THRESHOLD) {
      return 'warning';
    }

    return 'critical';
  }

  /**
   * Determines overall system health status.
   *
   * @param bool $ffmpegAvailable
   *   Whether FFmpeg is available.
   * @param bool $isEnabled
   *   Whether the module is enabled.
   * @param int $queueCount
   *   Total queue item count.
   * @param float $successRate
   *   The 24h success rate percentage.
   * @param int $totalConversions
   *   Total number of conversions in the 24h period.
   *
   * @return string
   *   Overall status: 'healthy', 'degraded', or 'unhealthy'.
   */
  protected function determineOverallStatus(
    bool $ffmpegAvailable,
    bool $isEnabled,
    int $queueCount,
    float $successRate,
    int $totalConversions,
  ): string {
    // Unhealthy if FFmpeg not available or module disabled.
    if (!$ffmpegAvailable || !$isEnabled) {
      return 'unhealthy';
    }

    // Degraded if queue is large.
    if ($queueCount >= self::QUEUE_DEGRADED_THRESHOLD) {
      return 'degraded';
    }

    // Degraded if success rate is low (only when conversions have occurred).
    if ($totalConversions > 0 && $successRate < 80.0) {
      return 'degraded';
    }

    return 'healthy';
  }

}
