<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media_ui\Controller;

use Drupal\Component\Utility\Html;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use Drupal\ffmpeg_media\Service\QueueManagerInterface;
use Drupal\ffmpeg_media_ui\ValueObject\DashboardDependencies;
use Drupal\media\MediaInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

/**
 * Controller for the FFmpeg Media UI dashboard.
 */
final class DashboardController extends ControllerBase {

  /**
   * Constructs a DashboardController object.
   *
   * @param \Drupal\ffmpeg_media_ui\ValueObject\DashboardDependencies $deps
   *   Bundled dashboard dependencies.
   */
  public function __construct(
    #[Autowire(service: DashboardDependencies::class)]
    protected readonly DashboardDependencies $deps,
  ) {}

  /**
   * Renders the main dashboard.
   *
   * @return array<string, mixed>
   *   Render array.
   */
  public function dashboard(): array {
    $uiConfig = $this->config('ffmpeg_media_ui.settings');
    $refreshSeconds = max(0, (int) ($uiConfig->get('auto_refresh_interval') ?? 5));
    $refreshInterval = $refreshSeconds * 1000;
    $queueListLimit = max(25, min(1000, (int) ($uiConfig->get('queue_editor_list_limit') ?? 250)));

    $request = $this->deps->requestStack->getCurrentRequest();
    $statusFilter = $request?->query->get('status', '') ?? '';

    $mediaData = $this->deps->mediaListData->getAllVideoMedia($statusFilter);
    $mediaTableItems = $this->deps->mediaListData->prepareMediaTableItems($mediaData['items']);

    $hasMappings = $this->deps->formatterSettingsResolver->hasAnyConfiguration();
    $configUrl = Url::fromRoute('ffmpeg_media.settings')->toString();
    $hasQueueEditorPermission = $this->currentUser()->hasPermission('manage ffmpeg queue');
    $hasOpsPermission = $this->currentUser()->hasPermission('view ffmpeg operations');
    $allowedBatchActions = $this->getAllowedBatchActions();

    $libraries = [
      'ffmpeg_media_ui/dashboard',
      'ffmpeg_media_ui/table-sort',
    ];
    if ($allowedBatchActions !== []) {
      $libraries[] = 'ffmpeg_media_ui/batch-operations';
    }
    if ($hasOpsPermission && $refreshInterval > 0) {
      $libraries[] = 'ffmpeg_media_ui/dashboard-refresh';
    }

    if ($hasQueueEditorPermission) {
      $libraries[] = 'ffmpeg_media_ui/queue-editor';
    }

    $drupalSettings = [];
    if ($allowedBatchActions !== []) {
      $drupalSettings['ffmpegMediaBatch'] = [
        'batchUrl' => Url::fromRoute('ffmpeg_media_ui.ajax.batch')->toString(),
        'allowedActions' => $allowedBatchActions,
      ];
    }
    if ($hasOpsPermission && $refreshInterval > 0) {
      $drupalSettings['ffmpegMediaUi'] = [
        'dashboardAjaxUrl' => Url::fromRoute('ffmpeg_media_ui.dashboard.ajax')->toString(),
        'refreshInterval' => $refreshInterval,
      ];
    }

    if ($hasQueueEditorPermission) {
      $drupalSettings['ffmpegMediaQueue'] = [
        'listUrl' => Url::fromRoute('ffmpeg_media_ui.ajax.queue.list')->toString(),
        'listLimit' => $queueListLimit,
        'reorderUrl' => Url::fromRoute('ffmpeg_media_ui.ajax.queue.reorder')->toString(),
        'actionUrl' => Url::fromRoute('ffmpeg_media_ui.ajax.queue.action')->toString(),
      ];
    }

    // Core media list — available to all dashboard users.
    $build = [
      '#theme' => 'ffmpeg_media_ui_dashboard',
      '#show_operations' => $hasOpsPermission,
      '#status_filter' => $this->deps->dashboardBuilder->buildStatusFilter($statusFilter),
      '#media_table' => $this->deps->dashboardBuilder->buildMediaTable($mediaTableItems),
      '#pager' => ['#type' => 'pager'],
      '#setup_alert' => $this->deps->dashboardBuilder->buildSetupAlert($hasMappings, $configUrl),
      '#show_queue_editor' => $hasQueueEditorPermission,
      '#quick_actions' => $this->deps->mediaListData->buildQuickActions(),
      '#live_refresh_enabled' => $hasOpsPermission && $refreshInterval > 0,
      '#attached' => [
        'library' => $libraries,
        'drupalSettings' => $drupalSettings,
      ],
      '#cache' => [
        'max-age' => 10,
        'contexts' => ['user', 'user.permissions', 'url.query_args'],
        'tags' => [
          'ffmpeg_media:dashboard',
          'ffmpeg_media:stats',
          'config:ffmpeg_media.settings',
          'config:ffmpeg_media_ui.settings',
        ],
      ],
    ];

    // Operations data — only for users with 'view ffmpeg operations'.
    if ($hasOpsPermission) {
      $conversionStats = $this->deps->statsService->getConversionStats();
      $dailyStats = $this->deps->statsService->get24HourStats();

      $ffmpegStatus = [
        'available' => $this->deps->ffmpegExecutor->isAvailable(),
        'version' => $this->deps->ffmpegExecutor->getVersion(),
        'path' => $this->currentUser()->hasPermission('administer ffmpeg media')
          ? $this->deps->ffmpegExecutor->getFfmpegPath()
          : '',
        'hardware' => $this->deps->hardwareDetector->getCapabilities(),
      ];

      $activeConversions = $this->deps->activeConversionsViewModelBuilder->build();
      $recentConversions = $this->deps->statsService->getRecentConversions(20);
      $recentFailures = $this->deps->statsService->getRecentFailures(10);

      $normalCount = $this->deps->queueFactory->get(QueueManagerInterface::QUEUE_NAME)->numberOfItems();
      $largeCount = $this->deps->queueFactory->get(QueueManagerInterface::QUEUE_LARGE_NAME)->numberOfItems();
      $queuedMediaCount = $this->deps->queueManager->getQueuedMediaCount();

      $queueStatus = [
        'queued_media' => $queuedMediaCount,
        'normal_count' => $normalCount,
        'large_count' => $largeCount,
        'total_count' => $normalCount + $largeCount,
      ];

      $build['#stats'] = $conversionStats + [
        'completed_24h' => $dailyStats['completed'] ?? 0,
        'failed_24h' => $dailyStats['failed'] ?? 0,
      ];
      $build['#queue_status'] = $queueStatus;
      $build['#ffmpeg_status'] = $ffmpegStatus;
      $build['#stats_cards'] = $this->deps->dashboardBuilder->buildStatsCards($conversionStats);
      $build['#stats_24h'] = $this->deps->dashboardBuilder->buildStats24h($dailyStats);
      $build['#enhanced_queue_status'] = $this->deps->dashboardBuilder->buildEnhancedQueueStatus($queueStatus);
      $build['#system_status'] = $this->deps->dashboardBuilder->buildSystemStatus($ffmpegStatus);
      $build['#active_conversions'] = $this->deps->dashboardBuilder->buildActiveConversions($activeConversions);
      $build['#recent_conversions'] = $this->deps->dashboardBuilder->buildRecentConversions($recentConversions);
      $build['#recent_failures'] = $this->deps->dashboardBuilder->buildRecentFailures($recentFailures);
    }

    return $build;
  }

  /**
   * Returns the batch actions available to the current user.
   *
   * @return array<int, string>
   *   Allowed action machine names.
   */
  #[\NoDiscard]
  private function getAllowedBatchActions(): array {
    $actions = [];

    if ($this->currentUserCanTranscode()) {
      $actions[] = 'transcode';
    }

    if ($this->currentUser()->hasPermission('manage media outputs')) {
      $actions[] = 'requeue';
      $actions[] = 'delete-outputs';
    }

    return $actions;
  }

  /**
   * Returns whether the current user can use transcode UI tools.
   */
  #[\NoDiscard]
  private function currentUserCanTranscode(): bool {
    $account = $this->currentUser();

    return $account->hasPermission('transcode media')
      || $account->hasPermission('convert media')
      || $account->hasPermission('reconvert media');
  }

  /**
   * AJAX endpoint to refresh dashboard data.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with updated dashboard data.
   */
  public function ajaxRefresh(): JsonResponse {
    $normalCount = $this->deps->queueFactory->get(QueueManagerInterface::QUEUE_NAME)->numberOfItems();
    $largeCount = $this->deps->queueFactory->get(QueueManagerInterface::QUEUE_LARGE_NAME)->numberOfItems();
    $queuedMediaCount = $this->deps->queueManager->getQueuedMediaCount();

    return new JsonResponse([
      'queue_status' => [
        'queued_media' => $queuedMediaCount,
        'normal_count' => $normalCount,
        'large_count' => $largeCount,
        'total_count' => $normalCount + $largeCount,
      ],
      'active_conversions' => $this->deps->activeConversionsViewModelBuilder->build(),
      'stats' => $this->deps->statsService->getConversionStats(),
      'stats_24h' => $this->deps->statsService->get24HourStats(),
    ], 200, [
      'Content-Type' => 'application/json',
      'Cache-Control' => 'no-cache, no-store, must-revalidate',
    ]);
  }

  /**
   * Renders the status page for a media item.
   *
   * @param \Drupal\media\MediaInterface $media
   *   The media entity.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request (reads optional ?field_name= query parameter).
   *
   * @return array<string, mixed>
   *   Render array.
   */
  public function status(MediaInterface $media, Request $request): array {
    if (!$media->access('view')) {
      throw new AccessDeniedHttpException('Access denied for this media item.');
    }

    $fieldName = (string) $request->query->get('field_name', '');
    $status = $this->deps->mediaOperations->getStatus($media, $fieldName);
    $outputs = $this->deps->mediaOperations->getOutputs($media, $fieldName);
    $uiConfig = $this->config('ffmpeg_media_ui.settings');
    $refreshSeconds = max(0, (int) ($uiConfig->get('auto_refresh_interval') ?? 5));
    $attachedLibraries = ['ffmpeg_media_ui/dashboard'];
    $progressRouteOptions = $fieldName !== '' ? ['query' => ['field_name' => $fieldName]] : [];
    $uiSettings = [
      'mediaId' => $media->id(),
      'progressUrl' => Url::fromRoute('ffmpeg_media_ui.ajax.progress', ['media' => $media->id()], $progressRouteOptions)->toString(),
      'showNotifications' => (bool) ($uiConfig->get('show_progress_notifications') ?? TRUE),
    ];

    if ($refreshSeconds > 0) {
      $attachedLibraries[] = 'ffmpeg_media_ui/progress-tracker';
      $uiSettings['refreshInterval'] = $refreshSeconds * 1000;
    }

    $build = [
      '#type' => 'container',
      '#attributes' => ['class' => ['ffmpeg-media-status-page']],
      '#attached' => [
        'library' => $attachedLibraries,
        'drupalSettings' => [
          'ffmpegMediaUi' => $uiSettings,
        ],
      ],
      '#cache' => [
        'max-age' => 10,
        'contexts' => ['user.permissions', 'url.path', 'url.query_args:field_name'],
        'tags' => [
          'media:' . $media->id(),
          'config:ffmpeg_media_ui.settings',
        ],
      ],
    ];

    // Map status to badge CSS modifier.
    $statusBadgeClass = 'ffmpeg-status-badge--' . $status['status'];

    // Media info header with status badge.
    $build['header'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['ffmpeg-status-header']],
      'title' => [
        '#type' => 'html_tag',
        '#tag' => 'h2',
        '#value' => Html::escape((string) $media->label()),
      ],
      'badge' => [
        '#type' => 'html_tag',
        '#tag' => 'span',
        '#value' => match ($status['status']) {
          'none' => $this->t('Not Started'),
          'pending' => $this->t('Pending'),
          'converting', 'processing' => $this->t('Processing'),
          'completed' => $this->t('Completed'),
          'failed' => $this->t('Failed'),
          default => ucfirst($status['status']),
        },
        '#attributes' => [
          'class' => ['ffmpeg-status-badge', $statusBadgeClass],
        ],
      ],
    ];

    // Two-column layout: content (left) + sidebar (right).
    $build['main'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['ffmpeg-status-main']],
    ];

    $build['main']['content'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['ffmpeg-status-content']],
    ];

    $build['main']['sidebar'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => ['ffmpeg-status-sidebar'],
        'role' => 'complementary',
      ],
    ];

    // Source video preview (configurable).
    if ((bool) ($uiConfig->get('video_preview_enabled') ?? TRUE)) {
      $videoPreview = $this->deps->statusPageBuilder->buildVideoPreview($media, $fieldName);
      if ($videoPreview !== NULL) {
        $build['main']['content']['video_preview'] = $videoPreview;
        $build['#attached']['library'][] = 'ffmpeg_media_ui/video-preview';
      }
    }

    // Progress section.
    $build['main']['content']['progress_section'] = $this->deps->statusPageBuilder->buildProgressSection($status);

    // Error feedback with categorization.
    $errorDetails = $this->deps->statusPageBuilder->buildErrorDetails($status);
    if ($errorDetails !== NULL) {
      $build['main']['content']['progress_section']['error_details'] = $errorDetails;
    }

    // Outputs section.
    $outputsSection = $this->deps->statusPageBuilder->buildOutputsSection($outputs);
    if ($outputsSection !== NULL) {
      $build['main']['content']['outputs_section'] = $outputsSection;
    }

    // Transcript section.
    $transcriptSection = $this->deps->statusPageBuilder->buildTranscriptSection($media, $fieldName);
    if ($transcriptSection !== NULL) {
      $build['main']['content']['transcript_section'] = $transcriptSection;
    }

    // Video info section — in sidebar (configurable via UI settings).
    if ((bool) ($uiConfig->get('show_technical_details') ?? TRUE)) {
      $videoInfo = $this->deps->mediaOperations->getVideoInfo($media, $fieldName);
      if ($videoInfo !== NULL) {
        $build['main']['sidebar']['info_section'] = [
          '#type' => 'container',
          '#attributes' => [
            'class' => [
              'ffmpeg-media-dashboard__section',
              'ffmpeg-status-section',
              'ffmpeg-status-section--info',
            ],
          ],
          'heading' => [
            '#type' => 'html_tag',
            '#tag' => 'h3',
            '#value' => $this->t('Video Information'),
          ],
          'card' => $this->deps->statusPageBuilder->buildVideoInfoCard($videoInfo),
        ];
      }
    }

    // Actions section — in sidebar.
    $build['main']['sidebar']['actions_section'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => [
          'ffmpeg-media-dashboard__section',
          'ffmpeg-status-section',
          'ffmpeg-status-section--actions',
        ],
      ],
      'heading' => [
        '#type' => 'html_tag',
        '#tag' => 'h3',
        '#value' => $this->t('Actions'),
      ],
      'buttons' => $this->deps->statusPageBuilder->buildActionButtons($media, $status, $fieldName),
    ];

    return $build;
  }

  /**
   * Title callback for transcode page.
   *
   * @param \Drupal\media\MediaInterface $media
   *   The media entity.
   *
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
   *   Page title.
   */
  public function transcodeTitle(MediaInterface $media): TranslatableMarkup {
    if (!$media->access('update')) {
      return $this->t('Transcode video');
    }

    return $this->t('Transcode: @title', ['@title' => $media->label()]);
  }

}
