<?php

namespace Drupal\user_provisioning\Form;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Url;
use Drupal\user_provisioning\Helpers\moUserProvisioningLogger;
use Drupal\user_provisioning\moUserProvisioningConstants;
use Drupal\user_provisioning\moUserProvisioningOperationsHandler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Manual provisioning sync form.
 */
class MoUserProvisioningManualSync extends FormBase {

  /**
   * {@inheritDoc}
   */
  public function getFormId(): string {
    return 'mo_provisioning_manual_sync';
  }

  /**
   * The base URL.
   *
   * @var string
   *   The base URL.
   */
  private string $baseUrl;

  /**
   * The config object.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   *   The config object.
   */
  private ImmutableConfig $config;

  /**
   * Logger for user provisioning operations.
   *
   * @var \Drupal\user_provisioning\Helpers\moUserProvisioningLogger
   *   The provisioning logger instance.
   */
  private moUserProvisioningLogger $moLogger;

  /**
   * The current request.
   *
   * @var \Symfony\Component\HttpFoundation\Request
   *   The current request object.
   */
  private Request $request;

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

  /**
   * Constructs the manual sync form.
   */
  public function __construct(
    ConfigFactoryInterface $config_factory,
    MessengerInterface $messenger,
    RequestStack $request_stack,
    EntityTypeManagerInterface $entity_type_manager,
  ) {
    $this->requestStack = $request_stack;
    $this->request = $this->requestStack->getCurrentRequest();
    $this->baseUrl = $this->request->getSchemeAndHttpHost() . $this->request->getBaseUrl();
    $this->config = $config_factory->get('user_provisioning.settings');
    $this->messenger = $messenger;
    $this->entityTypeManager = $entity_type_manager;
    $this->moLogger = new moUserProvisioningLogger();
  }

  /**
   * Creates the class instance.
   */
  public static function create(ContainerInterface $container): MoUserProvisioningManualSync|static {
    return new static(
      $container->get('config.factory'),
      $container->get('messenger'),
      $container->get('request_stack'),
      $container->get('entity_type.manager'),
    );
  }

  /**
   * {@inheritDoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['#prefix'] = '<div id="modal_example_form">';
    $form['#suffix'] = '</div>';
    $form['status_messages'] = [
      '#type' => 'status_messages',
      '#weight' => -10,
    ];

    $manual_prov = $this->config->get('manual_provisioning');
    $create_user = $this->config->get('mo_user_provisioning_create_user');

    if ($manual_prov && $create_user) {

      $form['mo_user_prov_manual_sync'] = [
        '#type' => 'table',
        '#responsive' => TRUE,
      ];

      $form['mo_user_prov_manual_sync']['sync_table'] = $this->moProvisioningManualProvTable();
    }
    else {
      $form['mo_user_prov_manual_sync'] = [
        '#markup' => $this->t('It seems that you have not checked the <strong>Manual/On-Demand Provisioning</strong> or <strong>Create user</strong> checkbox. You can enable it in the <a href="provisioning?summary=configuration">Edit Configurations</a> section and then perform the Manual Sync.'),
      ];
    }

    $form['actions'] = ['#type' => 'actions'];

    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';

    return $form;
  }

  /**
   * {@inheritDoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // @todo Implement submitForm() method.
  }

  /**
   * Builds the manual provisioning table row.
   */
  private function moProvisioningManualProvTable(): array {
    $row['drupal_username'] = [
      '#markup' => $this->t('<b>Enter the Drupal Username:</b>'),
    ];

    $row['search_user'] = [
      '#type' => 'entity_autocomplete',
      '#target_type' => 'user',
    ];

    $row['actions']['send'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#attributes' => [
        'class' => [
          'use-ajax',
          'button--primary',
        ],
      ],
      '#ajax' => [
        'callback' => [$this, 'performProvisionOnDemand'],
        'event' => 'click',
      ],
      '#states' => ['disabled' => [':input[name = "mo_user_prov_manual_sync[sync_table][search_user]"]' => ['value' => '']]],
    ];

    return $row;
  }

  /**
   * Performs on-demand provisioning for the selected entity.
   */
  public function performProvisionOnDemand(array &$form, FormStateInterface $form_state) {

    // @todo Provision user or role on-demand
    $response = new AjaxResponse();
    $referer = $this->requestStack->getCurrentRequest()->headers->get('referer');

    $redirect_to = !empty($referer) ? $referer : Url::fromRoute('user_provisioning.provisioning')->toString();

    if ($form_state->hasAnyErrors()) {
      $response->addCommand(new ReplaceCommand('#modal_support_form', $form));
    }
    else {
      $entity_name = trim((string) $form_state->getValues()['mo_user_prov_manual_sync']['sync_table']['search_user']);

      $this->moLogger->addLog('Provisioning on demand. Entered entity name is :' . $entity_name, __LINE__, __FUNCTION__, __FILE__);
      $entity = $this->entityTypeManager->getStorage('user')->load($entity_name);

      if (is_null($entity)) {
        $this->messenger->addError($this->t('"@entity" cannot be recognized as a valid User or Role. Enter a valid entity (User or Role) to provision.', ['@entity' => $entity_name]));
        $response->addCommand(new RedirectCommand($redirect_to));
        return $response;
      }

      $mo_entity_handler = new moUserProvisioningOperationsHandler($entity);

      try {
        $result = $mo_entity_handler->insert();
        if (is_null($result)) {
          $this->messenger->addError($this->t('An error occurred while provisioning the user, please refer to <a href=":logs_url">drupal logs</a> for more information.', [':logs_url' => $this->baseUrl . moUserProvisioningConstants::DRUPAL_LOGS_PATH]));
          $response->addCommand(new RedirectCommand($redirect_to));
          return $response;
        }
        $this->messenger->addMessage($this->t('@entity successfully created at the configured application.', ['@entity' => $entity->label()]));
      }
      catch (\Exception $exception) {
        if ($exception->getCode() == moUserProvisioningConstants::STATUS_CONFLICT) {
          $this->messenger->addError($this->t('The application has returned 409 conflict i.e., the user already exists at the configured application.'));
        }
        else {
          $this->messenger->addError($exception->getMessage());
        }
        // @todo PUT operation will be called following this event to update the user fields.
      }
    }
    $response->addCommand(new RedirectCommand($redirect_to));
    return $response;
  }

}
