<?php

namespace Drupal\nodehive_core\Form;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Form\FormStateInterface;

/**
 * Form controller for the space entity edit forms.
 */
class SpaceForm extends ContentEntityForm {

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);
    $entity = $this->getEntity();
    $is_new = $entity->isNew();

    // Add wrapper class for styling.
    $form['#attributes']['class'][] = 'space-form';

    // Hide thumbnail and tags.
    if (isset($form['thumbnail'])) {
      $form['thumbnail']['#access'] = FALSE;
    }
    if (isset($form['tags'])) {
      $form['tags']['#access'] = FALSE;
    }

    // Hide internal reference fields.
    if (isset($form['menu'])) {
      $form['menu']['#access'] = FALSE;
    }

    // Basic Information at top.
    $form['basic_info'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['space-form__basic']],
      '#weight' => -100,
    ];

    $form['basic_info']['label'] = $form['label'];
    $form['basic_info']['label']['widget'][0]['value']['#title'] = $this->t('Space');
    unset($form['label']);

    // Machine name: auto-generated on add, read-only on edit.
    $machine_name_value = $entity->isNew() ? '' : ($entity->get('machine_name')->value ?? '');
    if ($is_new) {
      $form['basic_info']['machine_name'] = [
        '#type' => 'machine_name',
        '#title' => $this->t('Machine name'),
        '#description' => $this->t('A unique machine-readable name for this space. Auto-generated from the label, but can be customised.'),
        '#default_value' => $machine_name_value,
        '#machine_name' => [
          'exists' => [$this, 'machineNameExists'],
          'source' => ['basic_info', 'label', 'widget', 0, 'value'],
        ],
        '#required' => TRUE,
        '#weight' => -4,
      ];
    }
    else {
      $form['machine_name_value'] = [
        '#type' => 'hidden',
        '#value' => $machine_name_value,
      ];
    }
    $form['#entity_builders'][] = '::buildMachineName';
    unset($form['machine_name']);

    // Vertical tabs container.
    $form['tabs'] = [
      '#type' => 'vertical_tabs',
      '#weight' => -50,
    ];

    // General tab.
    $form['tab_general'] = [
      '#type' => 'details',
      '#title' => $this->t('General'),
      '#group' => 'tabs',
    ];

    $form['tab_general']['space_type'] = $form['space_type'];
    $form['tab_general']['space_type']['widget']['#description'] = $this->t('Select the type of space.');
    unset($form['space_type']);

    $form['tab_general']['space_url'] = $form['space_url'];
    $form['tab_general']['space_url']['widget'][0]['uri']['#description'] = $this->t('The public URL where this space is accessible (e.g., https://example.com).');
    unset($form['space_url']);

    $form['tab_general']['status'] = $form['status'];
    unset($form['status']);

    // Frontpage tab.
    $form['tab_frontpage'] = [
      '#type' => 'details',
      '#title' => $this->t('Frontpage'),
      '#group' => 'tabs',
    ];

    $form['tab_frontpage']['frontpage_node'] = $form['frontpage_node'];
    $form['tab_frontpage']['frontpage_node']['widget'][0]['target_id']['#description'] = $this->t('Select the node to use as the frontpage for this space.');
    unset($form['frontpage_node']);

    $form['tab_frontpage']['frontpage_login_url'] = $form['frontpage_login_url'];
    unset($form['frontpage_login_url']);

    // Users tab.
    $form['tab_users'] = [
      '#type' => 'details',
      '#title' => $this->t('Users'),
      '#description' => $this->t('Users who have access to manage content in this space.'),
      '#group' => 'tabs',
    ];

    $form['tab_users']['editors'] = $form['editors'];
    unset($form['editors']);

    // Content Types tab.
    $form['tab_content_types'] = [
      '#type' => 'details',
      '#title' => $this->t('Content Types'),
      '#description' => $this->t('Select which content types are allowed in this space. Leave empty to allow all types.'),
      '#group' => 'tabs',
    ];

    $form['tab_content_types']['content_types'] = $form['content_types'];
    unset($form['content_types']);

    // Deployment tab.
    $form['tab_deployment'] = [
      '#type' => 'details',
      '#title' => $this->t('Deployment'),
      '#group' => 'tabs',
    ];

    if (isset($form['git_repository'])) {
      $form['tab_deployment']['git_repository'] = $form['git_repository'];
      $form['tab_deployment']['git_repository']['widget'][0]['value']['#description'] = $this->t('The Git repository URL (e.g., https://github.com/org/repo).');
      unset($form['git_repository']);
    }

    if (isset($form['hosting_provider'])) {
      $form['tab_deployment']['hosting_provider'] = $form['hosting_provider'];
      unset($form['hosting_provider']);
    }

    // Dashboard views tab (if exists).
    if (isset($form['nodehive_space_views'])) {
      $form['tab_dashboard'] = [
        '#type' => 'details',
        '#title' => $this->t('Dashboard'),
        '#group' => 'tabs',
      ];
      $form['tab_dashboard']['nodehive_space_views'] = $form['nodehive_space_views'];
      unset($form['nodehive_space_views']);
    }

    // Attach library for styling.
    $form['#attached']['library'][] = 'nodehive_core/space_form';

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {
    $result = parent::save($form, $form_state);

    $entity = $this->getEntity();

    $message_arguments = ['%label' => $entity->toLink()->toString()];
    $logger_arguments = [
      '%label' => $entity->label(),
      'link' => $entity->toLink($this->t('View'))->toString(),
    ];

    switch ($result) {
      case SAVED_NEW:
        // Generate api key.
        $key = bin2hex(random_bytes(32));
        $entity->apikey->value = $key;
        $entity->save();

        $this->messenger()->addStatus($this->t('New space %label has been created.', $message_arguments));
        $this->logger('nodehive_core')->notice('Created new space %label', $logger_arguments);
        break;

      case SAVED_UPDATED:
        $this->messenger()->addStatus($this->t('The space %label has been updated.', $message_arguments));
        $this->logger('nodehive_core')->notice('Updated space %label.', $logger_arguments);
        break;
    }

    $tags = $entity->getCacheTags();
    $node_ids = $this->entityTypeManager->getStorage('node')
      ->getQuery()
      ->accessCheck(FALSE)
      ->condition('nodehive_space', $entity->id())
      ->execute();

    /** @var \Drupal\node\NodeInterface $item */
    foreach ($this->entityTypeManager->getStorage('node')->loadMultiple($node_ids) as $item) {
      $tags = array_merge($tags, $item->getCacheTags());
    }

    $tags[] = "config:system.menu.nodehive-editor";
    $tags[] = "config:system.menu.nodehive-administrator";
    Cache::invalidateTags($tags);
    $form_state->setRedirect('entity.nodehive_space.canonical', ['nodehive_space' => $entity->id()]);

    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    // The machine_name base field uses a string_textfield widget that expects
    // its form-state value as [{value: ...}]. The form replaces that widget
    // with a custom #type => 'machine_name' render element which submits a
    // plain string. Coerce it before parent::validateForm() calls
    // extractFormValues(), otherwise WidgetBase hits "Cannot unset string
    // offsets" when trying to remove the 'add_more' key from a string.
    $raw_machine_name = $form_state->getValue('machine_name');
    if (is_string($raw_machine_name)) {
      $form_state->setValue('machine_name', [['value' => $raw_machine_name]]);
    }

    parent::validateForm($form, $form_state);

    $entity = $this->getEntity();
    if ($entity->isNew() && is_numeric($raw_machine_name)) {
      $form_state->setErrorByName('machine_name', $this->t('The machine name must not be purely numeric, as numeric values are reserved for space IDs.'));
    }
    return $entity;
  }

  /**
   * Checks whether a machine name is already in use by another space.
   */
  public function machineNameExists(string $machine_name, array $element, FormStateInterface $form_state): bool {
    return (bool) $this->entityTypeManager->getStorage('nodehive_space')
      ->getQuery()
      ->condition('machine_name', $machine_name)
      ->accessCheck(FALSE)
      ->execute();
  }

  /**
   * Entity builder: maps the machine_name render element value onto the entity.
   */
  public function buildMachineName(string $entity_type_id, ContentEntityInterface $entity, array &$form, FormStateInterface $form_state): void {
    $value = $form_state->getValue('machine_name') ?? $form_state->getValue('machine_name_value');
    $entity->set('machine_name', $value);
  }

}
