<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_streaming\Plugin\FFmpegOutput;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\ffmpeg_media\Plugin\FFmpegOutput\AdaptiveOutputInterface;
use Drupal\ffmpeg_media\Plugin\FFmpegOutput\FFmpegOutputBase;
use Drupal\ffmpeg_media\Plugin\FFmpegOutput\SubtitleAwareOutputInterface;
use Drupal\ffmpeg_media\Service\BitrateAnalyzerInterface;
use Drupal\ffmpeg_media\ValueObject\ConversionOptions;
use Drupal\ffmpeg_media\ValueObject\VideoInfo;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * Base class for adaptive bitrate streaming output plugins (HLS/DASH).
 *
 * Provides shared functionality for multi-bitrate adaptive streaming:
 * - Per-title encoding with bitrate analysis
 * - Default representation ladder (360p/720p/1080p)
 * - Subtitle input argument building.
 */
abstract class AdaptiveBitrateOutputBase extends FFmpegOutputBase implements ContainerFactoryPluginInterface, AdaptiveOutputInterface, SubtitleAwareOutputInterface {

  /**
   * Constructs an AdaptiveBitrateOutputBase object.
   *
   * @param array<string, mixed> $configuration
   *   Plugin configuration.
   * @param string $plugin_id
   *   Plugin ID.
   * @param mixed $plugin_definition
   *   Plugin definition.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   File system service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   Config factory.
   * @param \Drupal\ffmpeg_media\Service\BitrateAnalyzerInterface|null $bitrateAnalyzer
   *   Bitrate analyzer for per-title encoding.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    FileSystemInterface $fileSystem,
    protected readonly ConfigFactoryInterface $configFactory,
    #[Autowire(service: BitrateAnalyzerInterface::class)]
    protected readonly ?BitrateAnalyzerInterface $bitrateAnalyzer = NULL,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $fileSystem);
  }

  /**
   * Gets representations, optionally using per-title encoding.
   *
   * @param \Drupal\ffmpeg_media\ValueObject\VideoInfo|null $videoInfo
   *   Source video info.
   *
   * @return array<int, array{name: string, width: int, height: int, bitrate: string, audio_bitrate: string}>
   *   Representation configurations.
   */
  #[\NoDiscard]
  protected function getRepresentations(?VideoInfo $videoInfo): array {
    $config = $this->configFactory->get('ffmpeg_media_streaming.settings');
    $usePerTitle = (bool) ($config->get('per_title_encoding') ?? FALSE);

    if ($usePerTitle && $videoInfo !== NULL && $this->bitrateAnalyzer !== NULL) {
      $ladder = $this->bitrateAnalyzer->analyzeBitrateLadder(
        $videoInfo->path,
        $videoInfo,
        ['max_rungs' => 5],
      );

      return array_map(fn($rung) => [
        'name' => $rung->getLabel(),
        'width' => $rung->width,
        'height' => $rung->height,
        'bitrate' => $rung->videoBitrate . 'k',
        'audio_bitrate' => ($rung->audioBitrate ?? 128) . 'k',
      ], $ladder);
    }

    $representations = $config->get('representations') ?? $this->getDefaultRepresentations();

    return $this->adaptRepresentationsToOrientation($representations, $videoInfo);
  }

  /**
   * Adapts representation dimensions for portrait-oriented source videos.
   *
   * Default representations assume landscape (e.g. 1920x1080). For portrait
   * sources the width and height are swapped so the encoded streams preserve
   * the original orientation instead of stretching to landscape.
   *
   * @param array<int, array{name: string, width: int, height: int, bitrate: string, audio_bitrate: string}> $representations
   *   Representation configurations.
   * @param \Drupal\ffmpeg_media\ValueObject\VideoInfo|null $videoInfo
   *   Source video info (NULL when unavailable).
   *
   * @return array<int, array{name: string, width: int, height: int, bitrate: string, audio_bitrate: string}>
   *   Adapted representations.
   */
  #[\NoDiscard]
  protected function adaptRepresentationsToOrientation(array $representations, ?VideoInfo $videoInfo): array {
    if ($videoInfo === NULL || !$videoInfo->isPortrait) {
      return $representations;
    }

    return array_map(static function (array $rep): array {
      [$rep['width'], $rep['height']] = [$rep['height'], $rep['width']];

      return $rep;
    }, $representations);
  }

  /**
   * Gets default representations for adaptive streaming.
   *
   * @return array<int, array{name: string, width: int, height: int, bitrate: string, audio_bitrate: string}>
   *   Default representation configurations.
   */
  #[\NoDiscard]
  protected function getDefaultRepresentations(): array {
    return [
      [
        'name' => '360p',
        'width' => 640,
        'height' => 360,
        'bitrate' => '800k',
        'audio_bitrate' => '96k',
      ],
      [
        'name' => '720p',
        'width' => 1280,
        'height' => 720,
        'bitrate' => '2500k',
        'audio_bitrate' => '128k',
      ],
      [
        'name' => '1080p',
        'width' => 1920,
        'height' => 1080,
        'bitrate' => '5000k',
        'audio_bitrate' => '192k',
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  #[\NoDiscard]
  abstract public function buildAdaptiveArguments(
    ConversionOptions $options,
    ?VideoInfo $videoInfo = NULL,
    array $audioTracks = [],
  ): array;

  /**
   * {@inheritdoc}
   */
  #[\NoDiscard]
  abstract public function buildAdaptiveArgumentsWithSubtitles(
    ConversionOptions $options,
    array $subtitleTracks,
  ): array;

  /**
   * {@inheritdoc}
   */
  #[\NoDiscard]
  public function buildSubtitleInputArguments(array $subtitleTracks): array {
    $args = [];

    foreach ($subtitleTracks as $track) {
      $args[] = '-i';
      $args[] = $track->path;
    }

    return $args;
  }

}
