<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Service;

use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Humanizes progress step labels and provides ETA calculations.
 *
 * @internal
 */
final class ProgressLabelService implements ProgressLabelServiceInterface {

  use StringTranslationTrait;

  /**
   * Minimum progress percentage before ETA is calculated.
   */
  private const int MIN_PERCENT_FOR_ETA = 5;

  /**
   * Processor human-readable names.
   *
   * @var array<string, string>
   */
  private const array PROCESSOR_NAMES = [
    'thumbnail' => 'Generating thumbnail',
    'video_info' => 'Analyzing video',
    'gif' => 'Creating GIF',
    'whisper' => 'Transcribing audio',
    'hdr' => 'Processing HDR',
    'watermark' => 'Applying watermark',
    'normalize' => 'Normalizing audio',
    'subtitles' => 'Burning subtitles',
  ];

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function humanize(string $step, ?string $currentOutput, int $currentOutputIndex, int $totalOutputs): string {
    $stepIndicator = $totalOutputs > 0
      ? ' (' . $this->t('Step @n of @total', ['@n' => $currentOutputIndex + 1, '@total' => $totalOutputs]) . ')'
      : '';

    // "Starting conversion"
    if ($step === 'Starting conversion') {
      return (string) $this->t('Preparing conversion') . $stepIndicator;
    }

    // "Converting to {format}"
    if (preg_match('/^Converting to (\w+)$/i', $step, $matches)) {
      $format = strtoupper($matches[1]);
      return $format . ' ' . (string) $this->t('format is being created') . $stepIndicator;
    }

    // "Two-pass encoding (pass 1) to {format}"
    if (preg_match('/^Two-pass encoding \(pass 1\) to (\w+)$/i', $step, $matches)) {
      $format = strtoupper($matches[1]);
      return $format . ': ' . (string) $this->t('Analysis pass') . $stepIndicator;
    }

    // "Two-pass encoding (pass 2) to {format}"
    if (preg_match('/^Two-pass encoding \(pass 2\) to (\w+)$/i', $step, $matches)) {
      $format = strtoupper($matches[1]);
      return $format . ': ' . (string) $this->t('Encoding pass') . $stepIndicator;
    }

    // "Completed {format}"
    if (preg_match('/^Completed (\w+)$/i', $step, $matches)) {
      $format = strtoupper($matches[1]);
      return $format . ' ' . (string) $this->t('completed');
    }

    // "Running processor: {name}"
    if (preg_match('/^Running processor: (\w+)$/i', $step, $matches)) {
      $processorId = $matches[1];
      $humanName = self::PROCESSOR_NAMES[$processorId] ?? $processorId;
      return (string) $this->t('Post-processing: @name', ['@name' => $humanName]);
    }

    return $step;
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function getEstimatedCompletion(int $startedAt, int $progressPercent): ?string {
    if ($progressPercent < self::MIN_PERCENT_FOR_ETA || $progressPercent >= 100) {
      return NULL;
    }

    $elapsed = time() - $startedAt;
    if ($elapsed <= 0) {
      return NULL;
    }

    // $progressPercent is guaranteed >= MIN_PERCENT_FOR_ETA (5) by the
    // guard at method entry, so division by zero cannot occur here.
    $etaSeconds = (int) (($elapsed / $progressPercent) * (100 - $progressPercent));

    if ($etaSeconds < 10) {
      return (string) $this->t('Almost done');
    }

    if ($etaSeconds < 60) {
      return (string) $this->t('~@seconds seconds remaining', ['@seconds' => $etaSeconds]);
    }

    $minutes = (int) ceil($etaSeconds / 60);
    if ($minutes === 1) {
      return (string) $this->t('~1 minute remaining');
    }

    return (string) $this->t('~@minutes minutes remaining', ['@minutes' => $minutes]);
  }

}
