<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_advanced\Form;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configure FFmpeg Media Advanced processing settings.
 */
final class AdvancedSettingsForm extends ConfigFormBase {

  /**
   * Constructs an AdvancedSettingsForm.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
   *   The typed config manager.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   File system service for stream wrapper resolution.
   * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $streamWrapperManager
   *   Stream wrapper manager for URI scheme validation.
   */
  public function __construct(
    ConfigFactoryInterface $config_factory,
    TypedConfigManagerInterface $typedConfigManager,
    protected readonly FileSystemInterface $fileSystem,
    protected readonly StreamWrapperManagerInterface $streamWrapperManager,
  ) {
    parent::__construct($config_factory, $typedConfigManager);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get('config.factory'),
      $container->get('config.typed'),
      $container->get('file_system'),
      $container->get('stream_wrapper_manager'),
    );
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function getFormId(): string {
    return 'ffmpeg_media_advanced_settings';
  }

  /**
   * {@inheritdoc}
   *
   * @phpstan-return array<int, string>
   */
  #[\Override]
  protected function getEditableConfigNames(): array {
    return ['ffmpeg_media_advanced.settings'];
  }

  /**
   * {@inheritdoc}
   *
   * @phpstan-return array<string, mixed>
   */
  #[\Override]
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $config = $this->config('ffmpeg_media_advanced.settings');

    // HDR Settings.
    $form['hdr'] = [
      '#type' => 'details',
      '#title' => $this->t('<abbr title="High Dynamic Range">HDR</abbr>'),
      '#open' => FALSE,
    ];

    $form['hdr']['hdr_passthrough_enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable HDR passthrough'),
      '#description' => $this->t('Pass HDR metadata through without tone mapping.'),
      '#default_value' => $config->get('hdr_passthrough_enabled') ?? FALSE,
    ];

    $form['hdr']['hdr_tone_mapping_algorithm'] = [
      '#type' => 'select',
      '#title' => $this->t('Tone mapping algorithm'),
      '#description' => $this->t('Algorithm used for HDR to <abbr title="Standard Dynamic Range">SDR</abbr> tone mapping.'),
      '#options' => [
        'hable' => $this->t('Hable'),
        'reinhard' => $this->t('Reinhard'),
        'mobius' => $this->t('Mobius'),
        'linear' => $this->t('Linear'),
      ],
      '#default_value' => $config->get('hdr_tone_mapping_algorithm') ?? 'hable',
    ];

    $form['hdr']['hdr_tone_mapping_peak'] = [
      '#type' => 'number',
      '#title' => $this->t('Tone mapping peak'),
      '#description' => $this->t('Peak luminance in nits.'),
      '#default_value' => $config->get('hdr_tone_mapping_peak') ?? 100.0,
      '#min' => 0,
      '#step' => 0.1,
    ];

    $form['hdr']['hdr_desaturation'] = [
      '#type' => 'number',
      '#title' => $this->t('Desaturation'),
      '#description' => $this->t('Desaturation strength applied during tone mapping.'),
      '#default_value' => $config->get('hdr_desaturation') ?? 0.0,
      '#min' => 0.0,
      '#max' => 1.0,
      '#step' => 0.1,
    ];

    // Two-Pass Encoding Settings.
    $form['two_pass'] = [
      '#type' => 'details',
      '#title' => $this->t('Two-Pass Encoding'),
      '#open' => TRUE,
    ];

    $form['two_pass']['two_pass_log_directory'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Log directory'),
      '#description' => $this->t('Directory for two-pass encoding log files.'),
      '#default_value' => $config->get('two_pass_log_directory') ?? 'temporary://ffmpeg_passlog',
    ];

    $form['two_pass']['two_pass_cleanup_logs'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Cleanup logs'),
      '#description' => $this->t('Remove two-pass log files after encoding completes.'),
      '#default_value' => $config->get('two_pass_cleanup_logs') ?? TRUE,
    ];

    // Watermark Settings.
    $form['watermark'] = [
      '#type' => 'details',
      '#title' => $this->t('Watermark'),
      '#open' => FALSE,
    ];

    $form['watermark']['watermark_image_path'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Image path'),
      '#description' => $this->t('Path to the watermark image file. Accepts stream URIs (e.g., <code>public://watermark.png</code>) or absolute paths.'),
      '#default_value' => $config->get('watermark_image_path') ?? '',
    ];

    $form['watermark']['watermark_text'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Text'),
      '#description' => $this->t('Text to overlay as a watermark.'),
      '#default_value' => $config->get('watermark_text') ?? '',
    ];

    $form['watermark']['watermark_position'] = [
      '#type' => 'select',
      '#title' => $this->t('Position'),
      '#description' => $this->t('Watermark placement position on the video.'),
      '#options' => [
        'top_left' => $this->t('Top left'),
        'top_right' => $this->t('Top right'),
        'bottom_left' => $this->t('Bottom left'),
        'bottom_right' => $this->t('Bottom right'),
        'center' => $this->t('Center'),
      ],
      '#default_value' => $config->get('watermark_position') ?? 'bottom_right',
    ];

    $form['watermark']['watermark_opacity'] = [
      '#type' => 'number',
      '#title' => $this->t('Opacity'),
      '#description' => $this->t('Watermark opacity from 0.0 (transparent) to 1.0 (opaque).'),
      '#default_value' => $config->get('watermark_opacity') ?? 0.7,
      '#min' => 0.0,
      '#max' => 1.0,
      '#step' => 0.1,
    ];

    $form['watermark']['watermark_scale'] = [
      '#type' => 'number',
      '#title' => $this->t('Scale'),
      '#description' => $this->t('Watermark size relative to video dimensions.'),
      '#default_value' => $config->get('watermark_scale') ?? 0.15,
      '#min' => 0.0,
      '#max' => 1.0,
      '#step' => 0.01,
    ];

    $form['watermark']['watermark_margin'] = [
      '#type' => 'number',
      '#title' => $this->t('Margin'),
      '#description' => $this->t('Margin in pixels from the video edge.'),
      '#default_value' => $config->get('watermark_margin') ?? 10,
      '#min' => 0,
    ];

    $form['watermark']['watermark_font_size'] = [
      '#type' => 'number',
      '#title' => $this->t('Font size'),
      '#description' => $this->t('Font size for text watermarks.'),
      '#default_value' => $config->get('watermark_font_size') ?? 24,
      '#min' => 1,
    ];

    $form['watermark']['watermark_font_color'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Font color'),
      '#description' => $this->t('Font color for text watermarks (e.g., white, #FFFFFF).'),
      '#default_value' => $config->get('watermark_font_color') ?? 'white',
    ];

    $form['watermark']['watermark_font_file'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Font file'),
      '#description' => $this->t('Path to a custom font file for text watermarks. Accepts stream URIs (e.g., <code>public://fonts/custom.ttf</code>) or absolute paths.'),
      '#default_value' => $config->get('watermark_font_file') ?? '',
    ];

    // Audio Normalization Settings.
    $form['normalization'] = [
      '#type' => 'details',
      '#title' => $this->t('Audio Normalization'),
      '#open' => FALSE,
    ];

    $form['normalization']['normalize_mode'] = [
      '#type' => 'select',
      '#title' => $this->t('Normalization mode'),
      '#description' => $this->t('Audio normalization standard to apply.'),
      '#options' => [
        'ebu_r128' => $this->t('<abbr title="European Broadcasting Union">EBU</abbr> R128'),
        'peak' => $this->t('Peak'),
        'rms' => $this->t('<abbr title="Root Mean Square">RMS</abbr>'),
      ],
      '#default_value' => $config->get('normalize_mode') ?? 'ebu_r128',
    ];

    $form['normalization']['normalize_preset'] = [
      '#type' => 'select',
      '#title' => $this->t('Normalization preset'),
      '#description' => $this->t('Predefined normalization target values.'),
      '#options' => [
        'streaming' => $this->t('Streaming'),
        'broadcast' => $this->t('Broadcast'),
        'podcast' => $this->t('Podcast'),
        'music' => $this->t('Music'),
      ],
      '#default_value' => $config->get('normalize_preset') ?? 'streaming',
    ];

    $form['normalization']['normalize_target_i'] = [
      '#type' => 'number',
      '#title' => $this->t('Target integrated loudness (<abbr title="Loudness Units Full Scale">LUFS</abbr>)'),
      '#description' => $this->t('Target integrated loudness in LUFS for EBU R128.'),
      '#default_value' => $config->get('normalize_target_i') ?? -16.0,
      '#step' => 0.1,
      '#states' => [
        'visible' => [
          ':input[name="normalize_mode"]' => ['value' => 'ebu_r128'],
        ],
      ],
    ];

    $form['normalization']['normalize_target_tp'] = [
      '#type' => 'number',
      '#title' => $this->t('Target true peak (<abbr title="Decibels True Peak">dBTP</abbr>)'),
      '#description' => $this->t('Maximum true peak level in dBTP for EBU R128.'),
      '#default_value' => $config->get('normalize_target_tp') ?? -1.5,
      '#step' => 0.1,
      '#states' => [
        'visible' => [
          ':input[name="normalize_mode"]' => ['value' => 'ebu_r128'],
        ],
      ],
    ];

    $form['normalization']['normalize_target_lra'] = [
      '#type' => 'number',
      '#title' => $this->t('Target loudness range (<abbr title="Loudness Units">LU</abbr>)'),
      '#description' => $this->t('Target <abbr title="Loudness Range">LRA</abbr> in LU for EBU R128.'),
      '#default_value' => $config->get('normalize_target_lra') ?? 11.0,
      '#step' => 0.1,
      '#states' => [
        'visible' => [
          ':input[name="normalize_mode"]' => ['value' => 'ebu_r128'],
        ],
      ],
    ];

    $form['normalization']['normalize_peak_level'] = [
      '#type' => 'number',
      '#title' => $this->t('Peak level (dB)'),
      '#description' => $this->t('Target peak level in dB for peak normalization.'),
      '#default_value' => $config->get('normalize_peak_level') ?? -1.0,
      '#step' => 0.1,
      '#states' => [
        'visible' => [
          ':input[name="normalize_mode"]' => ['value' => 'peak'],
        ],
      ],
    ];

    $form['normalization']['normalize_rms_level'] = [
      '#type' => 'number',
      '#title' => $this->t('RMS level (<abbr title="Decibels">dB</abbr>)'),
      '#description' => $this->t('Target RMS level in dB for RMS normalization.'),
      '#default_value' => $config->get('normalize_rms_level') ?? -18.0,
      '#step' => 0.1,
      '#states' => [
        'visible' => [
          ':input[name="normalize_mode"]' => ['value' => 'rms'],
        ],
      ],
    ];

    // Subtitles Settings.
    $form['subtitles'] = [
      '#type' => 'details',
      '#title' => $this->t('Subtitles'),
      '#open' => FALSE,
    ];

    $form['subtitles']['subtitles_font_name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Font name'),
      '#description' => $this->t('Font family name for subtitle rendering.'),
      '#default_value' => $config->get('subtitles_font_name') ?? 'Arial',
    ];

    $form['subtitles']['subtitles_font_size'] = [
      '#type' => 'number',
      '#title' => $this->t('Font size'),
      '#description' => $this->t('Font size for subtitle text.'),
      '#default_value' => $config->get('subtitles_font_size') ?? 24,
      '#min' => 1,
    ];

    $form['subtitles']['subtitles_primary_color'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Primary color'),
      '#description' => $this->t('Subtitle text color in hex RGB (e.g., FFFFFF).'),
      '#default_value' => $config->get('subtitles_primary_color') ?? 'FFFFFF',
    ];

    $form['subtitles']['subtitles_outline_color'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Outline color'),
      '#description' => $this->t('Subtitle outline color in hex RGB (e.g., 000000).'),
      '#default_value' => $config->get('subtitles_outline_color') ?? '000000',
    ];

    $form['subtitles']['subtitles_outline_width'] = [
      '#type' => 'number',
      '#title' => $this->t('Outline width'),
      '#description' => $this->t('Width of the subtitle text outline.'),
      '#default_value' => $config->get('subtitles_outline_width') ?? 2,
      '#min' => 0,
    ];

    $form['subtitles']['subtitles_position'] = [
      '#type' => 'select',
      '#title' => $this->t('Position'),
      '#description' => $this->t('Vertical position of subtitles on the video.'),
      '#options' => [
        'top' => $this->t('Top'),
        'bottom' => $this->t('Bottom'),
        'middle' => $this->t('Middle'),
      ],
      '#default_value' => $config->get('subtitles_position') ?? 'bottom',
    ];

    $form['subtitles']['subtitles_margin_v'] = [
      '#type' => 'number',
      '#title' => $this->t('Vertical margin'),
      '#description' => $this->t('Vertical margin in pixels from the edge.'),
      '#default_value' => $config->get('subtitles_margin_v') ?? 30,
      '#min' => 0,
    ];

    $form['subtitles']['subtitles_encoding'] = [
      '#type' => 'select',
      '#title' => $this->t('Character encoding'),
      '#description' => $this->t('Character encoding for subtitle files.'),
      '#options' => [
        'UTF-8' => $this->t('UTF-8'),
        'ISO-8859-1' => $this->t('ISO-8859-1'),
        'ASCII' => $this->t('ASCII'),
        'UTF-16' => $this->t('UTF-16'),
      ],
      '#default_value' => $config->get('subtitles_encoding') ?? 'UTF-8',
    ];

    $form['subtitles']['subtitles_force_style'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Force style'),
      '#description' => $this->t('Override subtitle file styles with these settings.'),
      '#default_value' => $config->get('subtitles_force_style') ?? TRUE,
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    parent::validateForm($form, $form_state);

    // Validate two-pass log directory.
    $logDir = (string) $form_state->getValue('two_pass_log_directory');
    if ($logDir !== '') {
      if (str_contains($logDir, '://')) {
        $scheme = explode('://', $logDir, 2)[0];
        if (!$this->streamWrapperManager->getViaScheme($scheme)) {
          $form_state->setErrorByName('two_pass_log_directory', $this->t('The stream wrapper @scheme:// is not available.', [
            '@scheme' => $scheme,
          ]));
        }
      }
      elseif (!str_starts_with($logDir, '/') && !preg_match('#^[A-Za-z]:[/\\\\]#', $logDir)) {
        $form_state->setErrorByName('two_pass_log_directory', $this->t('The log directory must be an absolute path or a valid stream URI (e.g., temporary://ffmpeg_passlog).'));
      }
    }

    // Validate watermark image path (supports stream URIs and absolute paths).
    $watermarkImagePath = (string) $form_state->getValue('watermark_image_path');
    if ($watermarkImagePath !== '') {
      $this->validateFilePath($form_state, $watermarkImagePath, 'watermark_image_path', 'watermark image');
    }

    // Validate watermark font color (named colors or hex).
    $fontColor = trim((string) $form_state->getValue('watermark_font_color'));
    if ($fontColor !== '') {
      $allowedNamedColors = [
        'white', 'black', 'red', 'green', 'blue', 'yellow', 'cyan', 'magenta',
        'gray', 'grey', 'orange', 'purple', 'pink', 'brown', 'lime', 'navy',
        'teal', 'maroon', 'olive', 'aqua', 'fuchsia', 'silver',
      ];
      $normalizedColor = ltrim($fontColor, '#');
      $isNamedColor = in_array(strtolower($fontColor), $allowedNamedColors, TRUE);
      $isHexColor = preg_match('/^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/', $normalizedColor);
      if (!$isNamedColor && !$isHexColor) {
        $form_state->setErrorByName('watermark_font_color', $this->t('Font color must be a named color (e.g., white, red) or a hex RGB value (e.g., #FFFFFF).'));
      }
    }

    // Validate watermark font file (supports stream URIs and absolute paths).
    $watermarkFontFile = (string) $form_state->getValue('watermark_font_file');
    if ($watermarkFontFile !== '') {
      $this->validateFilePath($form_state, $watermarkFontFile, 'watermark_font_file', 'watermark font');
    }

    // Validate HDR tone mapping peak.
    $toneMappingPeak = $form_state->getValue('hdr_tone_mapping_peak');
    if ($toneMappingPeak !== NULL && $toneMappingPeak !== '' && (float) $toneMappingPeak <= 0) {
      $form_state->setErrorByName('hdr_tone_mapping_peak', $this->t('The tone mapping peak must be greater than 0.'));
    }
    if ($toneMappingPeak !== NULL && $toneMappingPeak !== '' && (float) $toneMappingPeak > 10000) {
      $form_state->setErrorByName('hdr_tone_mapping_peak', $this->t('The tone mapping peak must not exceed 10000 nits.'));
    }

    // Validate hex color fields.
    $colorFields = [
      'subtitles_primary_color' => $this->t('Primary color'),
      'subtitles_outline_color' => $this->t('Outline color'),
    ];

    foreach ($colorFields as $field => $label) {
      $value = (string) $form_state->getValue($field);
      $value = ltrim($value, '#');
      if ($value !== '' && !preg_match('/^[0-9A-Fa-f]{6}$/', $value)) {
        $form_state->setErrorByName($field, $this->t('@label must be a 6-character hex RGB value (e.g., FFFFFF).', [
          '@label' => $label,
        ]));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $this->config('ffmpeg_media_advanced.settings')
      // HDR settings.
      ->set('hdr_passthrough_enabled', (bool) $form_state->getValue('hdr_passthrough_enabled'))
      ->set('hdr_tone_mapping_algorithm', (string) $form_state->getValue('hdr_tone_mapping_algorithm'))
      ->set('hdr_tone_mapping_peak', (float) $form_state->getValue('hdr_tone_mapping_peak'))
      ->set('hdr_desaturation', (float) $form_state->getValue('hdr_desaturation'))
      // Two-Pass settings.
      ->set('two_pass_log_directory', (string) $form_state->getValue('two_pass_log_directory'))
      ->set('two_pass_cleanup_logs', (bool) $form_state->getValue('two_pass_cleanup_logs'))
      // Watermark settings.
      ->set('watermark_image_path', (string) $form_state->getValue('watermark_image_path'))
      ->set('watermark_text', (string) $form_state->getValue('watermark_text'))
      ->set('watermark_position', (string) $form_state->getValue('watermark_position'))
      ->set('watermark_opacity', (float) $form_state->getValue('watermark_opacity'))
      ->set('watermark_scale', (float) $form_state->getValue('watermark_scale'))
      ->set('watermark_margin', (int) $form_state->getValue('watermark_margin'))
      ->set('watermark_font_size', (int) $form_state->getValue('watermark_font_size'))
      ->set('watermark_font_color', (string) $form_state->getValue('watermark_font_color'))
      ->set('watermark_font_file', (string) $form_state->getValue('watermark_font_file'))
      // Audio Normalization settings.
      ->set('normalize_mode', (string) $form_state->getValue('normalize_mode'))
      ->set('normalize_preset', (string) $form_state->getValue('normalize_preset'))
      ->set('normalize_target_i', (float) $form_state->getValue('normalize_target_i'))
      ->set('normalize_target_tp', (float) $form_state->getValue('normalize_target_tp'))
      ->set('normalize_target_lra', (float) $form_state->getValue('normalize_target_lra'))
      ->set('normalize_peak_level', (float) $form_state->getValue('normalize_peak_level'))
      ->set('normalize_rms_level', (float) $form_state->getValue('normalize_rms_level'))
      // Subtitles settings.
      ->set('subtitles_font_name', (string) $form_state->getValue('subtitles_font_name'))
      ->set('subtitles_font_size', (int) $form_state->getValue('subtitles_font_size'))
      ->set('subtitles_primary_color', (string) $form_state->getValue('subtitles_primary_color'))
      ->set('subtitles_outline_color', (string) $form_state->getValue('subtitles_outline_color'))
      ->set('subtitles_outline_width', (int) $form_state->getValue('subtitles_outline_width'))
      ->set('subtitles_position', (string) $form_state->getValue('subtitles_position'))
      ->set('subtitles_margin_v', (int) $form_state->getValue('subtitles_margin_v'))
      ->set('subtitles_encoding', (string) $form_state->getValue('subtitles_encoding'))
      ->set('subtitles_force_style', (bool) $form_state->getValue('subtitles_force_style'))
      ->save();

    parent::submitForm($form, $form_state);
  }

  /**
   * Validates a file path, supporting both stream URIs and absolute paths.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param string $path
   *   The file path or stream URI to validate.
   * @param string $element
   *   The form element name for error reporting.
   * @param string $label
   *   Human-readable label for the file type.
   */
  private function validateFilePath(FormStateInterface $form_state, string $path, string $element, string $label): void {
    // Stream URI: validate the scheme is registered, then resolve.
    if (str_contains($path, '://')) {
      $scheme = explode('://', $path, 2)[0];
      if (!$this->streamWrapperManager->getViaScheme($scheme)) {
        $form_state->setErrorByName($element, $this->t('The stream wrapper @scheme:// is not available for the @label file.', [
          '@scheme' => $scheme,
          '@label' => $label,
        ]));
        return;
      }
    }

    $realpath = $this->fileSystem->realpath($path);
    if ($realpath === FALSE || !is_file($realpath) || !is_readable($realpath)) {
      $form_state->setErrorByName($element, $this->t('The @label file at %path does not exist or is not readable.', [
        '%path' => $path,
        '@label' => $label,
      ]));
    }
  }

}
