<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_whisper\Form;

use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\ffmpeg_media\Enum\OutputType;
use Drupal\ffmpeg_media\FFmpegProcessorPluginManager;
use Drupal\ffmpeg_media\Plugin\FFmpegProcessor\FFmpegProcessorInterface;
use Drupal\ffmpeg_media\Service\ConversionFileManagerInterface;
use Drupal\ffmpeg_media\Service\FormatterSettingsResolverInterface;
use Drupal\ffmpeg_media\Service\OutputStorageServiceInterface;
use Drupal\ffmpeg_media\Service\SourceFileResolverInterface;
use Drupal\media\MediaInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Confirmation form for on-demand Whisper transcription.
 */
final class TranscribeForm extends ConfirmFormBase {

  /**
   * The media entity being transcribed.
   */
  protected ?MediaInterface $media = NULL;

  /**
   * The resolved field name from the query parameter.
   */
  protected string $fieldName = '';

  /**
   * Constructs a TranscribeForm object.
   *
   * @param \Drupal\ffmpeg_media\Service\FormatterSettingsResolverInterface $formatterSettingsResolver
   *   Formatter settings resolver service.
   * @param \Drupal\ffmpeg_media\Service\SourceFileResolverInterface $sourceFileResolver
   *   Source file resolver service.
   * @param \Drupal\ffmpeg_media\Service\OutputStorageServiceInterface $outputStorage
   *   Output storage service.
   * @param \Drupal\ffmpeg_media\FFmpegProcessorPluginManager $processorManager
   *   Processor plugin manager.
   * @param \Drupal\ffmpeg_media\Service\ConversionFileManagerInterface $conversionFileManager
   *   Conversion file manager.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   File system service.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
   *   Module handler service.
   */
  public function __construct(
    protected readonly FormatterSettingsResolverInterface $formatterSettingsResolver,
    protected readonly SourceFileResolverInterface $sourceFileResolver,
    protected readonly OutputStorageServiceInterface $outputStorage,
    protected readonly FFmpegProcessorPluginManager $processorManager,
    protected readonly ConversionFileManagerInterface $conversionFileManager,
    protected readonly FileSystemInterface $fileSystem,
    protected readonly ModuleHandlerInterface $moduleHandler,
  ) {}

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public static function create(ContainerInterface $container): static {
    $formatterSettingsResolver = $container->get(FormatterSettingsResolverInterface::class);
    $sourceFileResolver = $container->get(SourceFileResolverInterface::class);
    $outputStorage = $container->get(OutputStorageServiceInterface::class);
    $processorManager = $container->get('plugin.manager.ffmpeg_processor');
    $conversionFileManager = $container->get(ConversionFileManagerInterface::class);
    $fileSystem = $container->get('file_system');
    $moduleHandler = $container->get('module_handler');

    return new static(
      $formatterSettingsResolver,
      $sourceFileResolver,
      $outputStorage,
      $processorManager,
      $conversionFileManager,
      $fileSystem,
      $moduleHandler,
    );
  }

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

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function getQuestion(): TranslatableMarkup {
    if (!$this->media instanceof MediaInterface) {
      return $this->t('Generate transcript?');
    }

    return $this->t('Generate transcript for %title?', [
      '%title' => $this->media->label(),
    ]);
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function getDescription(): TranslatableMarkup {
    return $this->t('This runs Whisper transcription and stores the generated subtitle file.');
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function getCancelUrl(): Url {
    return $this->buildReturnUrl();
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function getConfirmText(): TranslatableMarkup {
    return $this->t('Generate transcript');
  }

  /**
   * {@inheritdoc}
   *
   * @return array<string, mixed>
   *   Form render array.
   */
  #[\Override]
  public function buildForm(array $form, FormStateInterface $form_state, ?MediaInterface $media = NULL): array {
    if (!$media instanceof MediaInterface) {
      throw new NotFoundHttpException('Media not found.');
    }

    $this->media = $media;
    $this->fieldName = (string) ($this->getRequest()->query->get('field_name', '') ?? '');

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

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    if (!$this->media instanceof MediaInterface) {
      $form_state->setRedirect('<front>');
      return;
    }

    $returnUrl = $this->buildReturnUrl();

    $contexts = $this->fieldName !== ''
      ? [$this->formatterSettingsResolver->getContextForField($this->media, $this->fieldName)]
      : $this->formatterSettingsResolver->getContextsForEntity($this->media);
    $context = $contexts[0] ?? NULL;
    if ($context === NULL) {
      $this->messenger()->addError($this->t('No video formatter configured for this media type.'));
      $form_state->setRedirectUrl($returnUrl);
      return;
    }

    $sourcePath = $this->sourceFileResolver->getSourcePath($this->media, $context->fieldName);
    if ($sourcePath === NULL) {
      $this->messenger()->addError($this->t('Source file not found for this media item.'));
      $form_state->setRedirectUrl($returnUrl);
      return;
    }

    if (!$this->processorManager->hasDefinition('whisper')) {
      $this->messenger()->addError($this->t('Whisper processor plugin is not available.'));
      $form_state->setRedirectUrl($returnUrl);
      return;
    }

    $processor = $this->processorManager->createInstance('whisper');
    if (!$processor instanceof FFmpegProcessorInterface) {
      $this->messenger()->addError($this->t('Whisper processor plugin returned an invalid instance.'));
      $form_state->setRedirectUrl($returnUrl);
      return;
    }

    if (!$processor->isAvailable()) {
      $reason = $processor->getUnavailableReason() ?? 'Unknown reason.';
      $this->messenger()->addError($this->t('Whisper is not available: @reason', [
        '@reason' => $reason,
      ]));
      $form_state->setRedirectUrl($returnUrl);
      return;
    }

    try {
      $options = $processor->getDefaultOptions();
      $outputPath = $processor->generateOutputPath($sourcePath, $options);
      $outputUri = $this->conversionFileManager->getStorageUri($outputPath);
      $outputDir = dirname($outputUri);
      $this->fileSystem->prepareDirectory($outputDir, FileSystemInterface::CREATE_DIRECTORY);
      $localOutputPath = $this->conversionFileManager->getLocalPath($outputUri);

      $success = $processor->process($sourcePath, $localOutputPath, $options);
      if ($success && file_exists($localOutputPath)) {
        $mimeType = $this->resolveTranscriptMimeType($localOutputPath);
        $file = $this->conversionFileManager->createFileEntity($outputUri, $mimeType);
        $this->outputStorage->setOutput((int) $this->media->id(), $context->fieldName, OutputType::Transcript->value, $file);

        $this->messenger()->addStatus($this->t('Transcript generated successfully for %title.', [
          '%title' => $this->media->label(),
        ]));
      }
      else {
        $this->messenger()->addError($this->t('Whisper transcription failed. Check the logs for details.'));
      }
    }
    catch (\Throwable $e) {
      $this->messenger()->addError($this->t('Whisper transcription failed: @error', [
        '@error' => $e->getMessage(),
      ]));
    }

    $form_state->setRedirectUrl($returnUrl);
  }

  /**
   * Resolves the transcript MIME type from the output file extension.
   *
   * @param string $path
   *   Local output path.
   *
   * @return string
   *   MIME type.
   */
  private function resolveTranscriptMimeType(string $path): string {
    $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));

    return match ($extension) {
      'vtt' => 'text/vtt',
      'srt' => 'application/x-subrip',
      default => 'text/plain',
    };
  }

  /**
   * Builds the status URL for redirects.
   *
   * @return \Drupal\Core\Url
   *   Status page URL when UI module is enabled, else media canonical URL.
   */
  private function buildReturnUrl(): Url {
    if (!$this->media instanceof MediaInterface) {
      return Url::fromRoute('<front>');
    }

    if ($this->moduleHandler->moduleExists('ffmpeg_media_ui')) {
      $routeOptions = $this->fieldName !== '' ? ['query' => ['field_name' => $this->fieldName]] : [];
      return Url::fromRoute('ffmpeg_media_ui.status', ['media' => $this->media->id()], $routeOptions);
    }

    return Url::fromRoute('entity.media.canonical', ['media' => $this->media->id()]);
  }

}
