<?php

namespace Drupal\azure_ad\Form;

use Drupal;
use Drupal\azure_ad\Helper\moAzureADHelper;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\user\Entity\User;
use Drupal\user_provisioning\moUserProvisioningConstants;
use Exception;
use Drupal\user_provisioning\Helpers\moUserProvisioningLogger;
use Drupal\user_provisioning\moUserProvisioningOperationsHandler;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Form\FormBuilder;

class MoDrupalToAzureSync extends FormBase
{
    private ImmutableConfig $config;
    private Config $config_factory;
    protected $messenger;
    private moUserProvisioningLogger $mo_logger;
    private $base_url;

    public function __construct(){
        global $base_url;
        $this->config = Drupal::config('azure_ad.settings');
        $this->config_factory = Drupal::configFactory()->getEditable('azure_ad.settings');
        $this->messenger = Drupal::messenger();
        $this->mo_logger = new moUserProvisioningLogger();
        $this->base_url = $base_url;
    }

    public function getFormId()
    {
        return "mo_drupal_to_azure";
    }

    public function buildForm(array $form, FormStateInterface $form_state)
    {
        $form['mo_azure_ad_overview_add_css'] = [
            '#attached' => [
                'library' => [
                    'azure_ad/azure_ad.admin',
                    'core/drupal.dialog.ajax'
                ]
            ],
        ];

        $form['azure_ad_summary_header_style'] = [
            '#markup' => t('<div class="mo_azure_header_container_step1">'),
        ];

        $test_config_result = $this->config->get('test_azure_config_result');

        if($test_config_result == null)
         $test_config_result = false;
        else if($test_config_result == true){
            $this->messenger->addError(t('An error occurred while performing the test configuration. Please click on the <a class="js-form-submit mo_top_bar_button form-submit use-ajax" data-dialog-type="modal" href="reviewconfig">Review Azure Configurations</a> button to perform the Test configuration again. Test Configuration must be successfull to perform user sync from Drupal to Azure AD.'));
        }

        $form['mo_azure_ad_manual_provisioning_back_to_overview'] = [
            '#type' => 'submit',
            '#value' => t('&#11164; &nbsp;Back to Provisioning types'),
            '#button_type' => 'danger',
            '#submit' => ['::moAzureProvBackStep4Button'],
        ];

        $form['review_config'] = array(
            '#markup' => '<a class="button button--primary js-form-submit mo_top_bar_button form-submit use-ajax reviewbutton" data-dialog-type="modal" href="reviewconfig" style="float:right;">Review Azure Configurations</a>',
        );

        $form['redirect_to_mapping'] = array(
            '#type' => 'submit',
            '#value' => t('Attribute Mapping'),
            '#submit' => array('::redirect_to_mapping_tab'),
            '#attributes' => ['style' => 'float: right;'],
        );

        $status = $this->config->get('azure_ad_provisioning_step');

        $this->AzureADManualProvisioning($form, $form_state);
        $this->AzureADAutoProvisioning($form, $form_state);
        $this->AzureADSchedulerProvisioning($form, $form_state);

        $form['submit_button'] = [
            '#type' => 'submit',
            '#value' => t('Save Configuration '),
            '#button_type' => 'primary',
            '#disabled' => $test_config_result
        ];

        $azure_ad_helper = new moAzureADHelper();
        $azure_ad_helper->moAzureShowCustomerSupportIcon($form, $form_state);

        return $form;
    }

    public function submitForm(array &$form, FormStateInterface $form_state){

        $form_values = $form_state->getValues();

        $manually_create_user = $form_values['azure_ad_manual_provisioning_operations']['operations']['create_user_azure_ad_manual'];
        $auto_create_user = $form_values['azure_ad_auto_provisioning_operations']['operations']['create_user_azure_ad_automatic'];

        $this->config_factory->set('azure_ad_manual_provisioning_checkbox', $manually_create_user)->save();
        $this->config_factory->set('azure_ad_automatic_provisioning_checkbox', $auto_create_user)->save();
        $this->messenger->addstatus(t('Configurations saved successfully.'));
    }

    public function redirect_to_mapping_tab(array &$form, FormStateInterface $form_state){
        $response = new RedirectResponse(Url::fromRoute('azure_ad.mapping')->toString());
        $response->send();
        return new Response();
    }

    private function AzureADAutoProvisioning(array &$form, FormStateInterface $form_state){

        $form['mo_azure_ad_auto_provisioning_fieldset'] = [
            '#type' => 'fieldset',
            '#title' => t('Automatic Provisioning Configurations'),
        ];

        $form['mo_azure_ad_auto_provisioning_fieldset']['azure_ad_auto_provisioning_operations'] = [
            '#type' => 'table',
            '#responsive' => true,
            '#attributes' => ['style' => 'border-collapse: separate;']
        ];

        $row = $this->moProvisioningOperations('automatic');
        $form['mo_azure_ad_auto_provisioning_fieldset']['azure_ad_auto_provisioning_operations']['operations'] = $row;
    }

    private function AzureADSchedulerProvisioning(array &$form, FormStateInterface $form_state){

        $form['mo_azure_ad_scheduler_provisioning_fieldset'] = [
            '#type' => 'fieldset',
            '#title' => t('Scheduler Based Provisioning Configuration <a href="azure_upgrade_plans"><small>[PREMIUM]</small></a>'),
        ];

        $form['mo_azure_ad_scheduler_provisioning_fieldset']['azure_ad_provisioning_operations'] = [
            '#type' => 'table',
            '#responsive' => true,
            '#attributes' => ['style' => 'border-collapse: separate;']
        ];

        $row = $this->moProvisioningOperations('scheduler');
        $form['mo_azure_ad_scheduler_provisioning_fieldset']['azure_ad_provisioning_operations']['operations'] = $row;

    }

    private function AzureADManualProvisioning(array &$form, FormStateInterface $form_state){

        $test_config_result = $this->config->get('test_azure_config_result');

        if($test_config_result == null)
         $test_config_result = false;

        $form['mo_azure_ad_manual_provisioning_fieldset'] = [
            '#type' => 'fieldset',
            '#title' => t('Manual Provisioning Configurations'),
        ];

        $form['mo_azure_ad_manual_provisioning_fieldset']['azure_ad_manual_provisioning_operations'] = [
            '#type' => 'table',
            '#responsive' => TRUE,
            '#attributes' => ['style' => 'border-collapse: separate;']
        ];

        $row = $this->moProvisioningOperations('manual');
        $form['mo_azure_ad_manual_provisioning_fieldset']['azure_ad_manual_provisioning_operations']['operations'] = $row;

        $form['mo_azure_ad_manual_provisioning_fieldset']['manual_provisioning_choose_operations']['create_user_fieldset']=[
            '#type' => 'fieldset',
            '#title' => $this->t('Sync Users<hr>'),
            '#states' => [
                'visible' => [
                    ':input[name="azure_ad_manual_provisioning_operations[operations][create_user_azure_ad_manual]"]' => ['checked' => TRUE],
                ],
            ],
        ];

        $form['mo_azure_ad_manual_provisioning_fieldset']['manual_provisioning_choose_operations']['create_user_fieldset']['mo_azure_drupal_username'] = [
            '#type' => 'textfield',
            '#attributes' => ['placeholder' => 'Search Drupal username of user to sync'],
            '#autocomplete_route_name' => 'user_provisioning.autocomplete',
            '#prefix' => '<p class="mo_azure_highlight_background"><strong>Note:</strong> Search the username of user in Drupal to sync (create) it to the Active Directory.</p><div class="container-inline">',
            '#disabled' => $test_config_result
        ];

        $form['mo_azure_ad_manual_provisioning_fieldset']['manual_provisioning_choose_operations']['create_user_fieldset']['mo_azure_sync_button'] = [
            '#type' => 'submit',
            '#value' => t('Sync'),
            '#disabled' => $test_config_result,
            '#button_type' => 'primary',
            '#attributes' => ['class' => ['mo_azure_sync_button']],
            '#submit' => ['::moAzureManualSync'],
        ];

        $form['mo_azure_ad_manual_provisioning_fieldset']['manual_provisioning_choose_operations']['create_user_fieldset']['mo_azure_sync_all_button'] = [
            '#type' => 'submit',
            '#value' => t('Sync All Users'),
            '#disabled' => $test_config_result,
            '#button_type' => 'primary',
            '#attributes' => ['class' => ['mo_azure_sync_all_button']],
            '#disabled' => true,
        ];
    }

    public function moProvisioningOperations($provision_type): array
    {
        if($provision_type == 'manual'){
            $type = 'Manually';
        }else if($provision_type == 'automatic'){
            $type = 'Automatically';
        }else{
            $type = 'On Cron Run';
        }

        $test_config_result = $this->config->get('test_azure_config_result');

        if($test_config_result == null)
         $test_config_result = false;

        $row['read_user_azure_ad'] = [
            '#type' => 'checkbox',
            '#title' => t('Read user '.$type),
            '#default_value' => $provision_type != 'scheduler',
            '#disabled' => true,
            '#prefix'=> '<div class="container-inline">',
        ];

        $row['create_user_azure_ad_'.$provision_type] = [
            '#type' => 'checkbox',
            '#title' => t('Create user '.$type),
            '#default_value' => $provision_type == 'scheduler' ? false : $this->config->get('azure_ad_'.$provision_type.'_provisioning_checkbox'),
            '#disabled' => $provision_type == 'scheduler' || $test_config_result,
        ];

        $row['update_user_azure_ad'] = [
            '#type' => 'checkbox',
            '#title' => $provision_type == 'scheduler'? t('Update user '.$type) : t('Update user '. $type .'&nbsp;<a href="azure_upgrade_plans"><b><small>[PREMIUM]</small></b></a>'),
            '#disabled' => TRUE,
        ];

        $row['deactivate_user_azure_ad'] = [
            '#type' => 'checkbox',
            '#title' => $provision_type == 'scheduler'? t('Deactivate user '.$type) : t('Deactivate user '. $type .'&nbsp;<a href="azure_upgrade_plans"><b><small>[PREMIUM]</small></b></a>'),
            '#disabled' => TRUE,
        ];

        $row['delete_user_azure_ad'] = [
            '#type' => 'checkbox',
            '#title' => $provision_type == 'scheduler'? t('Delete user '.$type) : t('Delete user '. $type .'&nbsp;<a href="azure_upgrade_plans"><b><small>[PREMIUM]</small></b></a>'),
            '#disabled' => TRUE,
        ];

        return $row;
    }

    public function attributeMapping(array &$form, FormStateInterface $form_state){

        $form['mo_azure_attribute_mapping_details'] = [
            '#type' => 'fieldset',
            '#title' => t('Mapping'),
        ];

        $form['mo_azure_attribute_mapping_details']['azure_ad_attribute_mapping'] = [
            '#type' => 'fieldset',
            '#title' => t('Basic Attribute Mapping<hr>'),
        ];

        $form['mo_azure_attribute_mapping_details']['azure_ad_attribute_mapping']['azure_ad_attribute_mapping_table'] = [
            '#type' => 'table',
            '#responsive' => TRUE ,
            '#attributes' => ['class' => ['attribute_mapping_table']]
        ];

        $row = $this->moAzureADAttributeMapTable();
        $form['mo_azure_attribute_mapping_details']['azure_ad_attribute_mapping']['azure_ad_attribute_mapping_table']['attributes'] = $row;

        $form['mo_azure_attribute_mapping_details']['azure_ad_custom_attribute_mapping'] = array(
            '#type' => 'fieldset',
            '#title' => t('Custom Attribute Mapping<a href="azure_upgrade_plans"><small>[PREMIUM]</small></a><hr>'),
        );

        $form['mo_azure_attribute_mapping_details']['azure_ad_custom_attribute_mapping']['attribute_mapping_info'] = array(
            '#markup' => '<div class="mo_azure_highlight_background">This feature allows you to map the user attributes from your Drupal to Azure AD.</div>',
        );

        $custom_fields = [];
        $usr = User::load(\Drupal::currentUser()->id());
        $usrVal = $usr->toArray();
        foreach ($usrVal as $key => $value) {
            $custom_fields[$key] = $key;
        }

        $form['mo_azure_attribute_mapping_details']['azure_ad_custom_attribute_mapping']['azure_ad_custom_attribute_mapping_table'] = [
            '#type' => 'table',
            '#responsive' => TRUE ,
            '#attributes' => ['class' => ['custom_attribute_mapping_table']]
        ];

        $row = $this->moAzureADCustomAttributeMapTable($custom_fields);
        $form['mo_azure_attribute_mapping_details']['azure_ad_custom_attribute_mapping']['azure_ad_custom_attribute_mapping_table']['custom_mapping'] = $row;
    }

    private function moAzureADAttributeMapTable(){

        $default_user_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('user', NULL);
        foreach($default_user_fields as $key => $value)
        {
            $default_user_fields[$key]=$key;
        }

        $default_user_fields = $default_user_fields;

        $email_attr = $this->config->get('azure_ad_mapping_mail_attr');
        $username_attr = $this->config->get('azure_ad_mapping_username_attr');

        if( !isset($email_attr) ){
            $this->config_factory->set('azure_ad_mapping_mail_attr', 'mail')->save();
        }

        if(!isset($username_attr)){
            $this->config_factory->set('azure_ad_mapping_username_attr', 'name')->save();
        }

        $row['username_attribute'] = [
            '#type' => 'select',
            '#title' => t('Username Attribute'),
            '#options' => $default_user_fields,
            '#default_value' => $this->config->get('azure_ad_mapping_username_attr')
        ];

        $row['email_attribute'] = [
            '#type' => 'select',
            '#title' => t('Email Attribute'),
            '#options' => $default_user_fields,
            '#default_value' => $this->config->get('azure_ad_mapping_mail_attr')
        ];

        return $row;
    }

    private function moAzureADCustomAttributeMapTable($custom_fields){
        $row['azure_ad_drupal_attr_name'] = [
            '#title' => t('Drupal Attribute Name'),
            '#type' => 'select',
            '#options' => $custom_fields,
            '#disabled' => true,
        ];

        $row['azure_ad_attribute_name'] = [
            '#type' => 'textfield',
            '#title' => t('Azure AD Attribute Name'),
            '#disabled' => true,
        ];

        $row['azure_ad_add_button'] = [
            '#type' => 'submit',
            '#button_type' => 'primary',
            '#disabled' => true,
            '#value' => '+',
        ];

        $row['azure_ad_sub_button'] = [
            '#type' => 'submit',
            '#button_type' => 'danger',
            '#disabled' => true,
            '#value' => '-',
        ];

        return $row;
    }

    public function saveAzureADSummary(array &$form, FormStateInterface $form_state){
        $overview = new MoAzureOverview();
        $overview->moAzureStep2SaveButton($form, $form_state);
    }

    public function saveSettingsAzureADManualProv(array &$form, FormStateInterface $form_state)
    {
        $this->config_factory->set('azure_ad_manual_provisioning_checkbox', $form_state->getValues()['azure_ad_manual_provisioning_operations']['operations']['create_user_azure_ad_manual'])->save();
        $this->messenger->addstatus(t('Configurations saved successfully.'));
    }

    public function saveSettingsAzureADAutomaticProv(array &$form, FormStateInterface $form_state)
    {
        $auto_create_user = $form_state->getValues()['azure_ad_auto_provisioning_operations']['operations']['create_user_azure_ad_automatic'];
        $this->config_factory->set('azure_ad_automatic_provisioning_checkbox', $auto_create_user)->save();
        $this->messenger->addstatus(t('Configurations saved successfully.'));
    }

    public function moAzureManualSync(array &$form, FormStateInterface $form_state){
        $entity_name = $form_state->getValues()['mo_azure_drupal_username'];

        if (empty($entity_name)){
            $this->messenger->addError($this->t('Please enter a valid entity (User or Role) to provision.'));
            return;
        }

        $this->config_factory->set('azure_ad_manual_provisioning_checkbox', true)->save();

        $this->mo_logger->addLog('Provisioning on demand. Entered entity name is :' . $entity_name, __LINE__, __FUNCTION__, basename(__FILE__));

        $entity = $this->getEntityToProvision($entity_name);
        if (is_null($entity)) {
            $this->messenger->addError('"' . $entity_name . '"' . $this->t(' can not be recognized as a valid User or Role. Enter a valid entity (User or Role) to provision.'));
            return;
        }

        $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="' . $this->base_url . moUserProvisioningConstants::DRUPAL_LOGS_PATH . '">drupal logs</a> for more information.'));
                return;
            }
            $this->messenger->addMessage($this->t($entity->label() . ' successfully created at the configured application. '));//Now you can click on <strong>All Done</strong>button.
        } catch (Exception $exception) {
            $this->messenger->addError($exception->getMessage());
        }
    }

    private function getEntityToProvision(string $entity_name) {
        $user = user_load_by_name($entity_name);
        if ($user != FALSE) {
            return User::load($user->id());
        }
        return NULL;
    }

    private function AzureADOverviewTab(array &$form, FormStateInterface $form_state){
        $response = new RedirectResponse(Url::fromRoute('azure_ad.overview')->toString());
        $response->send();
        return new Response();
    }

    public function moAzureProvBackStep4Button(){
        $this->config_factory->set('azure_ad_provisioning_step', 'step3')->save();
        $response = new RedirectResponse(Url::fromRoute('azure_ad.overview')->toString());
        $response->send();
        return new Response();
    }

}
