<?php

namespace Drupal\nodehive_public_api\Controller;

use Drupal\Core\Cache\CacheableJsonResponse;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Url;
use Drupal\nodehive_core\MenuHelperInterface;
use Drupal\nodehive_public_api\ApiResponseTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Controller for Spaces API endpoints.
 */
class SpacesApiController extends ControllerBase {

  use ApiResponseTrait;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Constructs a SpacesApiController object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   */
  public function __construct(
    EntityTypeManagerInterface $entity_type_manager,
    LanguageManagerInterface $language_manager,
    RequestStack $request_stack,
  ) {
    $this->entityTypeManager = $entity_type_manager;
    $this->languageManager = $language_manager;
    $this->requestStack = $request_stack;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager'),
      $container->get('language_manager'),
      $container->get('request_stack')
    );
  }

  /**
   * Returns the space context for a given space.
   *
   * @param string $space_ref
   *   The space ID.
   *
   * @return \Drupal\Core\Cache\CacheableJsonResponse
   *   The JSON response containing the space context.
   */
  public function getSpace(string $space_ref): CacheableJsonResponse {
    // Get the current language.
    $current_language = $this->languageManager->getCurrentLanguage();
    $language = $current_language->getId();

    // Load the space by ID (numeric) or machine name (string).
    $space_storage = $this->entityTypeManager->getStorage('nodehive_space');
    if (is_numeric($space_ref)) {
      $space = $space_storage->load($space_ref);
    }
    else {
      $ids = $space_storage->getQuery()
        ->condition('machine_name', $space_ref)
        ->accessCheck(FALSE)
        ->execute();
      $space = $ids ? $space_storage->load(reset($ids)) : NULL;
    }

    if (!$space) {
      $this->getLogger('nodehive_public_api')->notice(
        'Space @space_id not found.',
        ['@space_id' => $space_ref]
      );
      return $this->apiError(
        (string) $this->t('Space @space_id not found.', ['@space_id' => $space_ref]),
        404
      );
    }

    // Build space context.
    $space_data = [
      'id' => $space->id(),
      'name' => $space->label(),
      'machine_name' => $space->hasField('machine_name') && !$space->get('machine_name')->isEmpty()
        ? $space->get('machine_name')->value
        : NULL,
      'language' => $language,
      'space_url' => $space->hasField('space_url') && !$space->get('space_url')->isEmpty()
        ? $space->get('space_url')->uri
        : NULL,
      'space_type' => $space->hasField('space_type') && !$space->get('space_type')->isEmpty()
        ? $space->get('space_type')->value
        : NULL,
      'status' => (bool) $space->get('status')->value,
      'created' => $space->hasField('created') ? date('c', $space->get('created')->value) : NULL,
      'changed' => $space->hasField('changed') ? date('c', $space->get('changed')->value) : NULL,
    ];

    // Add tags if available.
    if ($space->hasField('tags') && !$space->get('tags')->isEmpty()) {
      $tags = [];
      foreach ($space->get('tags')->referencedEntities() as $tag) {
        $tags[] = [
          'id' => $tag->id(),
          'name' => $tag->label(),
        ];
      }
      $space_data['tags'] = $tags;
    }

    // Add frontpage node if set.
    if ($space->hasField('frontpage_node') && !$space->get('frontpage_node')->isEmpty()) {
      $frontpage_node = $space->get('frontpage_node')->entity;
      if ($frontpage_node) {
        $space_data['frontpage_node'] = [
          'id' => $frontpage_node->id(),
          'uuid' => $frontpage_node->uuid(),
          'title' => $frontpage_node->getTitle(),
          'type' => $frontpage_node->bundle(),
        ];
      }
    }

    // Get associated menus.
    $menu_storage = $this->entityTypeManager->getStorage('menu');
    $all_menus = $menu_storage->loadMultiple();
    $space_menus = [];

    foreach ($all_menus as $menu) {
      $menu_id = $menu->id();

      // Skip excluded menus.
      if (in_array($menu_id, MenuHelperInterface::API_EXCLUDED_MENUS)) {
        continue;
      }

      $referenced_spaces = $menu->getThirdPartySetting('nodehive_core', 'nodehive_space_field', []);
      if (in_array($space->id(), $referenced_spaces)) {
        try {
          $menu_url = Url::fromRoute('nodehive_public_api.menu', ['menu_id' => $menu_id], [
            'absolute' => TRUE,
            'language' => $current_language,
          ])->toString();

          $space_menus[] = [
            'id' => $menu_id,
            'label' => $menu->label(),
            'url' => $menu_url,
          ];
        }
        catch (\Exception $e) {
          $this->getLogger('nodehive_public_api')->error(
            'Error generating URL for menu @menu_id in space @space_id: @message',
            ['@menu_id' => $menu_id, '@space_id' => $space_ref, '@message' => $e->getMessage()]
          );
        }
      }
    }

    if (!empty($space_menus)) {
      $space_data['menus'] = $space_menus;
    }

    // Get enabled content types for this space.
    if ($space->hasField('content_types') && !$space->get('content_types')->isEmpty()) {
      // Get content count by type for this space using efficient query.
      $content_count_by_type = [];
      try {
        $node_storage = $this->entityTypeManager->getStorage('node');
        $query = $node_storage->getAggregateQuery()
          ->accessCheck(FALSE)
          ->condition('nodehive_space', $space->id())
          ->groupBy('type')
          ->aggregate('nid', 'COUNT');

        $results = $query->execute();

        foreach ($results as $result) {
          $content_count_by_type[$result['type']] = (int) $result['nid_count'];
        }
      }
      catch (\Exception $e) {
        $this->getLogger('nodehive_public_api')->error(
          'Error counting content for space @space_id: @message',
          ['@space_id' => $space_ref, '@message' => $e->getMessage()]
        );
      }

      $content_types = [];
      foreach ($space->get('content_types')->referencedEntities() as $content_type) {
        $type_id = $content_type->id();
        $content_types[] = [
          'id' => $type_id,
          'label' => $content_type->label(),
          'description' => $content_type->getDescription(),
          'count' => $content_count_by_type[$type_id] ?? 0,
        ];
      }
      if (!empty($content_types)) {
        $space_data['content_types'] = $content_types;
      }
    }

    // Create response with cache metadata.
    $cache_metadata = new CacheableMetadata();
    $cache_metadata->addCacheContexts(['user.permissions', 'languages:language_interface']);

    $cache_tags = [
      'nodehive_space:' . $space_ref,
      'nodehive_space_list',
      'node_list',
      'config:system.menu_list',
    ];

    // Add cache tags for content types.
    if ($space->hasField('content_types') && !$space->get('content_types')->isEmpty()) {
      foreach ($space->get('content_types')->referencedEntities() as $content_type) {
        $cache_tags[] = 'config:node.type.' . $content_type->id();
      }
    }

    $cache_metadata->addCacheTags($cache_tags);

    $response = $this->apiSuccess($space_data, $cache_metadata);
    $response->addCacheableDependency($space);

    // Only add cache dependencies for menus that are actually in the response.
    if (!empty($space_menus)) {
      foreach ($all_menus as $menu) {
        $referenced_spaces = $menu->getThirdPartySetting('nodehive_core', 'nodehive_space_field', []);
        if (in_array($space->id(), $referenced_spaces)) {
          $response->addCacheableDependency($menu);
        }
      }
    }

    return $response;
  }

}
