<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Service;

use Drupal\ffmpeg_media\Plugin\FFmpegCodec\FFmpegCodecInterface;
use Drupal\ffmpeg_media\ValueObject\ConversionOptions;
use Drupal\ffmpeg_media\ValueObject\VideoInfo;

/**
 * Interface for FFmpeg command execution service.
 *
 * @internal Runtime service, not part of the supported contrib extension API.
 */
interface FFmpegExecutorInterface {

  /**
   * Executes an FFmpeg command.
   *
   * @param array<int, string> $command
   *   The FFmpeg command to execute as an argument array.
   * @param array{timeout?: int|null, progress_callback?: \Closure, media_id?: int} $options
   *   Execution options:
   *   - timeout: int|null, maximum execution time in seconds.
   *   - progress_callback: callable, called with progress percentage.
   *   - media_id: int, media entity ID for progress tracking.
   *
   * @return bool
   *   TRUE if command executed successfully.
   *
   * @throws \Drupal\ffmpeg_media\Exception\FFmpegException
   *   If command execution fails.
   */
  #[\NoDiscard]
  public function execute(array $command, array $options = []): bool;

  /**
   * Executes an FFmpeg command with progress calculation based on duration.
   *
   * @param array<int, string> $command
   *   The FFmpeg command to execute as an argument array.
   * @param int $mediaId
   *   Media entity ID for progress tracking.
   * @param float|null $totalDuration
   *   Total video duration in seconds for percentage calculation.
   *   If NULL, progress cannot be calculated.
   * @param array{timeout?: int|null, progress_callback?: \Closure} $options
   *   Execution options:
   *   - timeout: int|null, maximum execution time in seconds.
   *   - progress_callback: callable, called with progress percentage (0-100).
   *
   * @return bool
   *   TRUE if command executed successfully.
   *
   * @throws \Drupal\ffmpeg_media\Exception\FFmpegException
   *   If command execution fails.
   */
  #[\NoDiscard]
  public function executeWithProgress(
    array $command,
    int $mediaId,
    ?float $totalDuration = NULL,
    array $options = [],
  ): bool;

  /**
   * Builds an FFmpeg command as an array for secure execution.
   *
   * This method returns the command as an array of arguments, which is
   * the recommended approach for security as it avoids shell interpretation
   * and potential command injection vulnerabilities.
   *
   * @param string $input
   *   Path to input file.
   * @param string $output
   *   Path to output file.
   * @param \Drupal\ffmpeg_media\Plugin\FFmpegCodec\FFmpegCodecInterface $codec
   *   Codec plugin to use.
   * @param \Drupal\ffmpeg_media\ValueObject\ConversionOptions $options
   *   Conversion options.
   *
   * @return array<int, string>
   *   Array of command arguments starting with ffmpeg path.
   */
  #[\NoDiscard]
  public function buildCommandArray(
    string $input,
    string $output,
    FFmpegCodecInterface $codec,
    ConversionOptions $options,
  ): array;

  /**
   * Gets video information using ffprobe.
   *
   * @param string $path
   *   Path to video file.
   *
   * @return \Drupal\ffmpeg_media\ValueObject\VideoInfo|null
   *   Video information or NULL if unable to probe.
   */
  #[\NoDiscard]
  public function getVideoInfo(string $path): ?VideoInfo;

  /**
   * Checks if FFmpeg is available.
   *
   * @return bool
   *   TRUE if FFmpeg is available and executable.
   */
  #[\NoDiscard]
  public function isAvailable(): bool;

  /**
   * Gets the FFmpeg version.
   *
   * @return string|null
   *   Version string or NULL if not available.
   */
  #[\NoDiscard]
  public function getVersion(): ?string;

  /**
   * Checks if a specific encoder is available.
   *
   * @param string $encoder
   *   Encoder name (e.g., 'libx264', 'libsvtav1').
   *
   * @return bool
   *   TRUE if encoder is available.
   */
  #[\NoDiscard]
  public function hasEncoder(string $encoder): bool;

  /**
   * Checks if a specific filter is available.
   *
   * @param string $filter
   *   Filter name (e.g., 'scale', 'thumbnail').
   *
   * @return bool
   *   TRUE if filter is available.
   */
  #[\NoDiscard]
  public function hasFilter(string $filter): bool;

  /**
   * Gets the path to ffmpeg binary.
   *
   * @return string
   *   Path to ffmpeg executable.
   */
  #[\NoDiscard]
  public function getFfmpegPath(): string;

  /**
   * Gets the path to ffprobe binary.
   *
   * @return string
   *   Path to ffprobe executable.
   */
  #[\NoDiscard]
  public function getFfprobePath(): string;

  /**
   * Checks if an error is related to hardware acceleration.
   *
   * @param string $errorOutput
   *   Error output from FFmpeg.
   * @param string $hwType
   *   Hardware type being used.
   *
   * @return bool
   *   TRUE if the error is hardware-related.
   */
  #[\NoDiscard]
  public function isHardwareError(string $errorOutput, string $hwType): bool;

  /**
   * Executes an FFmpeg command with hardware acceleration.
   *
   * This method injects hardware acceleration arguments into the command
   * before execution. It automatically handles inserting the appropriate
   * hwaccel flags based on the hardware type.
   *
   * @param array<int, string> $command
   *   The FFmpeg command to execute as an argument array.
   * @param string $hwType
   *   Hardware acceleration type (nvenc, vaapi, qsv, videotoolbox, amf).
   * @param array{timeout?: int|null, progress_callback?: \Closure} $options
   *   Execution options:
   *   - timeout: int|null, maximum execution time in seconds.
   *   - progress_callback: callable, called with progress percentage.
   *
   * @return bool
   *   TRUE if command executed successfully.
   *
   * @throws \Drupal\ffmpeg_media\Exception\FFmpegException
   *   If command execution fails.
   */
  #[\NoDiscard]
  public function executeWithHardware(
    array $command,
    string $hwType,
    array $options = [],
  ): bool;

}
