<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Service;

use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\ffmpeg_media\Plugin\FFmpegCodec\FFmpegCodecInterface;

/**
 * Validates output configurations for conversion profiles.
 *
 * Checks codec/container compatibility, audio codec compatibility,
 * two-pass encoding support, and experimental codec warnings.
 *
 * @internal
 */
final class ProfileOutputValidatorService implements ProfileOutputValidatorServiceInterface {

  use StringTranslationTrait;

  /**
   * Encoders that support two-pass encoding.
   *
   * @var array<int, string>
   */
  private const array TWO_PASS_ENCODERS = [
    'libx264',
    'libx265',
    'libvpx-vp9',
    'libsvtav1',
    'libvvenc',
  ];

  /**
   * Constructs a ProfileOutputValidatorService object.
   *
   * @param \Drupal\ffmpeg_media\Service\PluginFactoryInterface $pluginFactory
   *   Plugin factory for creating codec and output plugin instances.
   */
  public function __construct(
    private readonly PluginFactoryInterface $pluginFactory,
  ) {}

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function validateOutputs(array $outputs): array {
    $messages = [];

    foreach ($outputs as $output) {
      if (!is_array($output)) {
        continue;
      }
      $messages = [...$messages, ...$this->validateSingleOutput($output)];
    }

    return $messages;
  }

  /**
   * Validates a single output configuration.
   *
   * @param array<string, mixed> $output
   *   Output form values with keys: codec, container, options.
   *
   * @return array<int, string>
   *   Validation warning messages.
   */
  #[\NoDiscard]
  private function validateSingleOutput(array $output): array {
    $messages = [];
    $codecId = $output['codec'] ?? '';
    $containerId = $output['container'] ?? '';

    if ($codecId === '' || $containerId === '') {
      return $messages;
    }

    try {
      $codec = $this->pluginFactory->createCodecInstance($codecId);
    }
    catch (\Throwable) {
      return $messages;
    }

    // Validate container/codec compatibility.
    $supportedContainers = $codec->getSupportedContainers();
    if (!in_array($containerId, $supportedContainers, TRUE)) {
      $messages[] = (string) $this->t('The codec %codec does not support the %container container. Supported: %supported.', [
        '%codec' => $codec->getLabel(),
        '%container' => strtoupper($containerId),
        '%supported' => implode(', ', array_map('strtoupper', $supportedContainers)),
      ]);
    }

    // Validate audio codec compatibility.
    $audioCodecId = $output['options']['audio_codec'] ?? '';
    if ($audioCodecId !== '') {
      $messages = [...$messages, ...$this->validateAudioCodecCompatibility($containerId, $audioCodecId)];
    }

    // Validate two-pass compatibility.
    $twoPass = (bool) ($output['options']['two_pass'] ?? FALSE);
    if ($twoPass) {
      $messages = [...$messages, ...$this->validateTwoPassCompatibility($codec)];
    }

    // Warn about experimental codec status.
    if ($codecId === 'vvc') {
      $messages[] = (string) $this->t('VVC/H.266 is experimental with limited browser support (Safari only). Consider AV1 or VP9 for broader compatibility.');
    }

    return $messages;
  }

  /**
   * Validates audio codec compatibility with a container format.
   *
   * @param string $containerId
   *   Container format plugin ID.
   * @param string $audioCodecId
   *   Audio codec ID (e.g. 'aac', 'libopus').
   *
   * @return array<int, string>
   *   Validation warning messages.
   */
  #[\NoDiscard]
  private function validateAudioCodecCompatibility(string $containerId, string $audioCodecId): array {
    $messages = [];

    try {
      $outputPlugin = $this->pluginFactory->createOutputInstance($containerId);
    }
    catch (\Throwable) {
      return $messages;
    }

    if (!$outputPlugin->supportsAudio()) {
      $messages[] = (string) $this->t('The %container format does not support audio.', [
        '%container' => strtoupper($containerId),
      ]);
    }
    elseif (!$outputPlugin->supportsAudioCodec($audioCodecId)) {
      $supported = $outputPlugin->getSupportedAudioCodecs();
      $messages[] = (string) $this->t('The audio codec %audio is not compatible with %container. Supported: %supported.', [
        '%audio' => $audioCodecId,
        '%container' => strtoupper($containerId),
        '%supported' => implode(', ', $supported),
      ]);
    }

    return $messages;
  }

  /**
   * Validates two-pass encoding compatibility with a codec.
   *
   * @param \Drupal\ffmpeg_media\Plugin\FFmpegCodec\FFmpegCodecInterface $codec
   *   The codec plugin instance.
   *
   * @return array<int, string>
   *   Validation warning messages.
   */
  #[\NoDiscard]
  private function validateTwoPassCompatibility(FFmpegCodecInterface $codec): array {
    $encoder = $codec->getEncoder();
    if (in_array($encoder, self::TWO_PASS_ENCODERS, TRUE)) {
      return [];
    }

    return [
      (string) $this->t('Two-pass encoding is not supported with %codec (encoder: %encoder).', [
        '%codec' => $codec->getLabel(),
        '%encoder' => $encoder,
      ]),
    ];
  }

}
