<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Commands;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
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 Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;

/**
 * Drush commands for FFmpeg Media video transcription.
 */
final class TranscribeCommands extends DrushCommands {

  use CommandHelperTrait;

  /**
   * Constructs a TranscribeCommands instance.
   *
   * @param \Drupal\ffmpeg_media\Service\FormatterSettingsResolverInterface $formatterSettingsResolver
   *   Formatter settings resolver service.
   * @param \Drupal\ffmpeg_media\Service\OutputStorageServiceInterface $outputStorage
   *   Output storage service.
   * @param \Drupal\ffmpeg_media\Service\SourceFileResolverInterface $sourceFileResolver
   *   Source file resolver service.
   * @param \Drupal\ffmpeg_media\FFmpegProcessorPluginManager $processorManager
   *   Processor plugin manager.
   * @param \Drupal\ffmpeg_media\Service\ConversionFileManagerInterface $conversionFileManager
   *   Conversion file manager.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   Entity type manager.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   File system service.
   */
  public function __construct(
    protected readonly FormatterSettingsResolverInterface $formatterSettingsResolver,
    protected readonly OutputStorageServiceInterface $outputStorage,
    protected readonly SourceFileResolverInterface $sourceFileResolver,
    protected readonly FFmpegProcessorPluginManager $processorManager,
    protected readonly ConversionFileManagerInterface $conversionFileManager,
    protected readonly EntityTypeManagerInterface $entityTypeManager,
    protected readonly FileSystemInterface $fileSystem,
  ) {
    parent::__construct();
  }

  /**
   * Generate a transcript for a video using Whisper AI.
   *
   * Runs the Whisper processor on the source file without re-converting
   * the video. Requires the ffmpeg_media_whisper module and Whisper
   * processor to be enabled.
   *
   * @param int $media_id
   *   The media entity ID to transcribe.
   * @param array $options
   *   Command options.
   *
   * @phpstan-param array{force: bool} $options
   */
  #[CLI\Command(name: 'ffmpeg:transcribe', aliases: ['fft'])]
  #[CLI\Argument(name: 'media_id', description: 'The media entity ID to transcribe.')]
  #[CLI\Option(name: 'force', description: 'Overwrite existing transcript.')]
  #[CLI\Usage(name: 'ffmpeg:transcribe 123', description: 'Generate transcript for media ID 123.')]
  #[CLI\Usage(name: 'ffmpeg:transcribe 123 --force', description: 'Regenerate transcript even if one exists.')]
  public function transcribe(int $media_id, array $options = ['force' => FALSE]): void {
    $force = (bool) $options['force'];

    $media = $this->loadMedia($media_id, $this->entityTypeManager, $this->formatterSettingsResolver);
    if ($media === NULL) {
      return;
    }

    $contexts = $this->formatterSettingsResolver->getContextsForEntity($media);
    $context = $contexts[0] ?? NULL;
    if ($context === NULL) {
      $this->logger()?->error('No FFmpeg formatter configuration found for this media type.');
      return;
    }

    // Check for existing transcript.
    if (!$force) {
      $existingFile = $this->outputStorage->getOutput((int) $media->id(), $context->fieldName, OutputType::Transcript->value);
      if ($existingFile !== NULL) {
        $this->logger()?->warning('Media {id} already has a transcript. Use --force to regenerate.', [
          'id' => $media_id,
        ]);
        return;
      }
    }

    $sourcePath = $this->sourceFileResolver->getSourcePath($media, $context->fieldName);
    if ($sourcePath === NULL) {
      $this->logger()?->error('Source file not found for media {id}.', ['id' => $media_id]);
      return;
    }

    if (!$this->processorManager->hasDefinition('whisper')) {
      $this->logger()?->error('Whisper processor plugin is not available. Install the ffmpeg_media_whisper module.');
      return;
    }

    $processor = $this->processorManager->createInstance('whisper');
    if (!$processor instanceof FFmpegProcessorInterface) {
      $this->logger()?->error('Whisper processor plugin returned invalid instance.');
      return;
    }

    if (!$processor->isAvailable()) {
      $reason = $processor->getUnavailableReason() ?? 'Unknown reason.';
      $this->logger()?->error('Whisper is not available: {reason}', ['reason' => $reason]);
      return;
    }

    $this->logger()?->notice('Running Whisper transcription for media {id}...', ['id' => $media_id]);

    try {
      $processorOptions = $processor->getDefaultOptions();
      $outputPath = $processor->generateOutputPath($sourcePath, $processorOptions);
      $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, $processorOptions);

      if ($success && file_exists($localOutputPath)) {
        // Store transcript output via OutputStorage.
        $mimeType = $this->resolveTranscriptMimeType($localOutputPath);
        $file = $this->conversionFileManager->createFileEntity($outputUri, $mimeType);
        $this->outputStorage->setOutput((int) $media->id(), $context->fieldName, OutputType::Transcript->value, $file);

        $this->logger()?->success('Transcript generated for media {id}: {path}', [
          'id' => $media_id,
          'path' => $outputUri,
        ]);
      }
      else {
        $this->logger()?->error('Whisper transcription did not produce output. Check FFmpeg logs for details.');
      }
    }
    catch (\Throwable $e) {
      $this->logger()?->error('Transcription failed for media {id}: {error}', [
        'id' => $media_id,
        'error' => $e->getMessage(),
      ]);
    }
  }

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

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

}
