<?php

namespace Drupal\nodehive_visualeditor\Controller;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use Drupal\nodehive_core\SpaceInterface;
use Drupal\path_alias\AliasManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\jwt\Authentication\Provider\JwtAuth;

/**
 * Defines the VisualEditorController class.
 */
class VisualEditorController extends ControllerBase {

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

  /**
   * The form builder.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $formBuilder;

  /**
   * The private temp store factory.
   *
   * @var \Drupal\Core\TempStore\PrivateTempStoreFactory
   */
  protected $tempStoreFactory;

  /**
   * The path alias manager.
   *
   * @var \Drupal\path_alias\AliasManagerInterface
   */
  protected $aliasManager;

  /**
   * The JWT authentication provider.
   *
   * @var \Drupal\jwt\Authentication\Provider\JwtAuth|null
   */
  protected $jwtAuth;

  /**
   * Constructs a new VisualEditorController object.
   *
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
   *   The form builder.
   * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
   *   The private temp store factory.
   * @param \Drupal\path_alias\AliasManagerInterface $alias_manager
   *   The path alias manager.
   * @param \Drupal\jwt\Authentication\Provider\JwtAuth|null $jwt_auth
   *   The JWT authentication provider (optional, jwt module only).
   */
  public function __construct(RequestStack $request_stack, FormBuilderInterface $form_builder, PrivateTempStoreFactory $temp_store_factory, AliasManagerInterface $alias_manager, ?JwtAuth $jwt_auth = NULL) {
    $this->requestStack = $request_stack;
    $this->formBuilder = $form_builder;
    $this->tempStoreFactory = $temp_store_factory;
    $this->aliasManager = $alias_manager;
    $this->jwtAuth = $jwt_auth;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('request_stack'),
      $container->get('form_builder'),
      $container->get('tempstore.private'),
      $container->get('path_alias.manager'),
      $container->get('jwt.authentication.jwt', ContainerInterface::NULL_ON_INVALID_REFERENCE),
    );
  }

  /**
   * Will show the first space available or the space selected in the tempstore.
   *
   * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
   *   Return a renderable array or redirect to the space canonical route.
   */
  public function visualEditorPage() {
    $spaces = $this->entityTypeManager()
      ->getStorage("nodehive_space")
      ->loadByProperties([
        'status' => 1,
      ]);

    if (!$spaces) {
      return [
        '#theme' => 'inline_template',
        '#markup' => $this->t(
          "No spaces available, please <a href='@link'>create a new space.</a>",
          [
            '@link' => Url::fromUserInput('/space/add')->toString(),
          ]
        ),
      ];
    }
    else {
      $store = $this->tempStoreFactory->get('nodehive_tempstore');
      $space_id = $store->get('nodehive_space') ?: '';

      if (!$space_id) {
        $space_id = array_key_first($spaces);
      }

      $url = Url::fromRoute('entity.nodehive_space.canonical', ['nodehive_space' => $space_id])->toString();
      return new RedirectResponse($url);
    }
  }

  /**
   * Redirects /space/{id}/visualeditor to the canonical space route.
   *
   * Preserves all query parameters (e.g. ?url=...).
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   A redirect response to the canonical space route.
   */
  public function redirectToCanonical($nodehive_space) {
    if ($nodehive_space instanceof SpaceInterface) {
      $space_id = $nodehive_space->id();
    }
    else {
      $space_id = $nodehive_space;
    }

    $query = $this->requestStack->getCurrentRequest()->query->all();
    $url = Url::fromRoute('entity.nodehive_space.canonical', ['nodehive_space' => $space_id], ['query' => $query]);
    return new RedirectResponse($url->toString(), 301);
  }

  /**
   * Display the visual editor page for a single space.
   *
   * @return array
   *   Return a renderable array for the visual editor page.
   */
  public function visualEditorSingleSpacePage($nodehive_space) {

    // Get all published spaces.
    $spaces = $this->entityTypeManager()
      ->getStorage("nodehive_space")
      ->loadByProperties([
        'status' => 1,
      ]);

    if (!$spaces) {
      $this->messenger()->addWarning(
        $this->t(
          "No spaces available, please <a href='@link'>create a new space.</a>",
          [
            '@link' => Url::fromUserInput('/space/add')->toString(),
          ]
        )
      );
    }

    // Handle both entity upcasting (SpaceInterface) and raw ID.
    if ($nodehive_space instanceof SpaceInterface) {
      $space_entity = $nodehive_space;
      $space_id = $space_entity->id();
    }
    else {
      $space_id = $nodehive_space;
      $space_entity = $this->entityTypeManager()
        ->getStorage('nodehive_space')
        ->load($nodehive_space);
    }

    // Check if the space actually exists.
    if (!$space_entity) {
      $this->messenger()->addWarning(
        $this->t("The selected space does not exist.")
      );
      return $this->redirect('nodehive_visualeditor.visual_editor');
    }

    $url_from_get = $this->requestStack->getCurrentRequest()->query->get("url");

    if ($url_from_get) {
      $url = $url_from_get;
    }
    else {
      // Check if the frontpage_node field exists and has a value.
      if (!$space_entity->get('frontpage_node')->isEmpty()) {
        /** @var \Drupal\node\NodeInterface $frontpage_node */
        $frontpage_node = $space_entity->get('frontpage_node')->entity;
        if ($frontpage_node && $frontpage_node->access('view')) {
          $url = $space_entity->space_url[0]->uri . '/node/' . $frontpage_node->id();
        }
        else {
          $url = $space_entity->space_url[0]->uri;
        }
      }
      else {
        $url = $space_entity->space_url[0]->uri;
      }
    }

    $base_url = $space_entity->space_url[0]->uri;

    // Try to resolve the previewed URL to a node edit URL.
    $node_edit_url = NULL;
    if ($url_from_get && $base_url) {
      $node_edit_url = $this->resolveNodeEditUrl($url_from_get, $base_url);
    }

    $recent_edit_form = $this->formBuilder->getForm('Drupal\nodehive_visualeditor\Form\RecentEditForm', $space_id);

    // Generate JWT token if module is enabled.
    $jwt_token = NULL;
    if ($this->moduleHandler()->moduleExists('jwt') && $this->jwtAuth) {
      $jwt_token = $this->jwtAuth->generateToken();
    }

    // Build the rest of your render array.
    $build['content'] = [
      '#theme' => 'nodehive_visualeditor',
      '#recent_edit_form' => $recent_edit_form,
      '#spaces' => $spaces,
      '#selected_space' => $space_entity->toArray(),
      '#selected_space_id' => $space_id,
      '#url' => $url,
      '#node_edit_url' => $node_edit_url,
      '#current_path_with_query' => $this->requestStack->getCurrentRequest()->getRequestUri(),
      '#frontend_login_url' => $space_entity->frontpage_login_url[0]->uri,
      '#base_url' => $base_url,
      '#attached' => [
        'drupalSettings' => [
          'nodehive_visualeditor' => [
            'is_multilingual' => $this->isSiteMultilingual(),
            'jwt_token' => $jwt_token,
            'iframe_origin' => $base_url,
          ],
        ],
      ],
      '#cache' => ['max-age' => 0],
    ];
    return $build;
  }

  /**
   * Resolves a frontend URL to a Drupal node edit URL.
   *
   * @param string $frontend_url
   *   The full frontend URL (e.g., https://netnode.ch/de/blog/article-title).
   * @param string $base_url
   *   The space base URL (e.g., https://netnode.ch).
   *
   * @return string|null
   *   The node edit URL or NULL if the URL cannot be resolved.
   */
  protected function resolveNodeEditUrl(string $frontend_url, string $base_url): ?string {
    // Strip the base URL to get the path (e.g., /de/blog/article-title).
    $path = str_replace($base_url, '', $frontend_url);
    if (empty($path) || $path === '/') {
      return NULL;
    }

    // Determine langcode from path prefix.
    $langcode = NULL;
    $languages = $this->languageManager()->getLanguages();
    foreach ($languages as $lang) {
      $prefix = '/' . $lang->getId() . '/';
      if (str_starts_with($path, $prefix)) {
        $langcode = $lang->getId();
        // Strip the language prefix for alias lookup.
        $path = '/' . substr($path, strlen($prefix));
        break;
      }
    }

    // Try to resolve the path alias to an internal path.
    try {
      $internal_path = $this->aliasManager->getPathByAlias($path, $langcode);
      if (preg_match('#^/node/(\d+)$#', $internal_path, $matches)) {
        $nid = $matches[1];
        return Url::fromRoute('entity.node.edit_form', ['node' => $nid])->toString();
      }
    }
    catch (\Exception $e) {
      // Alias lookup failed, ignore.
    }

    return NULL;
  }

  /**
   * Helper method to get the space title.
   *
   * @return string|null
   *   Return string or null if no label found.
   */
  public function getTitle($nodehive_space) {
    if ($nodehive_space instanceof SpaceInterface) {
      return $nodehive_space->label();
    }

    $space_entity = $this->entityTypeManager()
      ->getStorage('nodehive_space')
      ->load($nodehive_space);

    return $space_entity->label();
  }

  /**
   * Checks if the site is configured with multiple languages.
   *
   * @return bool
   *   Returns true if the site is multilingual, false otherwise.
   */
  protected function isSiteMultilingual() {
    $languages = $this->languageManager()->getLanguages();
    return count($languages) > 1;
  }

}
