<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Resolver;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\ffmpeg_media\Enum\OutputType;
use Drupal\ffmpeg_media\Service\OutputStorageServiceInterface;
use Drupal\ffmpeg_media\Service\ProfileManagerInterface;
use Drupal\ffmpeg_media\Service\SubtitleHandlerInterface;
use Drupal\ffmpeg_media\ValueObject\ConversionProfile;

/**
 * Resolves converted video sources from output storage.
 *
 * Extracts the shared source-resolution logic used by both
 * ConvertedVideoFormatter and VideoPlayerFormatter into a single
 * reusable service. Third-party formatters inject this service
 * to get converted outputs for playback.
 */
final class VideoSourceResolver implements VideoSourceResolverInterface {

  /**
   * MIME type mapping for video output formats.
   *
   * @var array<string, string>
   */
  private const MIME_TYPES = [
    'mp4' => 'video/mp4',
    'webm' => 'video/webm',
    'mkv' => 'video/x-matroska',
    'flv' => 'video/x-flv',
    'hls' => 'application/vnd.apple.mpegurl',
    'dash' => 'application/dash+xml',
  ];

  /**
   * Constructs a VideoSourceResolver.
   *
   * @param \Drupal\ffmpeg_media\Service\OutputStorageServiceInterface $outputStorage
   *   Output storage service.
   * @param \Drupal\ffmpeg_media\Service\ProfileManagerInterface $profileManager
   *   Profile manager service.
   * @param \Drupal\ffmpeg_media\Service\SubtitleHandlerInterface $subtitleHandler
   *   Subtitle handler for language extraction.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   Cache backend for resolved sources.
   */
  public function __construct(
    private readonly OutputStorageServiceInterface $outputStorage,
    private readonly ProfileManagerInterface $profileManager,
    private readonly SubtitleHandlerInterface $subtitleHandler,
    private readonly CacheBackendInterface $cache,
  ) {}

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function resolveSources(
    int $mediaId,
    string $fieldName,
    ?string $profileId = NULL,
    array $formatPriority = [],
  ): ResolvedSources {
    $resolvedProfileId = $profileId ?? $this->profileManager->getDefaultProfileId();

    // Determine format priority: explicit override → profile keys.
    if ($formatPriority === []) {
      $formatPriority = $this->profileManager->getEnabledOutputKeys($resolvedProfileId);
    }

    $cacheKey = 'ffmpeg_media:resolved:' . $mediaId . ':' . $fieldName . ':' . $resolvedProfileId . ':' . implode(',', $formatPriority);
    $cached = $this->cache->get($cacheKey);
    if ($cached !== FALSE) {
      return $cached->data;
    }

    // Batch-retrieve all outputs in a single query to avoid N+1.
    $allOutputs = $this->outputStorage->getAllOutputs($mediaId, $fieldName);

    // Resolve video sources from format priority.
    $sources = [];
    foreach ($formatPriority as $format) {
      $files = $allOutputs[$format] ?? [];
      $file = $files[0] ?? NULL;
      if ($file === NULL) {
        continue;
      }

      $container = ConversionProfile::containerFromKey($format);
      $sources[] = [
        'src' => $file->createFileUrl(),
        'type' => self::MIME_TYPES[$container] ?? $file->getMimeType() ?? 'application/octet-stream',
      ];
    }

    $isConverted = $sources !== [];

    // Resolve poster from thumbnail output.
    $poster = NULL;
    $thumbnailFiles = $allOutputs[OutputType::Thumbnail->value] ?? [];
    if ($thumbnailFiles !== []) {
      $poster = $thumbnailFiles[0]->createFileUrl();
    }

    // Prefer subtitle tracks, fall back to transcript.
    $subtitles = [];
    $subtitleFiles = $allOutputs[OutputType::SubtitleTracks->value] ?? [];

    foreach ($subtitleFiles as $file) {
      $filename = $file->getFilename();
      $language = '';
      if ($filename !== NULL) {
        $extracted = $this->subtitleHandler->extractLanguageFromFilename($filename);
        if ($extracted !== NULL) {
          $language = $this->subtitleHandler->normalizeLanguageCode($extracted);
        }
      }
      $subtitles[] = [
        'src' => $file->createFileUrl(),
        'kind' => 'subtitles',
        'language' => $language,
        'label' => basename($filename ?? 'Subtitles'),
      ];
    }

    if ($subtitles === []) {
      $transcriptFiles = $allOutputs[OutputType::Transcript->value] ?? [];
      if ($transcriptFiles !== []) {
        $subtitles[] = [
          'src' => $transcriptFiles[0]->createFileUrl(),
          'kind' => 'captions',
          'language' => '',
          'label' => 'Transcript',
        ];
      }
    }

    $cacheTags = [
      'media:' . $mediaId,
      'ffmpeg_media:outputs:' . $mediaId,
      'config:ffmpeg_media.settings',
      'config:ffmpeg_media.cdn',
      'config:ffmpeg_media.profile.' . $resolvedProfileId,
    ];

    $resolved = new ResolvedSources(
      sources: $sources,
      poster: $poster,
      subtitles: $subtitles,
      isConverted: $isConverted,
      cacheTags: $cacheTags,
    );

    $this->cache->set($cacheKey, $resolved, CacheBackendInterface::CACHE_PERMANENT, $cacheTags);

    return $resolved;
  }

}
