<?php

namespace Drupal\pagedesigner\Plugin;

use Drupal\Component\Plugin\PluginBase;
use Drupal\Component\Plugin\PluginInspectionInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\pagedesigner\Entity\Element;
use Drupal\pagedesigner\Plugin\pagedesigner\HandlerPluginInterface;
use Drupal\pagedesigner\Service\ElementHandler;
use Drupal\ui_patterns\Definition\PatternDefinitionField;
use Drupal\ui_patterns\UiPatternsManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Base implementation for pagedesigner element handlers.
 */
abstract class HandlerPluginBase extends PluginBase implements HandlerPluginInterface, PluginInspectionInterface, ContainerFactoryPluginInterface {

  /**
   * The pattern definitions.
   *
   * @var array
   */
  protected static $patternDefinitions = NULL;

  /**
   * The element view builder.
   *
   * @var \Drupal\pagedesigner\ElementViewBuilder
   */
  protected $viewBuilder = NULL;

  /**
   * Create a new pagedesigner plugin.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\ui_patterns\UiPatternsManagerInterface $patternManager
   *   The pattern manager.
   * @param \Drupal\pagedesigner\Service\ElementHandler $elementHandler
   *   The element handler.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The renderer.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
   *   The language manager.
   */
  public function __construct(
    $configuration,
    $plugin_id,
    $plugin_definition,
    protected UiPatternsManager $patternManager,
    protected ElementHandler $elementHandler,
    protected RendererInterface $renderer,
    protected EntityTypeManager $entityTypeManager,
    protected LanguageManagerInterface $languageManager,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->viewBuilder = $entityTypeManager->getViewBuilder('pagedesigner_element');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
          $configuration,
          $plugin_id,
          $plugin_definition,
          $container->get('plugin.manager.ui_patterns'),
          $container->get('pagedesigner.service.element_handler'),
          $container->get('renderer'),
          $container->get('entity_type.manager'),
          $container->get('language_manager'),
      );
  }

  /**
   * {@inheritdoc}
   */
  public function collectAttachments(array &$attachments) {

  }

  /**
   * {@inheritdoc}
   */
  public function collectPatterns(array &$patterns, bool $render_markup = FALSE) {

  }

  /**
   * {@inheritdoc}
   */
  public function adaptPatterns(array &$patterns) {

  }

  /**
   * {@inheritdoc}
   */
  public function prepare(PatternDefinitionField &$field, array &$fieldArray) {

  }

  /**
   * {@inheritdoc}
   */
  public function get(Element $entity, string &$result = '') {
    if ($entity->hasField('field_content')) {
      $result .= (string) $entity->field_content->value;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getContent(Element $entity, array &$list = [], $published = TRUE) {
    $entity = ($published) ? $entity->loadNewestPublished() : $entity;
    if ($entity != NULL) {
      $value = $this->elementHandler->get($entity);
      $list[(string) $entity->id()] = $value;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function patch(Element $entity, array $data) {
    if (isset($data[0]) && \is_scalar($data[0])) {
      $entity->field_content->value = (string) $data[0];
      $entity->saveEdit();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function generate($definition, array $data, ?Element &$entity = NULL) {
    $type = $definition['type'];
    $name = (!empty($definition['name'])) ? $definition['name'] : $definition['type'];
    $entity = Element::create(
      [
        'type' => $type,
        'name' => $name,
        'parent' => ['target_id' => $data['parent']],
        'container' => ['target_id' => $data['container']],
        'entity' => ['target_id' => $data['entity']],
      ]
    );
  }

  /**
   * {@inheritdoc}
   */
  public function copy(Element $entity, ?Element $container = NULL, ?Element &$clone = NULL) {
    $clone = $entity->createDuplicate();
    $clone->container->entity = $container;
    $clone->langcode->value = $entity->langcode->value;
    if ($container != NULL) {
      $clone->entity->entity = $container->entity->entity;
      $clone->langcode->value = $container->langcode->value;
    }
    else {
      $clone->entity->entity = NULL;
    }

    $children = [];
    foreach ($entity->children as $item) {
      if ($item->entity != NULL) {
        $child = $this->elementHandler->copy($item->entity, $container);
        $child->langcode->value = $clone->langcode->value;
        $child->container->entity = $container;
        if ($container != NULL) {
          $child->entity->entity = $container->entity->entity;
        }
        else {
          $child->entity->entity = NULL;
        }
        $child->parent->entity = $clone;
        $child->setPublished(FALSE);
        $child->save();
        $children[] = $child->id();
      }
    }
    $clone->children->setValue($children);
    $clone->setPublished(FALSE);
    $clone->save();
  }

  /**
   * {@inheritdoc}
   */
  public function delete(Element $entity, bool $remove = FALSE) {
    foreach ($entity->children as $item) {
      if ($item->entity != NULL) {
        $this->elementHandler->delete($item->entity, $remove);
      }
    }
    if ($remove) {
      $entity->delete();
    }
    else {
      $entity->deleted->value = TRUE;
      $entity->status->value = FALSE;
      $entity->saveEdit();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function restore(Element $entity) {
    foreach ($entity->children as $item) {
      if ($item->entity != NULL) {
        $this->elementHandler->restore($item->entity);
      }
    }
    $entity->deleted->value = FALSE;
    $entity->save();
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->pluginDefinition['name'];
  }

  /**
   * {@inheritdoc}
   */
  public function view(Element $entity, string $view_mode, array &$build = []) {
  }

  /**
   * {@inheritdoc}
   */
  public function build(Element $entity, string $view_mode, array &$build = []) {
    $value = $this->get($entity);
    $build = ['#markup' => $value, '#pagedesigner_element' => $entity] + $build;
  }

  /**
   * {@inheritdoc}
   */
  public function serialize(Element $entity, array &$result = []) {
    $result = ['entity' => $entity] + $result;
  }

  /**
   * {@inheritdoc}
   */
  public function describe(Element $entity, array &$result = []) {
    $result['id'] = $entity->id();
    $result['parent'] = $entity->parent->target_id;
    $result['container'] = $entity->container->target_id;
    $result['entity'] = $entity->entity->target_id;
  }

  /**
   * {@inheritdoc}
   */
  public function publish(Element $entity) {
    foreach ($entity->children as $item) {
      $this->elementHandler->publish($item->entity);
    }
    if (!$entity->isPublished()) {
      $entity->setPublished(TRUE);
      $entity->save();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function unpublish(Element $entity) {
    foreach ($entity->children as $item) {
      $this->elementHandler->unpublish($item->entity);
    }
    if ($entity->isPublished()) {
      $entity->setPublished(FALSE);
      $entity->save();
    }
  }

  /**
   * Returns the definition of the specified pattern.
   *
   * @param string $id
   *   The id of the pattern.
   *
   * @return \Drupal\ui_patterns\Definition\PatternDefinition|null
   *   The pattern or null.
   */
  protected function getPatternDefinition($id) {
    if (self::$patternDefinitions == NULL) {
      self::$patternDefinitions = $this->patternManager->getDefinitions();
      $this->elementHandler->collectPatterns(self::$patternDefinitions);
    }
    if (!empty(self::$patternDefinitions[$id])) {
      return self::$patternDefinitions[$id];
    }
    return NULL;
  }

}
