<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_ui\Service;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Url;
use Drupal\ffmpeg_media\Enum\ConversionStatus;
use Drupal\ffmpeg_media\Service\ProgressTrackerInterface;
use Drupal\ffmpeg_media\Utility\QueueItemKey;
use Drupal\media\MediaInterface;

/**
 * Builds active conversion view models for dashboard rendering.
 *
 * @internal
 */
final class ActiveConversionsViewModelBuilder implements ActiveConversionsViewModelBuilderInterface {

  use StringTranslationTrait;

  /**
   * Constructs an ActiveConversionsViewModelBuilder object.
   */
  public function __construct(
    protected readonly ProgressTrackerInterface $progressTracker,
    protected readonly EntityTypeManagerInterface $entityTypeManager,
    protected readonly DateFormatterInterface $dateFormatter,
    protected readonly TimeInterface $time,
    TranslationInterface $translation,
  ) {
    $this->stringTranslation = $translation;
  }

  /**
   * {@inheritdoc}
   */
  #[\Override]
  public function build(): array {
    $activeConversions = $this->progressTracker->getActiveConversions();
    if ($activeConversions === []) {
      return [];
    }

    $mediaIds = array_map(
      static fn($progress) => $progress->mediaId,
      $activeConversions,
    );

    /** @var array<int, \Drupal\media\MediaInterface> $mediaEntities */
    $mediaEntities = [];
    if ($mediaIds !== []) {
      try {
        $mediaEntities = $this->entityTypeManager
          ->getStorage('media')
          ->loadMultiple($mediaIds);
      }
      catch (\Throwable) {
        // Continue without labels and edit links when entity loading fails.
      }
    }

    $rows = [];
    foreach ($activeConversions as $progress) {
      $media = $mediaEntities[$progress->mediaId] ?? NULL;

      // Filter out deleted or inaccessible media entities.
      if ($media === NULL || !$media->access('view')) {
        continue;
      }

      $mediaName = $media !== NULL ? $media->getName() : (string) $this->t('Unknown');
      $mediaUrl = $this->buildMediaStatusUrl($media, $progress->fieldName);
      $fieldLabel = $progress->fieldName !== '' && $media !== NULL && $media->hasField($progress->fieldName)
        ? (string) $media->get($progress->fieldName)->getFieldDefinition()->getLabel()
        : $progress->fieldName;

      $duration = '';
      if ($progress->startedAt !== NULL && $progress->status !== ConversionStatus::Pending) {
        $elapsed = $this->time->getRequestTime() - $progress->startedAt;
        $duration = $this->dateFormatter->formatInterval($elapsed);
      }

      $rows[] = [
        'item_id' => QueueItemKey::fromParts($progress->mediaId, $progress->fieldName),
        'media_id' => $progress->mediaId,
        'field_name' => $progress->fieldName,
        'field_label' => $fieldLabel,
        'media_name' => $mediaName,
        'media_url' => $mediaUrl,
        'status' => $progress->status->value,
        'status_label' => $this->getStatusLabel($progress->status->value),
        'percent' => $progress->percent,
        'current_step' => $progress->currentStep,
        'current_output' => $progress->currentOutput,
        'duration' => $duration,
        'estimated_remaining' => $this->formatEstimatedTime($progress->estimatedRemainingTime),
      ];
    }

    return $rows;
  }

  /**
   * Builds a status-page URL for a media entity, if possible.
   */
  private function buildMediaStatusUrl(?MediaInterface $media, string $fieldName = ''): ?string {
    if ($media === NULL) {
      return NULL;
    }

    try {
      return Url::fromRoute('ffmpeg_media_ui.status', ['media' => $media->id()], $fieldName !== '' ? [
        'query' => ['field_name' => $fieldName],
      ] : [])->toString();
    }
    catch (\Throwable) {
      return NULL;
    }
  }

  /**
   * Gets a human-readable status label.
   */
  private function getStatusLabel(?string $status): string {
    if ($status === NULL) {
      return (string) $this->t('Unknown');
    }

    $labels = [
      ConversionStatus::Pending->value => $this->t('Pending'),
      ConversionStatus::Converting->value => $this->t('Converting'),
      ConversionStatus::Completed->value => $this->t('Completed'),
      ConversionStatus::Failed->value => $this->t('Failed'),
    ];

    return (string) ($labels[$status] ?? $status);
  }

  /**
   * Formats estimated remaining time.
   */
  private function formatEstimatedTime(?int $seconds): string {
    if ($seconds === NULL || $seconds <= 0) {
      return '';
    }

    return $this->dateFormatter->formatInterval($seconds) . ' ' . (string) $this->t('remaining');
  }

}
