<?php

namespace Drupal\pagedesigner;

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Routing\CurrentRouteMatch;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\pagedesigner\Entity\Element;
use Drupal\pagedesigner\Service\ElementHandler;

/**
 * Base class for handling operations on elements and content entities.
 */
class PagedesignerService implements PagedesignerServiceInterface {
  use StringTranslationTrait;

  /**
   * Cache the edit access.
   *
   * @var bool
   */
  protected $editAccess = NULL;

  /**
   * Create a new instance.
   *
   * @param \Drupal\pagedesigner\Service\ElementHandler $elementHandler
   *   The element handler.
   * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
   *   The language manager.
   * @param \Drupal\Core\Routing\CurrentRouteMatch $currentRoute
   *   The current route.
   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
   *   The current user.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
   *   The entity field manager.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
   *   The module handler.
   */
  public function __construct(
    protected ElementHandler $elementHandler,
    protected LanguageManagerInterface $languageManager,
    protected CurrentRouteMatch $currentRoute,
    protected AccountProxyInterface $currentUser,
    protected EntityFieldManagerInterface $entityFieldManager,
    protected ModuleHandlerInterface $moduleHandler,
  ) {
  }

  /**
   * Get all pagedesigner fields of the entity.
   *
   *   The entity to get the fields from.
   *
   * @return array
   *   The list of fields, keyed by field name.
   */
  public function getPagedesignerFields(ContentEntityInterface $entity) {
    $pagedesignerFields = [];
    $fields = $entity->getFields(TRUE);
    foreach ($fields as $field) {
      if ($field->getFieldDefinition()->getType() == 'pagedesigner_item') {
        $pagedesignerFields[$field->getName()] = $field;
      }
    }
    return $pagedesignerFields;
  }

  /**
   * {@inheritdoc}
   */
  public function addContainer(ContentEntityInterface &$entity) {
    $changed = FALSE;
    $fields = $this->getPagedesignerFields($entity);
    foreach ($fields as $field) {
      if ($this->addContainerForField($entity, $field->getName())) {
        $changed = TRUE;
      }
    }
    return $changed;
  }

  /**
   * {@inheritdoc}
   */
  public function addContainerForField(ContentEntityInterface &$entity, string $field_name) {
    $changed = FALSE;
    $language = $entity->language()->getId();
    if ($entity->get($field_name)->isEmpty()) {
      if (!$entity->isDefaultTranslation()) {
        $container = $this->getContainer($entity->getUntranslated(), $field_name);
        if (!$container->hasTranslation($language)) {
          $container->addTranslation($language);
          $container->save();
        }
      }
      else {
        $container = $this->getContainer($entity, $field_name);
      }
      $entity->get($field_name)->entity = $container;
      $changed = TRUE;
    }
    elseif (!$entity->isDefaultTranslation()) {
      $container = $entity->get($field_name)->entity;
      if ($container && !$container->hasTranslation($language)) {
        $container->addTranslation($language);
        $container->save();
      }
    }
    return $changed;
  }

  /**
   * {@inheritdoc}
   */
  public function overrideContainers(ContentEntityInterface &$entity) {
    $fields = $this->getPagedesignerFields($entity);
    foreach ($fields as $field) {
      $container = $this->createContainer($entity);
      $entity->getUntranslated()->get($field->getName())->entity = $container;
    }
    foreach ($entity->getTranslationLanguages(FALSE) as $language) {
      foreach ($fields as $field) {
        $entity->getTranslation($language->getId())->get($field->getName())->entity = $entity->getUntranslated()->get($field->getName())->entity;
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function saveContainer(ContentEntityInterface &$entity) {
    $fields = $this->getPagedesignerFields($entity);
    foreach ($fields as $field) {
      if (!$entity->get($field->getName())->isEmpty()) {
        $container = $entity->get($field->getName())->entity;
        $container->entity = $entity;
        $container->save();
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function enableTranslation(ContentEntityInterface &$entity, ?string $field_name = NULL) {
    foreach ($entity->getTranslationLanguages(FALSE) as $language) {
      if ($entity->hasTranslation($language->getId())) {
        $entity = $entity->getTranslation($language->getId());
        if (!empty($field_name)) {
          if ($this->addContainerForField($entity, $field_name)) {
            $entity->save();
          }
        }
        else {
          if ($this->addContainer($entity)) {
            $entity->save();
          }
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getContainer(ContentEntityInterface $entity, $field_name = 'field_pagedesigner_content') {
    $container = $entity->get($field_name)->entity;
    if ($container == NULL) {
      $container = $this->createContainer($entity);
    }
    else {
      $container = $this->getLanguage($container, $entity->language()->getId());
    }
    return $container;
  }

  /**
   * {@inheritdoc}
   */
  public function createContainer(ContentEntityInterface $entity) {
    $container = Element::create(['type' => 'container', 'name' => 'container']);
    $container->langcode->value = $entity->language()->getId();
    if (!empty($entity) && !$entity->isNew()) {
      $container->entity->entity = $entity;
    }
    $container->save();
    return $container;
  }

  /**
   * {@inheritdoc}
   */
  public function clearPagedesigner(ContentEntityInterface &$entity, ?string $field_name = NULL) {
    if (empty($field)) {
      $fields = $this->getPagedesignerFields($entity);
      foreach ($fields as $field_name => $field) {
        $this->doClearPagedesigner($entity, $field_name);
      }
    }
    else {
      $this->doClearPagedesigner($entity, $field_name);
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function doClearPagedesigner(ContentEntityInterface &$entity, ?string $field_name = NULL) {
    if (empty($field_name)) {
      return;
    }
    foreach ($entity->getTranslationLanguages(TRUE) as $language) {
      if ($entity->hasTranslation($language->getId())) {
        $entity = $entity->getTranslation($language->getId());
        $container = $this->getContainer($entity, $field_name);
        if (!empty($container)) {
          $this->getHandler()->delete($container);
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getLanguage(Element $container, $language) {
    if (!$container->hasTranslation($language)) {
      $container->addTranslation($language);
      $container->save();
    }
    return $container->getTranslation($language);
  }

  /**
   * {@inheritdoc}
   */
  public function isEditMode(?ContentEntityInterface $entity = NULL, ?AccountInterface $user = NULL) {
    return $this->isPagedesignerRoute() && $this->hasEditAccess($entity, $user);
  }

  /**
   * {@inheritdoc}
   */
  public function isPagedesignerRoute() {
    return $this->currentRoute->getRouteName() == 'pagedesigner.node.edit_mode';
  }

  /**
   * {@inheritdoc}
   */
  public function hasEditAccess(?ContentEntityInterface $entity = NULL, ?AccountInterface $user = NULL) {
    if ($this->editAccess !== NULL) {
      return $this->editAccess;
    }
    if ($entity == NULL) {
      if ($this->isPagedesignerRoute()) {
        $entity = $this->currentRoute->getParameter('node');
      }
      else {
        return FALSE;
      }
    }
    if ($user == NULL) {
      $user = $this->currentUser;
    }

    $hook_result = TRUE;
    $access_results = $this->getModuleHandler()->invokeAll('pagedesigner_access', [$entity, $user]);
    /** @var \Drupal\Core\Access\AccessResult $access_result */
    foreach ($access_results as $access_result) {
      if (!$access_result->isAllowed()) {
        $hook_result = FALSE;
      }
    }

    $this->editAccess = $user->hasPermission('edit pagedesigner element entities')
    && $entity->access('update', $user)
    && $hook_result;
    return $this->editAccess;

  }

  /**
   * {@inheritdoc}
   */
  public function getHandler() {
    return $this->elementHandler;
  }

  /**
   * {@inheritdoc}
   */
  public function getModuleHandler() {
    return $this->moduleHandler;
  }

}
