<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use Drupal\ffmpeg_media\Service\ProfileManagerInterface;
use Drupal\ffmpeg_media\ValueObject\ConversionProfile;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * Controller for listing conversion profiles.
 */
final class ProfileListController extends ControllerBase {

  /**
   * Constructs a ProfileListController object.
   *
   * @param \Drupal\ffmpeg_media\Service\ProfileManagerInterface $profileManager
   *   The profile manager service.
   */
  public function __construct(
    #[Autowire(service: ProfileManagerInterface::class)]
    protected readonly ProfileManagerInterface $profileManager,
  ) {}

  /**
   * Displays the list of conversion profiles.
   *
   * @return array<string, mixed>
   *   Render array for the profiles list page.
   */
  public function list(): array {
    $profiles = $this->profileManager->getAllProfiles();

    $profileData = [];
    $defaultProfileId = $this->profileManager->getDefaultProfileId();
    foreach ($profiles as $id => $profile) {
      $isDefault = $id === $defaultProfileId;

      $profileData[] = [
        'id' => $id,
        'label' => $profile->label,
        'description' => $profile->description,
        'output_summary' => $this->buildOutputSummary($profile->outputs),
        'output_count' => $this->countEnabledOutputs($profile->outputs),
        'is_default' => $isDefault,
        'generate_thumbnail' => $profile->generateThumbnail,
        'edit_url' => Url::fromRoute('ffmpeg_media.profile_edit', ['profile_id' => $id]),
        'clone_url' => Url::fromRoute('ffmpeg_media.profile_clone', ['profile_id' => $id]),
        'delete_url' => $isDefault ? NULL : Url::fromRoute('ffmpeg_media.profile_delete', ['profile_id' => $id]),
        'set_default_url' => $isDefault ? NULL : Url::fromRoute('ffmpeg_media.profile_set_default', ['profile_id' => $id]),
        'category' => $this->categorize($profile),
        'primary_codec' => $this->getPrimaryCodec($profile),
        'is_gpu' => $profile->isHardwareRequired(),
        'is_streaming' => $this->isStreaming($profile),
        'output_pills' => $this->buildOutputPills($profile->outputs),
      ];
    }

    return [
      '#theme' => 'ffmpeg_media_profiles',
      '#profiles' => $profileData,
      '#profile_groups' => $this->groupByCategory($profileData),
      '#attached' => [
        'library' => ['ffmpeg_media/profiles'],
      ],
      '#cache' => [
        'tags' => [ProfileManagerInterface::CACHE_TAG, 'config:ffmpeg_media.settings'],
        'contexts' => ['user.permissions'],
        'max-age' => -1,
      ],
    ];
  }

  /**
   * Determines the category of a profile.
   *
   * @param \Drupal\ffmpeg_media\ValueObject\ConversionProfile $profile
   *   The profile.
   *
   * @return string
   *   Category key: 'gpu', 'streaming', or 'standard'.
   */
  protected function categorize(ConversionProfile $profile): string {
    if ($profile->isHardwareRequired()) {
      return 'gpu';
    }
    if ($this->isStreaming($profile)) {
      return 'streaming';
    }
    return 'standard';
  }

  /**
   * Checks whether a profile uses streaming outputs.
   *
   * @param \Drupal\ffmpeg_media\ValueObject\ConversionProfile $profile
   *   The profile.
   *
   * @return bool
   *   TRUE if any enabled output uses HLS or DASH.
   */
  protected function isStreaming(ConversionProfile $profile): bool {
    foreach ($profile->getEnabledOutputs() as $output) {
      if (in_array($output->container, ['hls', 'dash'], TRUE)) {
        return TRUE;
      }
    }
    return FALSE;
  }

  /**
   * Gets the primary codec used in a profile.
   *
   * @param \Drupal\ffmpeg_media\ValueObject\ConversionProfile $profile
   *   The profile.
   *
   * @return string
   *   Uppercase codec name (e.g. 'AV1', 'VP9').
   */
  protected function getPrimaryCodec(ConversionProfile $profile): string {
    $enabled = $profile->getEnabledOutputs();
    if ($enabled === []) {
      return '';
    }
    return strtoupper($enabled[0]->codec);
  }

  /**
   * Builds output pill data for display.
   *
   * @param array<int, \Drupal\ffmpeg_media\ValueObject\ProfileOutput> $outputs
   *   Output configurations.
   *
   * @return array<int, array{container: string, codec: string}>
   *   Array of pill data with uppercase values.
   */
  protected function buildOutputPills(array $outputs): array {
    $pills = [];
    foreach ($outputs as $output) {
      if ($output->enabled) {
        $pills[] = [
          'container' => strtoupper($output->container),
          'codec' => strtoupper($output->codec),
        ];
      }
    }
    return $pills;
  }

  /**
   * Groups profile data by category.
   *
   * @param array<int, array<string, mixed>> $profileData
   *   Flat list of profile data arrays.
   *
   * @return array<int, array{key: string, label: string, profiles: array<int, array<string, mixed>>}>
   *   Grouped profile data with category metadata.
   */
  protected function groupByCategory(array $profileData): array {
    $categories = [
      'standard' => $this->createCategoryGroup('standard', (string) $this->t('Standard Profiles')),
      'streaming' => $this->createCategoryGroup('streaming', (string) $this->t('Adaptive Streaming')),
      'gpu' => $this->createCategoryGroup('gpu', (string) $this->t('GPU-Accelerated')),
    ];

    foreach ($profileData as $profile) {
      $categoryKey = match ($profile['category'] ?? 'standard') {
        'gpu' => 'gpu',
        'streaming' => 'streaming',
        default => 'standard',
      };
      $categories[$categoryKey]['profiles'][] = $profile;
    }

    $groupedProfiles = [];
    if ($categories['standard']['profiles'] !== []) {
      $groupedProfiles[] = $categories['standard'];
    }
    if ($categories['streaming']['profiles'] !== []) {
      $groupedProfiles[] = $categories['streaming'];
    }
    if ($categories['gpu']['profiles'] !== []) {
      $groupedProfiles[] = $categories['gpu'];
    }

    return $groupedProfiles;
  }

  /**
   * Creates a typed profile category group.
   *
   * @param string $key
   *   The category machine name.
   * @param string $label
   *   The category label.
   *
   * @return array{key: string, label: string, profiles: array<int, array<string, mixed>>}
   *   Initialized category data.
   */
  protected function createCategoryGroup(string $key, string $label): array {
    return [
      'key' => $key,
      'label' => $label,
      'profiles' => [],
    ];
  }

  /**
   * Builds a summary string for output configurations.
   *
   * @param array<int, \Drupal\ffmpeg_media\ValueObject\ProfileOutput> $outputs
   *   Output configurations.
   *
   * @return string
   *   Human-readable output summary.
   */
  protected function buildOutputSummary(array $outputs): string {
    $enabled = [];
    foreach ($outputs as $output) {
      if ($output->enabled) {
        $container = strtoupper($output->container);
        $codec = strtoupper($output->codec);
        $enabled[] = "{$container} ({$codec})";
      }
    }

    return $enabled !== [] ? implode(', ', $enabled) : (string) $this->t('None');
  }

  /**
   * Counts enabled outputs.
   *
   * @param array<int, \Drupal\ffmpeg_media\ValueObject\ProfileOutput> $outputs
   *   Output configurations.
   *
   * @return int
   *   Number of enabled outputs.
   */
  protected function countEnabledOutputs(array $outputs): int {
    $count = 0;
    foreach ($outputs as $output) {
      if ($output->enabled) {
        $count++;
      }
    }

    return $count;
  }

}
