<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_image\Plugin\FFmpegProcessor;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\ffmpeg_media\Attribute\FFmpegProcessor;
use Drupal\ffmpeg_media\Plugin\FFmpegProcessor\FFmpegProcessorBase;
use Drupal\ffmpeg_media\Service\FFmpegExecutorInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * Processor plugin for generating video thumbnails.
 */
#[FFmpegProcessor(
  id: 'thumbnail',
  label: new TranslatableMarkup('Thumbnail Generator'),
  description: new TranslatableMarkup('Extracts a frame from video as thumbnail image.'),
  outputType: 'image',
  outputExtension: 'jpg',
  requiredFilters: ['scale'],
  weight: 0,
)]
final class ThumbnailProcessor extends FFmpegProcessorBase {

  /**
   * Constructs a ThumbnailProcessor object.
   *
   * @param array<string, mixed> $configuration
   *   Plugin configuration.
   * @param string $plugin_id
   *   Plugin ID.
   * @param mixed $plugin_definition
   *   Plugin definition.
   * @param \Drupal\ffmpeg_media\Service\FFmpegExecutorInterface $ffmpegExecutor
   *   FFmpeg executor service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   Config factory service.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    #[Autowire(service: FFmpegExecutorInterface::class)]
    FFmpegExecutorInterface $ffmpegExecutor,
    protected readonly ConfigFactoryInterface $configFactory,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $ffmpegExecutor);
  }

  /**
   * {@inheritdoc}
   *
   * @param string $inputPath
   *   Input file path.
   * @param string $outputPath
   *   Output file path.
   * @param array<string, mixed> $options
   *   Processing options.
   */
  #[\Override]
  #[\NoDiscard]
  public function process(string $inputPath, string $outputPath, array $options = []): bool {
    $options = $this->mergeOptions($options);

    $timestamp = $options['timestamp'] ?? 1.0;
    $width = $options['width'] ?? 640;
    $height = $options['height'] ?? -1;

    $command = $this->buildThumbnailCommand($inputPath, $outputPath, $timestamp, $width, $height);

    return $this->ffmpegExecutor->execute($command);
  }

  /**
   * {@inheritdoc}
   *
   * @return array<string, int|float>
   *   Default option values.
   */
  #[\Override]
  #[\NoDiscard]
  public function getDefaultOptions(): array {
    $imageConfig = $this->configFactory->get('ffmpeg_media_image.settings');
    $coreConfig = $this->configFactory->get('ffmpeg_media.settings');

    return [
      'timestamp' => $imageConfig->get('thumbnail_time_offset') ?? $coreConfig->get('thumbnail_time_offset') ?? $coreConfig->get('thumbnail_timestamp') ?? 1.0,
      'width' => $imageConfig->get('thumbnail_width') ?? $coreConfig->get('thumbnail_width') ?? 640,
      'height' => $coreConfig->get('thumbnail_height') ?? -1,
      'quality' => 2,
    ];
  }

  /**
   * {@inheritdoc}
   *
   * @param string $inputPath
   *   Input file path.
   * @param array<string, mixed> $options
   *   Processing options.
   */
  #[\Override]
  #[\NoDiscard]
  public function generateOutputPath(string $inputPath, array $options = []): string {
    $baseName = pathinfo($inputPath, PATHINFO_FILENAME) ?: 'video';
    $directory = pathinfo($inputPath, PATHINFO_DIRNAME) ?: '';

    return $directory . DIRECTORY_SEPARATOR . $baseName . '_thumbnail.jpg';
  }

  /**
   * Builds the FFmpeg command array for thumbnail generation.
   *
   * @param string $input
   *   Input file path.
   * @param string $output
   *   Output file path.
   * @param float $timestamp
   *   Timestamp in seconds.
   * @param int $width
   *   Thumbnail width.
   * @param int $height
   *   Thumbnail height (-1 for auto).
   *
   * @return array<int, string>
   *   Complete FFmpeg command as array of arguments.
   */
  protected function buildThumbnailCommand(
    string $input,
    string $output,
    float $timestamp,
    int $width,
    int $height,
  ): array {
    $ffmpegPath = $this->ffmpegExecutor->getFfmpegPath();

    // Use select filter for accurate frame selection.
    $scaleFilter = "scale={$width}:{$height}:force_original_aspect_ratio=decrease";

    return [
      $ffmpegPath,
      '-y',
      '-ss', number_format($timestamp, 2, '.', ''),
      '-i', $input,
      '-vframes', '1',
      '-vf', $scaleFilter,
      '-q:v', '2',
      $output,
    ];
  }

}
