<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_whisper\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\ffmpeg_media_whisper\Plugin\FFmpegProcessor\WhisperProcessor;

/**
 * Configure FFmpeg Media Whisper settings.
 */
final class WhisperSettingsForm extends ConfigFormBase {

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

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

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

    // Paths.
    $form['paths'] = [
      '#type' => 'details',
      '#title' => $this->t('Paths'),
      '#open' => TRUE,
    ];

    $form['paths']['whisper_cli_path'] = [
      '#type' => 'textfield',
      '#title' => $this->t('whisper-cli path'),
      '#description' => $this->t('Full path to the whisper-cli binary. Leave empty to auto-detect from system PATH.'),
      '#default_value' => $config->get('whisper_cli_path') ?? '',
    ];

    $form['paths']['model_dir'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Model directory'),
      '#description' => $this->t('Directory containing Whisper GGML model files (ggml-base.bin, etc.).'),
      '#default_value' => $config->get('model_dir') ?? '/usr/local/share/whisper/models',
      '#required' => TRUE,
    ];

    // Model & Language.
    $form['model_language'] = [
      '#type' => 'details',
      '#title' => $this->t('Model & Language'),
      '#open' => TRUE,
    ];

    $form['model_language']['model'] = [
      '#type' => 'select',
      '#title' => $this->t('Model'),
      '#description' => $this->t('The Whisper model size. Larger models are more accurate but slower.'),
      '#options' => WhisperProcessor::getAvailableModels(),
      '#default_value' => $config->get('model') ?? 'base',
      '#required' => TRUE,
    ];

    $form['model_language']['language'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Language'),
      '#description' => $this->t('ISO 639-1 language code (e.g., en, fr, de). Leave empty to let Whisper auto-detect the spoken language.'),
      '#default_value' => $config->get('language') ?? '',
    ];

    $form['model_language']['translate'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Translate to English'),
      '#description' => $this->t('Translate the detected language to English in the output.'),
      '#default_value' => $config->get('translate') ?? FALSE,
    ];

    $form['model_language']['output_format'] = [
      '#type' => 'select',
      '#title' => $this->t('Output format'),
      '#description' => $this->t('The subtitle output format.'),
      '#options' => WhisperProcessor::getAvailableFormats(),
      '#default_value' => $config->get('output_format') ?? 'srt',
      '#required' => TRUE,
    ];

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

    $form['advanced']['word_timestamps'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Word timestamps'),
      '#description' => $this->t('Include word-level timestamps in the output.'),
      '#default_value' => $config->get('word_timestamps') ?? FALSE,
    ];

    $form['advanced']['max_context'] = [
      '#type' => 'number',
      '#title' => $this->t('Maximum context tokens'),
      '#description' => $this->t('Maximum number of previous text tokens for the decoder context.'),
      '#default_value' => $config->get('max_context') ?? 224,
      '#min' => 1,
      '#max' => 448,
      '#required' => TRUE,
    ];

    $form['advanced']['beam_size'] = [
      '#type' => 'number',
      '#title' => $this->t('Beam size'),
      '#description' => $this->t('Number of beams for beam search decoding.'),
      '#default_value' => $config->get('beam_size') ?? 5,
      '#min' => 1,
      '#max' => 20,
      '#required' => TRUE,
    ];

    $form['advanced']['temperature'] = [
      '#type' => 'number',
      '#title' => $this->t('Temperature'),
      '#description' => $this->t('Sampling temperature. Lower values produce more deterministic results.'),
      '#default_value' => $config->get('temperature') ?? 0.0,
      '#min' => 0.0,
      '#max' => 1.0,
      '#step' => 0.1,
      '#required' => TRUE,
    ];

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

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

    $cliPath = (string) $form_state->getValue('whisper_cli_path');
    if ($cliPath !== '' && !is_executable($cliPath)) {
      $form_state->setErrorByName('whisper_cli_path', $this->t('The whisper-cli binary at %path is not found or not executable.', [
        '%path' => $cliPath,
      ]));
    }

    $modelDir = (string) $form_state->getValue('model_dir');
    if ($modelDir !== '') {
      if (str_contains($modelDir, '..') || str_contains($modelDir, "\0")) {
        $form_state->setErrorByName('model_dir', $this->t('Model directory must not contain path traversal sequences.'));
      }
      elseif (!str_starts_with($modelDir, '/')) {
        $form_state->setErrorByName('model_dir', $this->t('Model directory must be an absolute path.'));
      }
      elseif (!is_dir($modelDir)) {
        $form_state->setErrorByName('model_dir', $this->t('The model directory %dir does not exist.', [
          '%dir' => $modelDir,
        ]));
      }
    }

    $model = (string) $form_state->getValue('model');
    if ($modelDir !== '' && $model !== '' && is_dir($modelDir)) {
      $modelPath = rtrim($modelDir, '/') . '/ggml-' . $model . '.bin';
      if (!file_exists($modelPath)) {
        $this->messenger()->addWarning($this->t('Model file %file does not exist. Download it before using Whisper transcription.', [
          '%file' => $modelPath,
        ]));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $language = $form_state->getValue('language');
    $language = ($language !== '' && $language !== NULL) ? (string) $language : NULL;

    $this->config('ffmpeg_media_whisper.settings')
      ->set('whisper_cli_path', (string) $form_state->getValue('whisper_cli_path'))
      ->set('model_dir', (string) $form_state->getValue('model_dir'))
      ->set('model', (string) $form_state->getValue('model'))
      ->set('language', $language)
      ->set('translate', (bool) $form_state->getValue('translate'))
      ->set('output_format', (string) $form_state->getValue('output_format'))
      ->set('word_timestamps', (bool) $form_state->getValue('word_timestamps'))
      ->set('max_context', (int) $form_state->getValue('max_context'))
      ->set('beam_size', (int) $form_state->getValue('beam_size'))
      ->set('temperature', (float) $form_state->getValue('temperature'))
      ->save();

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

}
