<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_advanced\VideoFilter;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\ffmpeg_media_advanced\Plugin\FFmpegProcessor\WatermarkProcessor;
use Drupal\ffmpeg_media\VideoFilter\FilterContext;
use Drupal\ffmpeg_media\VideoFilter\VideoFilterProviderInterface;
use Drupal\ffmpeg_media\VideoFilter\VideoFilterResult;

/**
 * Provides watermark overlay filter for video conversions.
 *
 * Reads watermark configuration from ffmpeg_media_advanced.settings and
 * contributes the appropriate filter_complex fragment so the watermark
 * is burned into every converted output (MP4, WebM, HLS, DASH, etc.).
 */
final class WatermarkFilterProvider implements VideoFilterProviderInterface {

  /**
   * Constructs a WatermarkFilterProvider.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   Config factory.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   File system service.
   */
  public function __construct(
    private readonly ConfigFactoryInterface $configFactory,
    private readonly FileSystemInterface $fileSystem,
  ) {}

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getFilter(FilterContext $context): ?VideoFilterResult {
    $config = $this->configFactory->get('ffmpeg_media_advanced.settings');

    $imagePath = (string) ($config->get('watermark_image_path') ?? '');
    $text = (string) ($config->get('watermark_text') ?? '');

    if ($imagePath !== '') {
      return $this->buildImageFilter($imagePath, $config);
    }

    if ($text !== '') {
      return $this->buildTextFilter($text, $config);
    }

    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  #[\NoDiscard]
  public function getWeight(): int {
    // Overlays run after scaling/HDR but before output adjustments.
    return 25;
  }

  /**
   * Builds filter result for image watermark overlay.
   *
   * @param string $imagePath
   *   Stream URI or path to watermark image.
   * @param \Drupal\Core\Config\ImmutableConfig $config
   *   Advanced settings config.
   *
   * @return \Drupal\ffmpeg_media\VideoFilter\VideoFilterResult|null
   *   Filter result or NULL if image not found.
   */
  private function buildImageFilter(string $imagePath, $config): ?VideoFilterResult {
    $realPath = $this->fileSystem->realpath($imagePath);
    if ($realPath === FALSE) {
      return NULL;
    }

    if (!file_exists($realPath)) {
      return NULL;
    }

    $scale = (float) ($config->get('watermark_scale') ?? 0.15);
    $opacity = (float) ($config->get('watermark_opacity') ?? 0.7);
    $position = $this->buildPositionExpression($config);

    $filterComplex = sprintf(
      '[watermark_in]scale=iw*%s:-1,format=rgba,colorchannelmixer=aa=%s[wm];[video_in][wm]overlay=%s[video_out]',
      number_format($scale, 2),
      number_format($opacity, 2),
      $position,
    );

    return new VideoFilterResult(
      filterComplex: $filterComplex,
      extraInputs: [$realPath],
      extraInputLabels: ['[watermark_in]'],
    );
  }

  /**
   * Builds filter result for text watermark.
   *
   * @param string $text
   *   Watermark text.
   * @param \Drupal\Core\Config\ImmutableConfig $config
   *   Advanced settings config.
   *
   * @return \Drupal\ffmpeg_media\VideoFilter\VideoFilterResult
   *   Filter result.
   */
  private function buildTextFilter(string $text, $config): VideoFilterResult {
    $fontSize = (int) ($config->get('watermark_font_size') ?? 24);
    $fontColor = WatermarkProcessor::sanitizeFontColor(
      (string) ($config->get('watermark_font_color') ?? 'white'),
    );
    $opacity = (float) ($config->get('watermark_opacity') ?? 0.7);
    $textPosition = $this->buildTextPosition($config);

    // Escape text for FFmpeg drawtext filter (two-level escaping).
    // Must match the escaping in WatermarkProcessor::processTextWatermark().
    $text = str_replace(
      ['\\', ':', "'", '%', ';', '[', ']'],
      ['\\\\', '\\:', "\\'", '%%', '\\;', '\\[', '\\]'],
      $text,
    );

    $drawtext = sprintf(
      "drawtext=text='%s':fontsize=%d:fontcolor=%s@%s:%s",
      $text,
      $fontSize,
      $fontColor,
      number_format($opacity, 2),
      $textPosition,
    );

    $fontFile = (string) ($config->get('watermark_font_file') ?? '');
    if ($fontFile !== '') {
      $realFontPath = $this->fileSystem->realpath($fontFile);
      if ($realFontPath !== FALSE && file_exists($realFontPath)) {
        // Escape the font path for the drawtext filter parser.
        $escapedFontPath = str_replace(
          ['\\', ':', "'", '%', ';', '[', ']'],
          ['\\\\', '\\:', "\\'", '%%', '\\;', '\\[', '\\]'],
          $realFontPath,
        );
        $drawtext .= ':fontfile=' . $escapedFontPath;
      }
    }

    return new VideoFilterResult(
      filterComplex: '[video_in]' . $drawtext . '[video_out]',
    );
  }

  /**
   * Builds overlay position expression for image watermark.
   *
   * @param \Drupal\Core\Config\ImmutableConfig $config
   *   Advanced settings config.
   *
   * @return string
   *   FFmpeg overlay position expression.
   */
  private function buildPositionExpression($config): string {
    $position = (string) ($config->get('watermark_position') ?? 'bottom_right');
    $margin = (int) ($config->get('watermark_margin') ?? 10);

    return match ($position) {
      'top_left' => sprintf('%d:%d', $margin, $margin),
      'top_right' => sprintf('W-w-%d:%d', $margin, $margin),
      'bottom_left' => sprintf('%d:H-h-%d', $margin, $margin),
      'bottom_right' => sprintf('W-w-%d:H-h-%d', $margin, $margin),
      'center' => '(W-w)/2:(H-h)/2',
      default => sprintf('W-w-%d:H-h-%d', $margin, $margin),
    };
  }

  /**
   * Builds position expression for text watermark.
   *
   * @param \Drupal\Core\Config\ImmutableConfig $config
   *   Advanced settings config.
   *
   * @return string
   *   FFmpeg drawtext position expression.
   */
  private function buildTextPosition($config): string {
    $position = (string) ($config->get('watermark_position') ?? 'bottom_right');
    $margin = (int) ($config->get('watermark_margin') ?? 10);

    return match ($position) {
      'top_left' => sprintf('x=%d:y=%d', $margin, $margin),
      'top_right' => sprintf('x=w-tw-%d:y=%d', $margin, $margin),
      'bottom_left' => sprintf('x=%d:y=h-th-%d', $margin, $margin),
      'bottom_right' => sprintf('x=w-tw-%d:y=h-th-%d', $margin, $margin),
      'center' => 'x=(w-tw)/2:y=(h-th)/2',
      default => sprintf('x=w-tw-%d:y=h-th-%d', $margin, $margin),
    };
  }

}
