<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Service;

use Symfony\Component\Process\Process;

/**
 * Service for probing system-level GPU hardware presence.
 *
 * Checks for actual hardware devices on the system by examining
 * device files, DRM vendor information, and GPU management tools.
 * This ensures hardware acceleration detection reflects real hardware
 * availability rather than just FFmpeg compilation flags.
 *
 * @internal
 */
final class HardwareProbe implements HardwareProbeInterface {

  /**
   * Cached GPU vendor IDs.
   *
   * @var array<int, string>|null
   */
  protected ?array $cachedVendors = NULL;

  /**
   * Cached NVIDIA GPU presence result.
   */
  protected ?bool $cachedNvidiaPresent = NULL;

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function isNvidiaGpuPresent(): bool {
    if ($this->cachedNvidiaPresent !== NULL) {
      return $this->cachedNvidiaPresent;
    }

    // Check for NVIDIA device files (fastest check).
    if (file_exists('/dev/nvidia0') || file_exists('/dev/nvidiactl')) {
      $this->cachedNvidiaPresent = TRUE;
      return TRUE;
    }

    // Check DRM vendor files for NVIDIA vendor ID.
    if ($this->hasGpuVendor(self::VENDOR_NVIDIA)) {
      $this->cachedNvidiaPresent = TRUE;
      return TRUE;
    }

    // Fallback: try nvidia-smi (works without /dev/nvidia*).
    $process = new Process(['nvidia-smi', '-L']);
    $process->setTimeout(5);
    $process->run();

    $this->cachedNvidiaPresent = $process->isSuccessful()
      && !empty(trim($process->getOutput()));
    return $this->cachedNvidiaPresent;
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function isIntelGpuPresent(): bool {
    return $this->hasGpuVendor(self::VENDOR_INTEL);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function isAmdGpuPresent(): bool {
    return $this->hasGpuVendor(self::VENDOR_AMD);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function isDriDevicePresent(string $device = '/dev/dri/renderD128'): bool {
    return file_exists($device);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function hasGpuVendor(string $vendorId): bool {
    return in_array($vendorId, $this->getDetectedGpuVendors(), TRUE);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getDetectedGpuVendors(): array {
    if ($this->cachedVendors !== NULL) {
      return $this->cachedVendors;
    }

    $vendors = [];
    $vendorFiles = glob('/sys/class/drm/card*/device/vendor') ?: [];

    foreach ($vendorFiles as $file) {
      if (!is_readable($file)) {
        continue;
      }
      $content = file_get_contents($file);
      if ($content !== FALSE) {
        $vendor = $content |> trim(...) |> strtolower(...);
        if ($vendor !== '' && !in_array($vendor, $vendors, TRUE)) {
          $vendors[] = $vendor;
        }
      }
    }

    $this->cachedVendors = $vendors;
    return $vendors;
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function isAnyGpuPresent(): bool {
    return $this->isNvidiaGpuPresent()
      || !empty($this->getDetectedGpuVendors());
  }

}
