<?php

namespace Drupal\castorcito_sync\Form;

use Drupal\Core\Archiver\ArchiveTar;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Serialization\Yaml;
use Drupal\system\FileDownloadController;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines the CastorcitoSyncExportForm export form.
 *
 * @internal
 */
class CastorcitoSyncExportForm extends FormBase {

  /**
   * The export storage.
   *
   * @var \Drupal\Core\Config\StorageInterface
   */
  protected $exportStorage;

  /**
   * The file system.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected $fileSystem;

  /**
   * The castorcito component entity storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $componentStorage;

  /**
   * Constructs a CastorcitoExportController object.
   *
   * @param \Drupal\Core\File\FileSystemInterface $file_system
   *   The file system.
   * @param \Drupal\Core\Config\StorageInterface $export_storage
   *   The export storage.
   * @param \Drupal\Core\Entity\EntityStorageInterface $component_storage
   *   The castorcito component entity storage.
   */
  public function __construct(FileSystemInterface $file_system, StorageInterface $export_storage, EntityStorageInterface $component_storage) {
    $this->fileSystem = $file_system;
    $this->exportStorage = $export_storage;
    $this->componentStorage = $component_storage;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('file_system'),
      $container->get('config.storage.export'),
      $container->get('entity_type.manager')->getStorage('castorcito_component'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'castorcito_sync_export_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['markup'] = [
      '#markup' => $this->t('Export all Castorcito components or select the required.'),
    ];

    $form['export'] = [
      '#type' => 'radios',
      '#title' => $this->t('Choose your option to export'),
      '#default_value' => 0,
      '#required' => TRUE,
      '#options' => [
        'all' => $this->t('All'),
        'select' => $this->t('Select'),
      ],
    ];

    $form['components'] = [
      '#type' => 'checkboxes',
      '#options' => $this->loadedComponents(),
      '#title' => $this->t('Components'),
      '#states' => [
        'visible' => [
          ':input[name="export"]' => ['value' => 'select'],
        ],
      ],
    ];

    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Export'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $export = $form_state->getValue('export');
    $components = array_filter($form_state->getValue('components'));

    if ($export === 'select' && empty($components)) {
      $form_state->setErrorByName('components', $this->t('You must select at least one component.'));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    try {
      $this->fileSystem->delete($this->fileSystem->getTempDirectory() . '/config.tar.gz');
    }
    catch (FileException) {
      // Ignore failed deletes.
    }

    $export = $form_state->getValue('export');
    if ($export === 'select') {
      $export_config_names = $this->getExportComponents(array_filter($form_state->getValue('components')));
    }
    else {
      $export_config_names = $this->allExportComponents();
    }

    $archiver = new ArchiveTar($this->fileSystem->getTempDirectory() . '/config.tar.gz', 'gz');

    foreach ($export_config_names as $name) {
      $yml = $this->exportStorage->read($name);
      $archiver->addString("$name.yml", Yaml::encode($yml));
    }

    $form_state->setRedirect('castorcito_sync.export_download');
  }

  /**
   * Loads and returns the list of component labels.
   *
   * @return array
   *   An associative array of component IDs and their labels.
   */
  private function loadedComponents() {
    $components = [];

    foreach ($this->componentStorage->loadMultiple() as $id => $component) {
      $components[$id] = $component->label();
    }

    return $components;
  }

  /**
   * Gets all exportable components.
   *
   * @return array
   *   An associative array of configuration names.
   */
  private function allExportComponents() {
    $components = $this->componentStorage->loadMultiple();
    $export_components = [];

    foreach ($components as $component) {
      $category_config = 'castorcito.castorcito_category.' . $component->get('category');
      $component_config = 'castorcito.component.' . $component->id();

      $export_components[$category_config] = $category_config;
      $export_components[$component_config] = $component_config;
    }

    return $export_components;
  }

  /**
   * Get the selected components and dependencies.
   *
   * @param array $cids
   *   An array of ids of the components to be exported.
   *
   * @return array
   *   An array with the names of the components and dependencies to be exported.
   */
  private function getExportComponents(array $cids) {
    $components = $this->componentStorage->loadMultiple($cids);
    $export_components = [];

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

    foreach ($components as $component) {
      $category_config = 'castorcito.castorcito_category.' . $component->get('category');
      $component_config = 'castorcito.component.' . $component->id();

      $export_components[$category_config] = $category_config;
      $export_components[$component_config] = $component_config;

      $field_configuration = $component->get('field_configuration') ?? [];

      foreach ($field_configuration as $cfield) {
        if (!isset($cfield['id']) || !in_array($cfield['id'], $allowed_field_ids, true)) {
          continue;
        }

        $child_ids = [];

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

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

        if (!empty($child_ids)) {
          $child_export_components = $this->getExportComponents($child_ids);
          $export_components += $child_export_components;
        }
      }
    }

    return $export_components;
  }

}
