<?php

namespace Drupal\author_field\Form;

use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Menu\MenuLinkManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use GuzzleHttp\ClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configure author_field settings for this site.
 *
 * The config form builds setting up the number of authors
 * fetched from ORCID public API,
 * and checking the status of ORCID public/sandbox APIs.
 * This form stores the number of authors.
 */
class AuthorFieldSettingsForm extends ConfigFormBase {
  /**
   * A http client interface.
   */
  protected ClientInterface $httpClient;

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

  /**
   * The cache menu instance.
   */
  protected CacheBackendInterface $cacheMenu;

  /**
   * The menu link manager instance.
   */
  protected MenuLinkManagerInterface $menuLinkManager;

  /**
   * The config factory for the form.
   */
  public function __construct(ConfigFactoryInterface $config_factory,
        MenuLinkManagerInterface $menuLinkManager,
        CacheBackendInterface $cacheMenu,
        ClientInterface $http_client,
        MessengerInterface $messenger) {
    parent::__construct($config_factory);
    $this->cacheMenu = $cacheMenu;
    $this->menuLinkManager = $menuLinkManager;
    $this->httpClient = $http_client;
    $this->messenger = $messenger;
  }

  /**
   * Instantiate interfaces.
   */
  public static function create(ContainerInterface $container): AuthorFieldSettingsForm|ConfigFormBase|static {
    return new static(
          $container->get('config.factory'),
          $container->get('plugin.manager.menu.link'),
          $container->get('cache.menu'),
          $container->get('http_client'),
          $container->get('messenger')
      );
  }

  /**
   * Set up the form ID.
   */
  public function getFormId(): string {
    return 'author_field_settings';
  }

  /**
   * Get the config file name.
   */
  protected function getEditableConfigNames(): array {
    return ['author_field.settings'];
  }

  /**
   * Build the form for the number of authors and API checking routine.
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $config = $this->config('author_field.settings');
    $form['sandbox_inline_form'] = [
      '#type' => 'container',
      '#attributes' => [
        'style' => 'display: inline-flex;',
      ],
    ];

    $form['sandbox_inline_form']['sandbox_orcid_url'] = [
      '#title' => $this->t('Sandbox public API'),
      '#type' => 'textfield',
      '#size' => 80,
      '#default_value' => $config->get('sandbox_orcid_url'),
      '#placeholder' => $this->t('Sandbox public API'),
      '#attributes' => [
        'style' => 'width: 25em; margin-right: 0.5em;',
      ],
    ];

    $form['sandbox_inline_form']['check_sandbox_api_status'] = [
      '#type' => 'submit',
      '#value' => $this->t('Sandbox API Status'),
      '#submit' => [[$this, 'checkSandboxApiStatus']],
      '#limit_validation_errors' => [],
      '#attributes' => [
        'style' => 'margin-top: 3.1em; margin-bottom:2em;',
      ],
    ];

    $form['prod_inline_form'] = [
      '#type' => 'container',
      '#attributes' => [
        'style' => 'display: inline-flex;',
      ],
    ];

    $form['prod_inline_form']['orcid_url'] = [
      '#title' => $this->t('Production public API'),
      '#type' => 'textfield',
      '#size' => 80,
      '#default_value' => $config->get('orcid_url'),
      '#placeholder' => $this->t('Production public API'),
      '#attributes' => [
        'style' => 'width: 25em; margin-right: 0.5em;',
      ],
    ];

    $form['prod_inline_form']['check_production_api_status'] = [
      '#type' => 'submit',
      '#value' => $this->t('Production API Status'),
      '#submit' => [[$this, 'checkProductionApiStatus']],
      '#limit_validation_errors' => [],
      '#attributes' => [
        'style' => 'margin-top: 3.1em; margin-bottom:2em;',
      ],
    ];

    $depth_values = range(1, 20);
    $form['author_items_depth'] = [
      '#type' => 'select',
      '#attributes' => [
        'style' => 'width: 100px;',
      ],
      '#title' => $this->t("No of ORCID's to show in autocomplete"),
      '#description' => $this->t('Maximal items to display inside "Author" field.'),
      '#default_value' => $config->get('author_items_depth'),
      '#options' => array_combine($depth_values, $depth_values),
    ];

    $form['select_api'] = [
      '#type' => 'radios',
      '#title' => $this->t('Select API'),
      '#default_value' => $config->get('author_api'),
      '#description' => $this->t('API to use inside "Author" field for searching'),
      '#options' => [
        0 => $this->t('Sandbox'),
        1 => $this->t('Production'),
      ],
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * Validate values for the form elements.
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    if ($form_state->getValue('select_api') != 0 && $form_state->getValue('select_api') != 1) {
      $form_state->setErrorByName('select_api', $this->t('The value is not correct.'));
    }
    if ($form_state->getValue('author_items_depth') > '20' || $form_state->getValue('author_items_depth') < '1') {
      $form_state->setErrorByName('author_items_depth', $this->t('The value is not correct.'));
    }
    if (!UrlHelper::isValid(trim($form_state->getValue('sandbox_orcid_url')), TRUE)) {
      $form_state->setErrorByName('sandbox_orcid_url',
        $this->t('The sandbox public URL, "@sandbox_url", is not valid.',
          ['@sandbox_url' => trim($form_state->getValue('sandbox_orcid_url'))]));
    }
    if (!UrlHelper::isValid(trim($form_state->getValue('orcid_url')), TRUE)) {
      $form_state->setErrorByName('orcid_url',
        $this->t('The public URL, "@public_url", is not valid.',
          ['@public_url' => trim($form_state->getValue('orcid_url'))]));
    }
    parent::validateForm($form, $form_state);
  }

  /**
   * This callback function accesses the ORCID sandbox API.
   *
   * Displays the message for the search result.
   *
   * @throws \GuzzleHttp\Exception\GuzzleException
   */
  public function checkSandboxApiStatus(array &$form, FormStateInterface $form_state) {
    $sandbox_orcid_url = trim($form_state->getUserInput()['sandbox_orcid_url']);
    try {
      $response2 = $this->httpClient->request('GET', $sandbox_orcid_url . '?q=obama&start=0&rows=3', [
        'headers' => ['Accept' => 'application/vnd.orcid+json'],
      ]);

      $statusCode = $response2->getStatusCode();

      if ($statusCode != 200) {
        $this->messenger->addError(t('
      <b>Sandbox API service is down </b> <br/>
      <b>Website: </b> <a href="https://info.orcid.org/documentation/api-tutorials/" target="_blank">ORCID</a> <br/>
      <b>Status Code: </b> @status_code <br/>
        ', ['@status_code' => $statusCode]));
      }
      else {
        $json_string = $response2->getBody();

        $this->messenger->addMessage(t('
        <div>
          <b>Sandbox API service is up and running </b><br/>
          <b>Website: </b> <a href="https://info.orcid.org/documentation/api-tutorials/" target="_blank">ORCID</a> <br/>
          <b>Endpoint URL: </b>
          <a>@sandbox_url?q=obama&start=0&rows=3</a>
          <br/>
          <b>Status Code: </b> @status_code <br/>
          <a href="#" type="button" onclick="document.getElementById(`content`).style.display = (document.getElementById(`content`).style.display == `none`) ? `block` : `none`" class="collapsible"><b>Show/Hide Response Body </b></a> <br/>

          <div id="content" style="display:none">
            @json_string <br/>
          </div>
        </div>
        ',
          [
            '@sandbox_url' => $sandbox_orcid_url,
            '@status_code' => $statusCode,
            '@json_string' => $json_string,
          ]
        ));

        $this->config('author_field.settings')
          ->set('author_api', $form_state->getUserInput()['select_api'])
          ->set('author_items_depth', $form_state->getUserInput()['author_items_depth'])
          ->set('sandbox_orcid_url', $sandbox_orcid_url)
          ->save();
        $messages = $this->t('The configuration options have been saved.');
        $this->messenger->addStatus($messages);
      }
    }
    catch (\Exception $e) {
      // Wrap the markup in a div.
      $messages = $this->t('The sandbox public URL, "@sandbox_url" is not valid.',
        ['@sandbox_url' => $sandbox_orcid_url]);
      $this->messenger->addError($messages);
    }
  }

  /**
   * This callback function accesses the ORCID public API.
   *
   * Displays the message for the search result.
   *
   * @throws \GuzzleHttp\Exception\GuzzleException
   */
  public function checkProductionApiStatus(array &$form, FormStateInterface $form_state) {
    $orcid_url = trim($form_state->getUserInput()['orcid_url']);
    try {
      $response2 = $this->httpClient->request('GET', $orcid_url . '?q=obama&start=0&rows=3', [
        'headers' => ['Accept' => 'application/vnd.orcid+json'],
      ]);

      $statusCode = $response2->getStatusCode();

      if ($statusCode != 200) {
        $this->messenger->addError(t('
        <b>Production API service is down </b> <br/>
        <b>Website: </b> <a href="https://info.orcid.org/documentation/api-tutorials/" target="_blank">ORCID</a> <br/>
        <b>Status Code: </b> @status_code <br/>
        ', ['@status_code' => $statusCode]));
      }
      else {
        $json_string = $response2->getBody();

        $this->messenger->addMessage(t('
        <div>
          <b>Production API service is up and running </b><br/>
          <b>Website: </b> <a href="https://info.orcid.org/documentation/api-tutorials/" target="_blank">ORCID</a> <br/>
          <b>Endpoint URL: </b>
          <a>@public_url?q=obama&start=0&rows=3</a>
          <br/>
          <b>Status Code: </b> @status_code <br/>
          <a href="#" type="button" onclick="document.getElementById(`content`).style.display = (document.getElementById(`content`).style.display == `none`) ? `block` : `none`" class="collapsible"><b>Show/Hide Response Body </b></a> <br/>

          <div id="content" style="display:none">
            @json_string <br/>
          </div>
        </div>
        ',
          [
            '@public_url' => $orcid_url,
            '@status_code' => $statusCode,
            '@json_string' => $json_string,
          ]
        ));

        $this->config('author_field.settings')
          ->set('author_api', $form_state->getUserInput()['select_api'])
          ->set('author_items_depth', $form_state->getUserInput()['author_items_depth'])
          ->set('orcid_url', $orcid_url)
          ->save();
        $messages = $this->t('The configuration options have been saved.');
        $this->messenger->addStatus($messages);
      }
    }
    catch (\Exception $e) {
      // Wrap the markup in a div.
      $messages = $this->t('The public URL, "@public_url" is not valid.', ['@public_url' => $orcid_url]);
      $this->messenger->addError($messages);
    }
  }

  /**
   * The action function for the form submit.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->config('author_field.settings')
      ->set('author_api', $form_state->getValue('select_api'))
      ->set('author_items_depth', $form_state->getValue('author_items_depth'))
      ->set('sandbox_orcid_url', trim($form_state->getValue('sandbox_orcid_url')))
      ->set('orcid_url', trim($form_state->getValue('orcid_url')))
      ->save();
    parent::submitForm($form, $form_state);
    $this->cacheMenu->invalidateAll();
    $this->menuLinkManager->rebuild();
  }

}
