<?php

namespace Drupal\keycloak_user_provisioning\Form;

use Drupal\Component\Serialization\Json;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\user_provisioning\Helpers\moUserProvisioningLogger;
use Drupal\user_provisioning\moUserProvisioningConstants;
use Drupal\user_provisioning\moUserProvisioningEntityFactory;
use Drupal\user_provisioning\moUserProvisioningOperationsHandler;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\Core\Entity\EntityTypeManagerInterface;

/**
 * Provides a form for keycloak provisioning configuration.
 */
class MoKeycloakProvisioningConfiguration extends FormBase {
  use StringTranslationTrait;
  /**
   * Base URL of the site.
   *
   * @var string
   */
  private $baseUrl;

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

  /**
   * Config property.
   *
   * @var \Drupal\Core\Config\ConfigInterface
   */
  protected $configFactory;

  /**
   * Messenger property.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactoryService;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * The extension list module service.
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  protected $moduleExtensionList;

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

  /**
   * URL Path property.
   *
   * @var string
   */
  private $urlPath;

  /**
   * Logger property.
   *
   * @var Drupal\user_provisioning\Helpers\moUserProvisioningLogger
   */
  private moUserProvisioningLogger $moLogger;

  /**
   * Constructs a new MoKeycloakProvisioningConfiguration object.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger service.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   * @param \Drupal\Core\Extension\ModuleExtensionList $module_extension_list
   *   The module extension list service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(ConfigFactoryInterface $config_factory, MessengerInterface $messenger, RequestStack $request_stack, ModuleExtensionList $module_extension_list, EntityTypeManagerInterface $entity_type_manager) {
    $base_url = $request_stack->getCurrentRequest()->getSchemeAndHttpHost();
    $this->baseUrl = $base_url;
    $this->configFactoryService = $config_factory;
    $this->config = $config_factory->get('keycloak_user_provisioning.settings');
    $this->configFactory = $config_factory->getEditable('keycloak_user_provisioning.settings');
    $this->messenger = $messenger;
    $this->requestStack = $request_stack;
    $this->moduleExtensionList = $module_extension_list;
    $this->entityTypeManager = $entity_type_manager;
    $this->urlPath = $base_url . '/' . $module_extension_list->getPath('keycloak_user_provisioning') . '/includes';
    $this->moLogger = new moUserProvisioningLogger();
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('config.factory'),
      $container->get('messenger'),
      $container->get('request_stack'),
      $container->get('extension.list.module'),
      $container->get('entity_type.manager')
    );
  }

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

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['#prefix'] = '<div id="keycloak_provisioning_configuration">';
    $form['#suffix'] = '</div>';

    $form['status_messages'] = [
      '#type' => 'status_messages',
      '#weight' => -10,
    ];

    $form['markup_library'] = [
      '#attached' => [
        'library' => [
          'keycloak_user_provisioning/keycloak_user_provisioning.admin',
          'keycloak_user_provisioning/keycloak_user_provisioning.keycloak_test',
          'core/drupal.dialog.ajax',
        ],
      ],
    ];

    $request = $this->requestStack->getCurrentRequest();
    $provisioning_type_url = $request ? $request->getRequestUri() : '';

    if (strpos($provisioning_type_url, 'manualConfiguration') !== FALSE) {
      $this->manualConfiguration($form, $form_state);
    }
    elseif (strpos($provisioning_type_url, 'automaticConfiguration') !== FALSE) {
      $this->automaticConfiguration($form, $form_state);
    }
    elseif (strpos($provisioning_type_url, 'testManualSync') !== FALSE || strpos($_SERVER['REQUEST_URI'], 'syncUserManually') !== FALSE) {
      $this->testManualSync($form, $form_state);
    }
    elseif (strpos($provisioning_type_url, 'attributeListOfKeycloak') !== FALSE) {
      $this->displayKeycloakAttributes($form, $form_state);
    }

    return $form;
  }

  /**
   * Manual configuration form elements.
   */
  public function manualConfiguration(array &$form, FormStateInterface $form_state) {
    $form['keycloak_provisioning_operations'] = [
      '#markup' => '<br><div class="mo_keycloak_background_note">Choose operations in Manual Provisioning</div>',
    ];

    $this->moProvisioningOperations('manual', $form);

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

    $form['actions']['send_are'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save'),
      '#attributes' => [
        'class' => [
          'use-ajax',
          'button--primary',
        ],
      ],
      '#ajax' => [
        'callback' => '::saveSettingsKeycloakManualProv',
        'event' => 'click',
      ],
    ];

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

  /**
   * Automatic configuration form elements.
   */
  public function automaticConfiguration(array &$form, FormStateInterface $form_state) {
    $form['keycloak_provisioning_operations'] = [
      '#markup' => '<br><div class="mo_keycloak_background_note">Choose operations in Automatic Provisioning</div>',
    ];

    $this->moProvisioningOperations('automatic', $form);

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

    $form['actions']['send_are'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save'),
      '#attributes' => [
        'class' => [
          'use-ajax',
          'button--primary',
        ],
      ],
      '#ajax' => [
        'callback' => '::saveSettingsKeycloakAutomaticProv',
        'event' => 'click',
      ],
    ];
    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
  }

  /**
   * Builds provisioning operations checkboxes.
   *
   * @param string $provision_type
   *   The type of provisioning (manual, automatic, scheduler).
   * @param array $form
   *   The form array to modify.
   */
  public function moProvisioningOperations($provision_type, &$form) {
    $form['read_user_keycloak_' . $provision_type] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Read user'),
      '#default_value' => $provision_type != 'scheduler',
      '#prefix' => '<div class="keycloak_sync_checkboxes">',
    ];

    $form['create_user_keycloak_' . $provision_type] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Create user'),
      '#default_value' => $provision_type == 'scheduler' ? FALSE : $this->config->get('keycloak_user_provisioning_enable_' . $provision_type . '_provisioning_checkbox'),
      '#disabled' => $provision_type == 'scheduler',
    ];

    $premium_badge = ' <span class="premium-badge">PREMIUM</span>';
    $form['deactivate_user_keycloak_' . $provision_type] = [
      '#type' => 'checkbox',
      '#title' => $provision_type == 'scheduler'
        ? $this->t('Deactivate user')
        : Markup::create($this->t('Deactivate user') . $premium_badge),
      '#disabled' => TRUE,
    ];

    $form['update_user_keycloak_' . $provision_type] = [
      '#type' => 'checkbox',
      '#title' => $provision_type == 'scheduler'
        ? $this->t('Update user')
        : Markup::create($this->t('Update user') . $premium_badge),
      '#disabled' => TRUE,
    ];

    $form['delete_user_keycloak_' . $provision_type] = [
      '#type' => 'checkbox',
      '#title' => $provision_type == 'scheduler'
        ? $this->t('Delete user')
        : Markup::create($this->t('Delete user') . $premium_badge),
      '#disabled' => TRUE,
      '#suffix' => '</div>',
    ];
  }

  /**
   * Test manual user sync.
   *
   * @return array
   *   Returns array of form elements.
   */
  public function testManualSync(array &$form, FormStateInterface $form_state) {
    $form['manual_provisioning_test']['create_user_fieldset'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Sync user manually') . '<hr>',
    ];

    $form['manual_provisioning_test']['create_user_fieldset']['mo_keycloak_drupal_username'] = [
      '#type' => 'entity_autocomplete',
      '#target_type' => 'user',
      '#attributes' => ['placeholder' => 'Search Drupal username of user to sync'],
      '#prefix' => Markup::create(
              '<p class="mo_keycloak_highlight_background"><strong>' . $this->t('Note:') . '</strong> ' .
              $this->t('Search the username of user to sync (create) it to Keycloak.') . '</p><div class="container-inline">'
      ),
    ];

    $form['manual_provisioning_test']['create_user_fieldset']['mo_keycloak_sync_button'] = [
      '#type' => 'submit',
      '#value' => $this->t('Sync'),
      '#button_type' => 'primary',
      '#attributes' => [
        'class' => [
          'use-ajax',
          'button--primary',
          'mo_keycloak_sync_button',
        ],

      ],
      '#ajax' => [
        'callback' => [$this, 'moDrupalToKeycloakSyncTest'],
        'event' => 'click',
      ],
    ];

    if (strpos($_SERVER['REQUEST_URI'], 'syncUserManually') !== FALSE) {
      $form['manual_provisioning_test']['create_user_fieldset']['sync_all_button'] = [
        '#type' => 'submit',
        '#value' => $this->t('Sync All Users'),
        '#button_type' => 'primary',
        '#attributes' => ['class' => ['mo_keycloak_sync_all_button']],
        '#disabled' => TRUE,
      ];
    }
    return $form;
  }

  /**
   * Saves automatic provisioning settings.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The AJAX response.
   */
  public function saveSettingsKeycloakAutomaticProv(array &$form, FormStateInterface $form_state) {
    $automatic_create_user = $form_state->getValues()['create_user_keycloak_automatic'];

    $this->configFactory
      ->set('keycloak_user_provisioning_enable_automatic_provisioning_checkbox', $automatic_create_user)
      ->save();

    $this->messenger->addstatus($this->t('Configurations saved successfully.'));
    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
    return $response;
  }

  /**
   * Saves manual provisioning settings.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The AJAX response.
   */
  public function saveSettingsKeycloakManualProv(array &$form, FormStateInterface $form_state) {
    $manual_create_user = $form_state->getValues()['create_user_keycloak_manual'];

    $this->configFactory
      ->set('keycloak_user_provisioning_enable_manual_provisioning_checkbox', $manual_create_user)
      ->save();

    $this->messenger->addStatus($this->t('Configuration saved successfully.'));
    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
    return $response;
  }

  /**
   * Performs manual user sync from Drupal to Keycloak.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   Returns ajaxresponse object.
   */
  public function moDrupalToKeycloakSyncTest(array &$form, FormStateInterface $form_state) {

    $this->configFactory
      ->set('keycloak_user_provisioning_request_to_manual_sync', TRUE)
      ->save();
    $sync_user_id = ($form_state->getValues())['mo_keycloak_drupal_username'];
    // Pass your uid.
    $account = $this->entityTypeManager->getStorage('user')->load($sync_user_id);
    if (!$account) {
      $this->messenger->addError($this->t('User with ID @id could not be found.', ['@id' => $sync_user_id]));
      $response = new AjaxResponse();
      $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
      return $response;
    }
    $username = $account->getDisplayName();
    $this->moLogger->addLog('Provisioning on demand. Entered entity name is :' . $username, __LINE__, __FUNCTION__, basename(__FILE__));

    $entity = $this->getEntityToProvision($username);

    if (is_null($entity)) {
      $this->messenger->addError($this->t('"@username" can not be recognized as a valid User or Role. Enter a valid entity( User or Role) to provision.', ['@username' => $username]));
      $response = new AjaxResponse();
      $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
      return $response;
    }
    elseif (!$this->configFactory->get('keycloak_user_provisioning_enable_manual_provisioning_checkbox')) {
      $this->messenger->addError($this->t('Manual user provisioning is currently disabled. Please enable the "Create User" option in the Manual Provisioning Configuration settings before syncing users manually.'));
      $response = new AjaxResponse();
      $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
      return $response;
    }
    else {
      $mo_entity_handler = new moUserProvisioningOperationsHandler($entity);
      $operationObject = moUserProvisioningEntityFactory::getEntityHandler($entity);
      try {
        [$status_code, $content, $conflict] = $operationObject->searchResource();
        if ($conflict == moUserProvisioningConstants::KEYCLOAK_CONFLICT) {
          $this->messenger->addWarning($this->t('User "@username" already exists in Keycloak. Cannot create duplicate user.', ['@username' => $username]));
          $response = new AjaxResponse();
          $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
          return $response;
        }
        elseif ($conflict == moUserProvisioningConstants::KEYCLOAK_CONFLICT_UNDETERMINED) {
          $this->messenger->addError($this->t('Unable to determine if user "@username" exists in Keycloak. Please check your Keycloak connection and try again.', ['@username' => $username]));
          $response = new AjaxResponse();
          $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
          return $response;
        }
        elseif ($conflict == moUserProvisioningConstants::KEYCLOAK_NO_CONFLICT &&
              $this->configFactory->get('keycloak_user_provisioning_enable_manual_provisioning_checkbox')
          ) {
          try {
            $result = $mo_entity_handler->insert();
            if (is_null($result)) {
              $log_url = $this->baseUrl . moUserProvisioningConstants::DRUPAL_LOGS_PATH;
              $this->messenger->addError($this->t('An error occurred while provisioning the user, please refer to <a href="@log_url">drupal logs</a> for more information.', ['@log_url' => $log_url]));

              $response = new AjaxResponse();
              $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
              return $response;
            }
            $entity_label = $entity->label();
            $this->messenger->addMessage($this->t('@entity successfully created at the configured application.', ['@entity' => $entity_label]));
            $response = new AjaxResponse();
            $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
            return $response;
          }
          catch (\Exception $exception) {
            $this->messenger->addError($exception->getMessage());
          }
        }
        else {
          $response = new AjaxResponse();
          $response->addCommand(new ReplaceCommand('#keycloak_provisioning_configuration', $form));
          return $response;
        }
      }
      catch (\Exception $exception) {
        $this->messenger->addError($exception->getMessage());
      }
    }
  }

  /**
   * Provisions a specific user.
   *
   * @param string $entity_name
   *   The name of the entity to provision.
   *
   * @return \Drupal\user\Entity\User|null
   *   Returns user entity if it exists else null.
   */
  private function getEntityToProvision(string $entity_name) {
    $user_storage = $this->entityTypeManager->getStorage('user');
    $users = $user_storage->loadByProperties(['name' => $entity_name]);
    if (!empty($users)) {
      return reset($users);
    }
    return NULL;
  }

  /**
   * Displays Keycloak attributes received from server.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The form array.
   */
  public function displayKeycloakAttributes(array &$form, FormStateInterface $form_state) {
    $form['keycloak_attributes_display'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Attributes Received from Keycloak'),
    ];

    $keycloak_test_data = '';
    if (($this->config->get('mo_keycloak_attr_list_from_server')) != '') {
      $keycloak_test_data = Json::decode($this->config->get('mo_keycloak_attr_list_from_server'));
    }

    if ($keycloak_test_data != '') {
      if (isset($keycloak_test_data["errorCode"])) {
        $form['keycloak_attributes_display']['error_message'] = [
          '#markup' => Markup::create('<div style="color: #a94442; background-color: #f2dede; padding: 15px; margin-bottom: 20px; text-align: center; border: 1px solid #E6B3B2; font-size: 18pt;">ERROR</div>'),
        ];
      }
      else {
        $form['keycloak_attributes_display']['success_message'] = [
          '#markup' => Markup::create('<div style="color: #3c763d; background-color: #dff0d8; padding: 15px; margin-bottom: 20px; text-align: center; border: 1px solid #AEDB9A; font-size: 18pt;">SUCCESS</div>'),
        ];

        $form['keycloak_attributes_display']['attributes_table'] = [
          '#type' => 'table',
          '#responsive' => TRUE,
          '#attributes' => ['style' => 'border-collapse: separate;', 'class' => ['mo_keycloak_attributes_table']],
          '#header' => [
            ['data' => $this->t('Attribute Name'), 'width' => '50%'],
            ['data' => $this->t('Attribute Value'), 'width' => '50%'],
          ],
        ];

        foreach ($keycloak_test_data as $key => $value) {
          $form['keycloak_attributes_display']['attributes_table'][$key] = [
            'attribute_name' => [
              '#type' => 'item',
              '#plain_text' => $key,
            ],
            'attribute_value' => [
              '#type' => 'item',
              '#plain_text' => is_array($value) ? Json::encode($value) : $value,
            ],
          ];
        }
      }
    }
    else {
      $form['keycloak_attributes_display']['no_data_message'] = [
        '#markup' => Markup::create('<div style="color: #a94442; background-color: #f2dede; padding: 15px; margin-bottom: 20px; text-align: center; border: 1px solid #E6B3B2; font-size: 18pt;">No attributes data available. Please test your Keycloak configuration first.</div>'),
      ];
    }

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Handle form submission.
  }

}
