<?php

namespace Drupal\castorcito;

use Drupal\castorcito\CastorcitoComponentFieldManager;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileUrlGenerator;
use Drupal\Core\Form\FormState;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\Core\Database\Connection;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\user\PermissionHandlerInterface;
use Drupal\Component\Utility\Crypt;

/**
 * Provides management functionalities for Castorcito.
 *
 * @package Drupal\CastorcitoManager
 */
class CastorcitoManager {

  /**
   * The entity field manager.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $entityFieldManager;

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

  /**
   * The block manager.
   *
   * @var \Drupal\Core\Block\BlockManagerInterface
   */
  protected $blockManager;

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

  /**
   * The permission handler.
   *
   * @var \Drupal\user\PermissionHandlerInterface
   */
  protected $permissionHandler;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * The current database connection.
   *
   * @var Drupal\Core\Database\Connection
   */
  protected $connection;

  /**
   * The component field manager.
   *
   * @var \Drupal\castorcito\CastorcitoComponentFieldManager
   */
  protected $componentFieldManager;

  /**
   * Constructs a CastorcitoManager.
   *
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
   *   The entity field manager.
   * @param Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The The entity type manager.
   * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
   *   The block manager.
   * @param \Drupal\Core\File\FileUrlGenerator $file_url_generator
   *   The file url generator.
   * @param \Drupal\user\PermissionHandlerInterface $permission_handler
   *   The permission handler.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   The current user service.
   * @param Drupal\Core\Database\Connection $connection
   *   The current database connection.
   * @param \Drupal\castorcito\CastorcitoComponentFieldManager $component_field_manager
   *   The castorcito component field manager service.
   */
  public function __construct(
    EntityFieldManagerInterface $entity_field_manager,
    EntityTypeManagerInterface $entity_type_manager,
    BlockManagerInterface $block_manager,
    FileUrlGenerator $file_url_generator,
    PermissionHandlerInterface $permission_handler,
    AccountProxyInterface $current_user,
    Connection $connection,
    CastorcitoComponentFieldManager $component_field_manager
  ) {
    $this->entityFieldManager = $entity_field_manager;
    $this->entityTypeManager = $entity_type_manager;
    $this->blockManager = $block_manager;
    $this->fileUrlGenerator = $file_url_generator;
    $this->permissionHandler = $permission_handler;
    $this->currentUser = $current_user;
    $this->connection = $connection;
    $this->componentFieldManager = $component_field_manager;
  }

  /**
   * List all components as options.
   *
   * @return: Array.
   *   An array of component options.
   */
  public function castorcitoComponentsOptions(): array {
    $options = [];
    $components = $this->castorcitoComponents();
    foreach ($components as $component) {
      if (!$component->get('inside_container')) {
        $options[$component->id()] = $component->label();
      }
    }

    return $options;
  }

  /**
   * List all components.
   *
   * @param array|null $ids
   *   An array of component IDs or NULL to load all components.
   *
   * @return array
   *   An array of loaded components.
   */
  public function castorcitoComponents(?array $ids = NULL): array {
    $components = $this->entityTypeManager->getStorage('castorcito_component')->loadMultiple($ids);

    return $components;
  }

  /**
   * Load a component by its ID.
   *
   * @param $id
   *   The ID of the component to load.
   *
   * @return \Drupal\Core\Entity\EntityInterface|NULL
   *   The loaded component, or NULL if no component is found with the given ID.
   */
  public function castorcitoComponent($id) {
    return $this->entityTypeManager->getStorage('castorcito_component')->load($id);
  }

  /**
   * Retrieves a list of components with their field settings.
   *
   * @param array $ids
   *   An array of component IDs to fetch.
   *
   * @return array
   *   An associative array containing:
   *   - 'components': An array of component models.
   *   - 'components_fields_settings': An array of field configuration settings.
   */
  public function getCastorcitoComponents(array $ids): array {
    $components = [];
    $components_fields_settings = [];

    if ($all_components = $this->castorcitoComponents($ids)) {
      foreach ($all_components as $key => $component) {
        $components[$key] = $component->getModel();
        $components[$key]['label'] = $component->label();
        $components_fields_settings[$key]['fields'] = $this->processCfieldsSettings($component->get('field_configuration'));
        $components_fields_settings[$key]['order_fields'] = array_keys($components[$key]['fields']);
        $components_fields_settings[$key]['settings'] = $component->get('form_settings');
      }
    }

    $allowed_fields = [
      'container',
      'advanced_container',
    ];

    if (!empty($components_fields_settings)) {
      foreach ($components_fields_settings as $fields_settings) {
        foreach ($fields_settings['fields'] as $field) {
          if (in_array($field['id'], $allowed_fields)) {
            $child_ids = [];

            if (!empty($field['settings']['allowed_children'])) {
              $child_ids = array_keys($field['settings']['allowed_children']);
            }

            if (!empty($field['settings']['head_component'])) {
              $child_ids[] = $field['settings']['head_component'];
            }

            if (!empty($child_ids)) {
              $child_components = $this->getCastorcitoComponents($child_ids);
              $components += $child_components['components'];
              $components_fields_settings += $child_components['components_fields_settings'];
            }
          }
        }
      }
    }

    return [
      'components' => $components,
      'components_fields_settings' => $components_fields_settings,
    ];
  }

  /**
   * Function to add url autocomplete on cfields settings.
   *
   * @param array $cfields_settings
   *   The cfields configuration.
   * @return array $cfields_settings
   *   An array with the url_autocomplete link ot entity_reference cfield type
   */
  protected function processCfieldsSettings(array $cfields_settings): array {
    $url_autocomplete = $this->entityAutocompleteUrl('node');
    $cfields_autocomplete = [
      'link',
      'entity_reference',
      'webform',
    ];
    foreach ($cfields_settings as $ckey => $cfield) {
      if (!in_array($cfield['id'], $cfields_autocomplete)) {
        continue;
      }

      if ($cfield['id'] === 'entity_reference') {
        $entity_type = $cfield['settings']['entity_type'];
        $target_bundles = $cfield['settings']['entity_bundle'];
        $selection_settings['auto_create'] = '';
        $selection_settings['match_operator'] = 'CONTAINS';
        $selection_settings['match_limit'] = 10;
        $selection_settings['target_bundles'] = '';
        $selection_settings['sort'] = [
          'field' => '_none',
          'direction' => 'ASC',
        ];

        if ($this->isBundleEntityType($entity_type)) {
          $selection_settings['target_bundles'] = $target_bundles;
          $selection_settings['auto_create_bundle'] = reset($target_bundles);
        }

        $selection_handler = 'default:' . $entity_type;
        $url_autocomplete = $this->entityAutocompleteUrl($entity_type, $selection_handler, $selection_settings);
      }

      if ($cfield['id'] === 'webform') {
        $url_autocomplete = $this->entityAutocompleteUrl('webform');
      }

      $cfields_settings[$ckey]['settings']['url_autocomplete'] = $url_autocomplete;
    }

    return $cfields_settings;
  }

  /**
   * Selected components.
   *
   * @param array $components
   *   The configuration array of components.
   *
   * @return array
   *   An array of selected components.
   */
  public function selectedComponents(array $components): array {
    $selected_components = [];
    foreach ($components as $component) {
      if ($component !== 0) {
        array_push($selected_components, $component);
      }
    }

    return $selected_components;
  }

  /**
   * Retrieves all block definitions available in the system.
   *
   * This function returns an array of block definitions, which includes
   * metadata for each block, such as its plugin ID, label, provider, and more.
   *
   * @return array
   *   An associative array of block definitions, keyed by plugin ID.
   */
  public function getBlockDefinitions() {
    return $this->blockManager->getDefinitions();
  }

  /**
   * {@inheritdoc}
   */
  public function entityAutocompleteUrl($target_type, $selection_handler = 'default', $selection_settings = []): string {
    $data = serialize($selection_settings) . $target_type . $selection_handler;
    $selection_settings_key = Crypt::hmacBase64($data, Settings::getHashSalt());

    $key_value_storage = \Drupal::keyValue('entity_autocomplete');
    if (!$key_value_storage->has($selection_settings_key)) {
      $key_value_storage->set($selection_settings_key, $selection_settings);
    }

    $routes_parameters = [
      'target_type' => $target_type,
      'selection_handler' => $selection_handler,
      'selection_settings_key' => $selection_settings_key,
    ];

    $url = Url::fromRoute('system.entity_autocomplete', $routes_parameters)->getInternalPath();

    return $url;
  }

  /**
   * {@inheritdoc}
   */
  public function generalUrls(): array {
    $url_private_base = Url::fromRoute('system.files', [], ['absolute' => TRUE])->toString();
    $urls['url_token'] = Url::fromRoute('system.csrftoken')->toString(TRUE)->getGeneratedUrl();
    $urls['url_public'] = $this->fileUrlGenerator->generateAbsoluteString('public://');
    $urls['url_private'] = rtrim($url_private_base, '/') . '/';
    $urls['url_upload_image'] = Url::fromRoute('rest.castorcito_ajax_upload_image_resource.POST')->toString(TRUE)->getGeneratedUrl();
    $urls['url_list_block'] = Url::fromRoute('rest.castorcito_block_list_resource.GET')->toString(TRUE)->getGeneratedUrl();

    return $urls;
  }

  /**
   * {@inheritdoc}
   */
  public function getUserPermissions() {
    $user_permissions = $this->permissionHandler->getPermissions();
    $user_castorcito_permissions = array_filter($user_permissions, function ($permission, $key) {
      if ($permission['provider'] == 'castorcito') {
        return $this->currentUser->hasPermission($key);
      }
    }, ARRAY_FILTER_USE_BOTH);

    return array_keys($user_castorcito_permissions);
  }

  /**
   * Function to validate if component is usage.
   *
   * @param string $component_id
   * @return int 1 or 0
   */
  public function componentUsage(string $component_id): int {
    $json_fields = $this->entityTypeManager->getStorage('field_storage_config')->loadByProperties([
      'module' => 'json_field',
    ]);

    foreach ($json_fields as $field) {
      $table = $field->getTargetEntityTypeId() . '__' . $field->getName();
      $column = $field->getName() . '_value';
      $query = $this->connection->select($table, 't');
      $query->fields('t', [$column]);
      $query->where($column . ' LIKE :component_id', [':component_id' => '%' . $component_id . '%']);
      $results = $query->execute()->fetchAll();
      if (!empty($results)) {
        return TRUE;
      }
    }

    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function isBundleEntityType($entity_type) {
    $entity_type_definition = $this->entityTypeManager->getDefinition($entity_type);

    return $entity_type_definition->getBundleEntityType();
  }

  /**
   * Creates a new instance of the specified component field plugin.
   *
   * @param string $plugin_id
   *   The plugin ID of the field component to instantiate.
   *
   * @return \Drupal\castorcito\CastorcitoComponentFieldBase
   *   The created instance of the field component plugin.
   */
  public function getComponentFieldInstance($plugin_id) {
    return $this->componentFieldManager->createInstance($plugin_id);
  }

  /**
   * Retrieves the configuration form for a Castorcito component field instance.
   *
   * @param \Drupal\castorcito\CastorcitoComponentFieldBase $instance
   *   The instantiated Castorcito component field plugin whose configuration
   *   form should be retrieved.
   *
   * @return array
   *   A renderable form array containing the current configuration form
   *   for the specified plugin instance.
   */
  public function getComponentFieldInstanceConfigurationForm($instance) {
    $form = [];
    $form_state = new FormState();
    return $instance->buildConfigurationForm($form, $form_state);
  }

}
