<?php

declare(strict_types=1);

namespace Drupal\nodehive_dam\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\File\FileExists;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Image\ImageFactory;
use Drupal\Core\Render\RendererInterface;
use Drupal\file\FileRepositoryInterface;
use Drupal\media\MediaInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Controller for the Digital Asset Management interface.
 */
class DamController extends ControllerBase {

  /**
   * The renderer service.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected RendererInterface $renderer;

  /**
   * The file system service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected FileSystemInterface $fileSystem;

  /**
   * The file repository service.
   *
   * @var \Drupal\file\FileRepositoryInterface
   */
  protected FileRepositoryInterface $fileRepository;

  /**
   * The file URL generator service.
   *
   * @var \Drupal\Core\File\FileUrlGeneratorInterface
   */
  protected FileUrlGeneratorInterface $fileUrlGenerator;

  /**
   * The image factory service.
   *
   * @var \Drupal\Core\Image\ImageFactory
   */
  protected ImageFactory $imageFactory;

  /**
   * The date formatter service.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected DateFormatterInterface $dateFormatter;

  /**
   * The logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected LoggerInterface $logger;

  /**
   * Constructs a DamController object.
   */
  public function __construct(
    RendererInterface $renderer,
    FileSystemInterface $file_system,
    FileRepositoryInterface $file_repository,
    FileUrlGeneratorInterface $file_url_generator,
    ImageFactory $image_factory,
    DateFormatterInterface $date_formatter,
    LoggerInterface $logger,
  ) {
    $this->renderer = $renderer;
    $this->fileSystem = $file_system;
    $this->fileRepository = $file_repository;
    $this->fileUrlGenerator = $file_url_generator;
    $this->imageFactory = $image_factory;
    $this->dateFormatter = $date_formatter;
    $this->logger = $logger;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get('renderer'),
      $container->get('file_system'),
      $container->get('file.repository'),
      $container->get('file_url_generator'),
      $container->get('image.factory'),
      $container->get('date.formatter'),
      $container->get('logger.factory')->get('nodehive_dam'),
    );
  }

  /**
   * Redirects to the media add page with a destination back to the DAM.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   A redirect response.
   */
  public function addMedia(): RedirectResponse {
    $url = Url::fromRoute('entity.media.add_page', [], [
      'query' => ['destination' => '/nodehive/admin/dam'],
    ]);
    return new RedirectResponse($url->toString());
  }

  /**
   * Displays the DAM overview page.
   *
   * @return array
   *   A render array for the DAM page.
   */
  public function overview(): array {
    $media_types = $this->entityTypeManager()->getStorage('media_type')->loadMultiple();
    $type_options = [];
    foreach ($media_types as $type) {
      $type_options[$type->id()] = $type->label();
    }

    // Load folders from taxonomy.
    $folders = [];
    $term_storage = $this->entityTypeManager()->getStorage('taxonomy_term');
    $folder_terms = $term_storage->loadTree('nodehive_dam_folder', 0, NULL, TRUE);
    foreach ($folder_terms as $term) {
      $parents = $term_storage->loadParents($term->id());
      $parent_id = '';
      if (!empty($parents)) {
        $parent = reset($parents);
        $parent_id = $parent->id();
      }
      $folders[] = [
        'id' => $term->id(),
        'name' => $term->getName(),
        'depth' => $term->depth,
        'parent_id' => $parent_id,
      ];
    }

    // Load collections from taxonomy.
    $collections = [];
    $collection_terms = $term_storage->loadTree('nodehive_dam_collection', 0, NULL, TRUE);
    foreach ($collection_terms as $term) {
      $collections[] = [
        'id' => $term->id(),
        'name' => $term->getName(),
      ];
    }

    return [
      '#theme' => 'nodehive_dam_overview',
      '#media_types' => $type_options,
      '#folders' => $folders,
      '#collections' => $collections,
      '#attached' => [
        'library' => [
          'nodehive_dam/htmx',
          'nodehive_dam/dam',
        ],
      ],
    ];
  }

  /**
   * Returns the media grid as HTML partial for HTMX.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   The HTML response.
   */
  public function mediaGrid(Request $request): Response {
    $search = $request->query->get('search', '');
    $type = $request->query->get('type', '');
    $folder = $request->query->get('folder', '');
    $collection = $request->query->get('collection', '');
    $date_from = $request->query->get('date_from', '');
    $date_to = $request->query->get('date_to', '');
    $page = (int) $request->query->get('page', 0);
    $limit = 48;

    $query = $this->entityTypeManager()->getStorage('media')->getQuery()
      ->accessCheck(TRUE)
      ->sort('created', 'DESC')
      ->range($page * $limit, $limit);

    // Filter by folder.
    if ($folder === '_none') {
      // Show items not in any folder.
      $query->notExists('nodehive_dam_folder');
    }
    elseif (!empty($folder)) {
      $query->condition('nodehive_dam_folder', $folder);
    }

    // Filter by collection.
    if (!empty($collection)) {
      $query->condition('nodehive_dam_collections', $collection);
    }

    if (!empty($search)) {
      $query->condition('name', '%' . $search . '%', 'LIKE');
    }

    if (!empty($type)) {
      $query->condition('bundle', $type);
    }

    if (!empty($date_from)) {
      $timestamp = strtotime($date_from);
      if ($timestamp) {
        $query->condition('created', $timestamp, '>=');
      }
    }

    if (!empty($date_to)) {
      $timestamp = strtotime($date_to . ' 23:59:59');
      if ($timestamp) {
        $query->condition('created', $timestamp, '<=');
      }
    }

    $ids = $query->execute();
    $media_items = $this->entityTypeManager()->getStorage('media')->loadMultiple($ids);

    $items = [];
    foreach ($media_items as $media) {
      $items[] = $this->buildMediaItem($media);
    }

    // Check if there are more items.
    $count_query = $this->entityTypeManager()->getStorage('media')->getQuery()
      ->accessCheck(TRUE)
      ->count();

    // Apply same folder filter to count query.
    if ($folder === '_none') {
      $count_query->notExists('nodehive_dam_folder');
    }
    elseif (!empty($folder)) {
      $count_query->condition('nodehive_dam_folder', $folder);
    }

    // Apply same collection filter to count query.
    if (!empty($collection)) {
      $count_query->condition('nodehive_dam_collections', $collection);
    }

    if (!empty($search)) {
      $count_query->condition('name', '%' . $search . '%', 'LIKE');
    }
    if (!empty($type)) {
      $count_query->condition('bundle', $type);
    }

    $total = (int) $count_query->execute();
    $has_more = ($page + 1) * $limit < $total;

    $build = [
      '#theme' => 'nodehive_dam_grid',
      '#items' => $items,
      '#has_more' => $has_more,
      '#next_page' => $page + 1,
      '#current_params' => [
        'search' => $search,
        'type' => $type,
        'folder' => $folder,
        'collection' => $collection,
        'date_from' => $date_from,
        'date_to' => $date_to,
      ],
    ];

    $html = (string) $this->renderer->renderRoot($build);

    return new Response($html, 200, ['Content-Type' => 'text/html']);
  }

  /**
   * Handles file uploads via AJAX.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   JSON response with upload result or HTML partial.
   */
  public function upload(Request $request): Response {
    $uploaded_file = $request->files->get('file');

    if (!$uploaded_file || !$uploaded_file->isValid()) {
      return new JsonResponse(['error' => 'Invalid file upload'], 400);
    }

    $filename = $uploaded_file->getClientOriginalName();
    $mime_type = $uploaded_file->getMimeType();

    $this->logger->notice('Upload: @filename, mime: @mime', [
      '@filename' => $filename,
      '@mime' => $mime_type,
    ]);

    // Determine media type based on mime type.
    $media_type = $this->determineMediaType($mime_type, $filename);
    if (!$media_type) {
      return new JsonResponse(['error' => 'Unsupported file type'], 400);
    }

    // Create file entity.
    $destination = 'public://' . date('Y-m') . '/' . $filename;
    $directory = dirname($destination);
    $this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);

    $file_content = file_get_contents($uploaded_file->getRealPath());
    $file = $this->fileRepository->writeData($file_content, $destination, FileExists::Rename);

    if (!$file) {
      return new JsonResponse(['error' => 'Failed to save file'], 500);
    }

    // Create media entity.
    $media_storage = $this->entityTypeManager()->getStorage('media');
    $media_type_entity = $this->entityTypeManager()->getStorage('media_type')->load($media_type);

    if (!$media_type_entity) {
      return new JsonResponse(['error' => 'Media type not found'], 500);
    }

    $source_field = $media_type_entity->getSource()->getConfiguration()['source_field'] ?? NULL;
    if (!$source_field) {
      // Try to get source field from the source plugin.
      $source_field = $media_type_entity->getSource()->getSourceFieldDefinition($media_type_entity)->getName();
    }

    $media = $media_storage->create([
      'bundle' => $media_type,
      'name' => pathinfo($filename, PATHINFO_FILENAME),
      $source_field => [
        'target_id' => $file->id(),
      ],
    ]);

    // Assign to folder if specified.
    $folder_id = $request->request->get('folder');
    if (!empty($folder_id) && $folder_id !== '_none') {
      $media->set('nodehive_dam_folder', $folder_id);
    }

    $media->save();

    // Return HTML partial for the new item.
    $item = $this->buildMediaItem($media);
    $build = [
      '#theme' => 'nodehive_dam_grid_item',
      '#item' => $item,
    ];

    $html = (string) $this->renderer->renderRoot($build);

    return new Response($html, 200, ['Content-Type' => 'text/html']);
  }

  /**
   * Saves media name and alt text.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   * @param \Drupal\media\MediaInterface $media
   *   The media entity.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function save(Request $request, MediaInterface $media): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);

    if (isset($data['name'])) {
      $media->setName($data['name']);
    }

    // Update alt text if this is an image.
    if (isset($data['alt'])) {
      $source = $media->getSource();
      $source_field = $source->getSourceFieldDefinition($media->bundle->entity)->getName();
      $field_value = $media->get($source_field)->first();
      if ($field_value) {
        $field_value->set('alt', $data['alt']);
      }
    }

    try {
      $media->save();
      return new JsonResponse(['success' => TRUE]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Creates a new folder.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function createFolder(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $name = $data['name'] ?? '';
    $parent_id = $data['parent_id'] ?? '';

    if (empty($name)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'Name is required'], 400);
    }

    try {
      $term_storage = $this->entityTypeManager()->getStorage('taxonomy_term');
      $term_data = [
        'vid' => 'nodehive_dam_folder',
        'name' => $name,
      ];

      // Set parent if provided.
      if (!empty($parent_id)) {
        $term_data['parent'] = $parent_id;
      }

      $term = $term_storage->create($term_data);
      $term->save();

      return new JsonResponse([
        'success' => TRUE,
        'folder' => [
          'id' => $term->id(),
          'name' => $term->getName(),
        ],
      ]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Moves media items to a folder.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function moveToFolder(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $media_ids = $data['media_ids'] ?? [];
    $folder_id = $data['folder_id'] ?? NULL;

    if (empty($media_ids)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'No media items specified'], 400);
    }

    try {
      $media_storage = $this->entityTypeManager()->getStorage('media');
      $media_items = $media_storage->loadMultiple($media_ids);

      foreach ($media_items as $media) {
        if ($folder_id === NULL || $folder_id === '' || $folder_id === '_none') {
          // Remove from folder.
          $media->set('nodehive_dam_folder', NULL);
        }
        else {
          $media->set('nodehive_dam_folder', $folder_id);
        }
        $media->save();
      }

      return new JsonResponse(['success' => TRUE]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Updates a folder (name and/or parent).
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function renameFolder(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $folder_id = $data['folder_id'] ?? NULL;
    $name = $data['name'] ?? '';
    $parent_id = $data['parent_id'] ?? NULL;

    if (empty($folder_id) || empty($name)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'Folder ID and name are required'], 400);
    }

    try {
      $term_storage = $this->entityTypeManager()->getStorage('taxonomy_term');
      $term = $term_storage->load($folder_id);

      if (!$term) {
        return new JsonResponse(['success' => FALSE, 'error' => 'Folder not found'], 404);
      }

      $term->setName($name);

      // Update parent if provided.
      if ($parent_id !== NULL) {
        if ($parent_id === '' || $parent_id === '0') {
          // Set to root level.
          $term->set('parent', 0);
        }
        else {
          // Prevent setting folder as its own parent.
          if ($parent_id == $folder_id) {
            return new JsonResponse(['success' => FALSE, 'error' => 'Cannot set folder as its own parent'], 400);
          }
          $term->set('parent', $parent_id);
        }
      }

      $term->save();

      // Get the updated parent info.
      $parents = $term_storage->loadParents($term->id());
      $new_parent_id = '';
      if (!empty($parents)) {
        $parent = reset($parents);
        $new_parent_id = $parent->id();
      }

      return new JsonResponse([
        'success' => TRUE,
        'folder' => [
          'id' => $term->id(),
          'name' => $term->getName(),
          'parent_id' => $new_parent_id,
        ],
        'reload' => TRUE,
      ]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Creates a new collection.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function createCollection(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $name = $data['name'] ?? '';

    if (empty($name)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'Name is required'], 400);
    }

    try {
      $term_storage = $this->entityTypeManager()->getStorage('taxonomy_term');
      $term = $term_storage->create([
        'vid' => 'nodehive_dam_collection',
        'name' => $name,
      ]);
      $term->save();

      return new JsonResponse([
        'success' => TRUE,
        'collection' => [
          'id' => $term->id(),
          'name' => $term->getName(),
        ],
      ]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Renames a collection.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function renameCollection(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $collection_id = $data['collection_id'] ?? NULL;
    $name = $data['name'] ?? '';

    if (empty($collection_id) || empty($name)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'Collection ID and name are required'], 400);
    }

    try {
      $term_storage = $this->entityTypeManager()->getStorage('taxonomy_term');
      $term = $term_storage->load($collection_id);

      if (!$term) {
        return new JsonResponse(['success' => FALSE, 'error' => 'Collection not found'], 404);
      }

      $term->setName($name);
      $term->save();

      return new JsonResponse([
        'success' => TRUE,
        'collection' => [
          'id' => $term->id(),
          'name' => $term->getName(),
        ],
        'reload' => TRUE,
      ]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Deletes a collection.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function deleteCollection(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $collection_id = $data['collection_id'] ?? NULL;

    if (empty($collection_id)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'Collection ID is required'], 400);
    }

    try {
      $term_storage = $this->entityTypeManager()->getStorage('taxonomy_term');
      $term = $term_storage->load($collection_id);

      if (!$term) {
        return new JsonResponse(['success' => FALSE, 'error' => 'Collection not found'], 404);
      }

      // Remove this collection from all media items first.
      $media_storage = $this->entityTypeManager()->getStorage('media');
      $media_ids = $media_storage->getQuery()
        ->accessCheck(TRUE)
        ->condition('nodehive_dam_collections', $collection_id)
        ->execute();

      if (!empty($media_ids)) {
        $media_items = $media_storage->loadMultiple($media_ids);
        foreach ($media_items as $media) {
          $current_collections = [];
          foreach ($media->get('nodehive_dam_collections') as $item) {
            if ($item->target_id != $collection_id) {
              $current_collections[] = $item->target_id;
            }
          }
          $media->set('nodehive_dam_collections', $current_collections);
          $media->save();
        }
      }

      // Delete the collection term.
      $term->delete();

      return new JsonResponse([
        'success' => TRUE,
        'reload' => TRUE,
      ]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Adds media items to a collection.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function addToCollection(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $media_ids = $data['media_ids'] ?? [];
    $collection_id = $data['collection_id'] ?? NULL;

    if (empty($media_ids) || empty($collection_id)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'Media IDs and collection ID are required'], 400);
    }

    try {
      $media_storage = $this->entityTypeManager()->getStorage('media');
      $media_items = $media_storage->loadMultiple($media_ids);

      foreach ($media_items as $media) {
        // Get current collections.
        $current_collections = [];
        foreach ($media->get('nodehive_dam_collections') as $item) {
          $current_collections[] = $item->target_id;
        }

        // Add new collection if not already present.
        if (!in_array($collection_id, $current_collections)) {
          $current_collections[] = $collection_id;
          $media->set('nodehive_dam_collections', $current_collections);
          $media->save();
        }
      }

      return new JsonResponse(['success' => TRUE]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Removes media items from a collection.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with result.
   */
  public function removeFromCollection(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);
    $media_ids = $data['media_ids'] ?? [];
    $collection_id = $data['collection_id'] ?? NULL;

    if (empty($media_ids) || empty($collection_id)) {
      return new JsonResponse(['success' => FALSE, 'error' => 'Media IDs and collection ID are required'], 400);
    }

    try {
      $media_storage = $this->entityTypeManager()->getStorage('media');
      $media_items = $media_storage->loadMultiple($media_ids);

      foreach ($media_items as $media) {
        // Get current collections and remove the specified one.
        $current_collections = [];
        foreach ($media->get('nodehive_dam_collections') as $item) {
          if ($item->target_id != $collection_id) {
            $current_collections[] = $item->target_id;
          }
        }

        $media->set('nodehive_dam_collections', $current_collections);
        $media->save();
      }

      return new JsonResponse(['success' => TRUE]);
    }
    catch (\Exception $e) {
      return new JsonResponse(['success' => FALSE, 'error' => $e->getMessage()], 500);
    }
  }

  /**
   * Downloads multiple media items as a ZIP file.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   The ZIP file response.
   */
  public function download(Request $request): Response {
    $data = json_decode($request->getContent(), TRUE);
    $media_ids = $data['media_ids'] ?? [];

    if (empty($media_ids)) {
      return new JsonResponse(['error' => 'No media items specified'], 400);
    }

    $media_storage = $this->entityTypeManager()->getStorage('media');
    $media_items = $media_storage->loadMultiple($media_ids);

    if (empty($media_items)) {
      return new JsonResponse(['error' => 'No media items found'], 404);
    }

    // Create a temporary ZIP file using system temp directory.
    $temp_dir = $this->fileSystem->getTempDirectory();
    $zip_path = $temp_dir . '/dam_download_' . uniqid() . '.zip';

    $zip = new \ZipArchive();
    if ($zip->open($zip_path, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== TRUE) {
      return new JsonResponse(['error' => 'Failed to create ZIP file'], 500);
    }

    $file_count = 0;
    foreach ($media_items as $media) {
      $source = $media->getSource();
      $source_field = $source->getSourceFieldDefinition($media->bundle->entity)->getName();
      $source_file = $media->get($source_field)->entity;

      if ($source_file) {
        $file_uri = $source_file->getFileUri();
        $real_path = $this->fileSystem->realpath($file_uri);

        if ($real_path && file_exists($real_path)) {
          $filename = $source_file->getFilename();
          // Ensure unique filenames in the ZIP.
          $base_name = pathinfo($filename, PATHINFO_FILENAME);
          $extension = pathinfo($filename, PATHINFO_EXTENSION);
          $zip_filename = $filename;
          $counter = 1;
          while ($zip->locateName($zip_filename) !== FALSE) {
            $zip_filename = $base_name . '_' . $counter . '.' . $extension;
            $counter++;
          }
          $zip->addFile($real_path, $zip_filename);
          $file_count++;
        }
      }
    }

    $zip->close();

    if ($file_count === 0) {
      @unlink($zip_path);
      return new JsonResponse(['error' => 'No files to download'], 404);
    }

    // Build filename from folder name, date, and media types.
    $folder_name = 'all-media';
    $folder_id = $data['folder_id'] ?? '';
    if (!empty($folder_id) && $folder_id !== '_none') {
      $term = $this->entityTypeManager()->getStorage('taxonomy_term')->load($folder_id);
      if ($term) {
        $folder_name = $term->getName();
      }
    }
    elseif ($folder_id === '_none') {
      $folder_name = 'unfiled';
    }

    // Collect unique media types.
    $media_types = [];
    foreach ($media_items as $media) {
      $bundle_label = $media->get('bundle')->entity?->label() ?? $media->bundle();
      $media_types[$bundle_label] = TRUE;
    }
    $types_string = strtolower(implode('-', array_keys($media_types)));

    // Sanitize folder name for filename.
    $folder_name = preg_replace('/[^a-zA-Z0-9-_]/', '-', $folder_name);
    $folder_name = preg_replace('/-+/', '-', $folder_name);
    $folder_name = trim($folder_name, '-');

    $date_string = date('Y-m-d');
    $zip_filename = $date_string . '_' . $folder_name . '_' . $types_string . '.zip';

    // Send the ZIP file.
    $response = new Response(file_get_contents($zip_path));
    $response->headers->set('Content-Type', 'application/zip');
    $response->headers->set('Content-Disposition', 'attachment; filename="' . $zip_filename . '"');
    $response->headers->set('Content-Length', (string) filesize($zip_path));

    // Clean up temp file.
    @unlink($zip_path);

    return $response;
  }

  /**
   * Builds a render array for a single media item.
   *
   * @param \Drupal\media\MediaInterface $media
   *   The media entity.
   *
   * @return array
   *   The item data array.
   */
  protected function buildMediaItem(MediaInterface $media): array {
    $thumbnail = $media->get('thumbnail')->entity;
    $thumbnail_url = '';
    $preview_url = '';
    $file_url = '';
    $mime_type = '';

    // Get the source file for documents/files.
    $source = $media->getSource();
    $source_field = $source->getSourceFieldDefinition($media->bundle->entity)->getName();
    $source_file = $media->get($source_field)->entity;

    $file_size = 0;
    $image_width = 0;
    $image_height = 0;

    if ($source_file) {
      $file_url = $this->fileUrlGenerator->generateAbsoluteString($source_file->getFileUri());
      $mime_type = $source_file->getMimeType();
      $file_size = $source_file->getSize();

      // Get image dimensions if it's an image.
      if (str_starts_with($mime_type, 'image/')) {
        $image = $this->imageFactory->get($source_file->getFileUri());
        if ($image->isValid()) {
          $image_width = $image->getWidth();
          $image_height = $image->getHeight();
        }
      }
    }

    if ($thumbnail) {
      $file_uri = $thumbnail->getFileUri();

      // Grid thumbnail (medium).
      $thumb_style = $this->entityTypeManager()->getStorage('image_style')->load('medium');
      if ($thumb_style) {
        $thumbnail_url = $thumb_style->buildUrl($file_uri);
      }
      else {
        $thumbnail_url = $this->fileUrlGenerator->generateAbsoluteString($file_uri);
      }

      // Preview image (wide/large).
      $preview_style = $this->entityTypeManager()->getStorage('image_style')->load('wide');
      if ($preview_style) {
        $preview_url = $preview_style->buildUrl($file_uri);
      }
      else {
        $preview_url = $this->fileUrlGenerator->generateAbsoluteString($file_uri);
      }
    }

    // Get alt text for images (only image fields have alt property).
    $alt_text = '';
    $source_field_definition = $media->getFieldDefinition($source_field);
    $is_image_field = $source_field_definition && $source_field_definition->getType() === 'image';
    if ($is_image_field) {
      $source_field_value = $media->get($source_field)->first();
      if ($source_field_value) {
        $alt_text = $source_field_value->get('alt')->getValue() ?? '';
      }
    }

    // Build image style URLs for images.
    $image_styles = [];
    if ($source_file && str_starts_with($mime_type, 'image/')) {
      $source_uri = $source_file->getFileUri();
      $style_storage = $this->entityTypeManager()->getStorage('image_style');
      foreach ($style_storage->loadMultiple() as $style) {
        $image_styles[] = [
          'name' => $style->label(),
          'id' => $style->id(),
          'url' => $style->buildUrl($source_uri),
        ];
      }
      // Add original.
      array_unshift($image_styles, [
        'name' => 'Original',
        'id' => 'original',
        'url' => $file_url,
      ]);
    }

    return [
      'id' => $media->id(),
      'name' => $media->getName(),
      'bundle' => $media->bundle(),
      'bundle_label' => $media->get('bundle')->entity?->label() ?? $media->bundle(),
      'thumbnail_url' => $thumbnail_url,
      'preview_url' => $preview_url,
      'file_url' => $file_url,
      'mime_type' => $mime_type,
      'file_size' => $file_size,
      'image_width' => $image_width,
      'image_height' => $image_height,
      'alt_text' => $alt_text,
      'image_styles' => $image_styles,
      'created' => $media->getCreatedTime(),
      'created_formatted' => $this->dateFormatter->format($media->getCreatedTime(), 'short'),
      'edit_url' => $media->toUrl('edit-form')->toString(),
      'view_url' => $media->toUrl('canonical')->toString(),
    ];
  }

  /**
   * Determines the media type based on mime type.
   *
   * @param string $mime_type
   *   The file mime type.
   * @param string $filename
   *   The original filename (used for extension fallback).
   *
   * @return string|null
   *   The media type ID or NULL if not supported.
   */
  protected function determineMediaType(string $mime_type, string $filename = ''): ?string {
    // Also check extension as fallback since mime detection can be unreliable.
    $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
    $image_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'];
    $video_extensions = ['mp4', 'webm', 'ogg', 'mov', 'avi'];
    $audio_extensions = ['mp3', 'wav', 'ogg', 'flac', 'aac'];

    // Map mime types to media bundles.
    if (str_starts_with($mime_type, 'image/') || in_array($extension, $image_extensions)) {
      return 'image';
    }
    if (str_starts_with($mime_type, 'video/') || in_array($extension, $video_extensions)) {
      return 'video';
    }
    if (str_starts_with($mime_type, 'audio/') || in_array($extension, $audio_extensions)) {
      return 'audio';
    }
    if (in_array($mime_type, [
      'application/pdf',
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'application/vnd.ms-excel',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'text/plain',
    ])) {
      return 'document';
    }

    // Check if a file media type exists as fallback.
    $media_type = $this->entityTypeManager()->getStorage('media_type')->load('file');
    if ($media_type) {
      return 'file';
    }

    return 'document';
  }

}
