<?php

namespace Drupal\user_provisioning\ProviderSpecific\APIHandler\UserAPIHandler;

use Drupal\Core\Config\ImmutableConfig;
use Drupal\user_provisioning\Helpers\moUserProvisioningLogger;
use Drupal\user_provisioning\ProviderSpecific\APIHandler\moAPIHandlerInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Log\LoggerInterface;

/**
 * Handles Okta API requests for user provisioning.
 */
class moUserOktaAPIHandler implements moAPIHandlerInterface {

  /**
   * Okta server base URL.
   *
   * @var string
   *   Okta server base URL.
   */
  private string $serverUrl;

  /**
   * Okta API bearer token.
   *
   * @var string
   *   Okta API token.
   */
  private string $bearerToken;

  /**
   * HTTP client for outbound API calls.
   *
   * @var \GuzzleHttp\Client
   *   HTTP client instance.
   */
  private Client $httpClient;

  /**
   * Immutable provisioning configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   *   Read-only configuration object.
   */
  private ImmutableConfig $config;

  /**
   * Logger channel for provisioning logs.
   *
   * @var \Psr\Log\LoggerInterface
   *   Drupal logger channel.
   */
  private LoggerInterface $logger;

  /**
   * Logger utility for formatted provisioning logs.
   *
   * @var \Drupal\user_provisioning\Helpers\moUserProvisioningLogger
   *   Logger utility instance.
   */
  private moUserProvisioningLogger $moLogger;

  /**
   * Constructs the Okta API handler.
   */
  public function __construct() {
    $this->config = \Drupal::config('user_provisioning.settings');
    $this->serverUrl = $this->config->get('mo_provider_specific_provisioning_base_url');
    $this->bearerToken = $this->config->get('mo_provider_specific_provisioning_api_token');
    $this->httpClient = \Drupal::httpClient();
    $this->logger = \Drupal::logger('user_provisioning');
    $this->moLogger = new moUserProvisioningLogger();
  }

  /**
   * Displays an HTML-formatted error message and exits.
   */
  public static function showErrorMessage($get) {
    echo '<div style="font-family:Calibri;padding:0 3%;">
            <div style="color: #a94442;background-color: #f2dede;padding: 15px;margin-bottom: 20px;text-align:center;border:1px solid #E6B3B2;font-size:18pt;">
            ERROR
            </div><div style="color: #a94442;font-size:14pt; margin-bottom:20px;">';

    foreach ($get as $key => $val) {
      if ($key == 'state') {
        continue;
      }
      echo '<p><strong>' . $key . ': </strong>' . $val . '</p>';
    }
    echo '</div></div>';
    exit;
  }

  /**
   * Sends a GET request to Okta.
   */
  public function get($query) {
    $url = $this->getRequestUrl($query);
    $options = ['headers' => ['Authorization' => 'SSWS ' . $this->bearerToken]];

    $this->moLogger->addLog('Query url is ' . $url, __LINE__, __FUNCTION__, __FILE__);
    $this->moLogger->addFormattedLog($options, __LINE__, __FUNCTION__, __FILE__, 'The header for resource search request is:');
    try {
      return $this->httpClient->get($url, $options);
    }
    catch (\Exception $exception) {
      // 409 conflict should be handled through the catch statement.
      throw new \Exception($exception->getMessage(), $exception->getCode(), $exception);
    }
  }

  /**
   * Sends a POST request to Okta.
   */
  public function post(array $body) {
    $header = [
      'Accept' => 'application/json',
      'Authorization' => 'SSWS ' . $this->bearerToken,
      'Content-Type' => 'application/json',
    ];

    $url = $this->postRequestUrl();
    $options = [
      'headers' => $header,
      'body' => json_encode($body),
    ];

    $this->moLogger->addLog('Query url is ' . $url, __LINE__, __FUNCTION__, __FILE__);
    $this->moLogger->addFormattedLog($options, __LINE__, __FUNCTION__, __FILE__, 'The header and body for resource creation request is:');

    try {
      return $this->httpClient->request(
        'POST',
        $url,
        $options,
      );
    }
    catch (GuzzleException $exception) {
      throw new \Exception($exception->getMessage(), $exception->getCode(), $exception);
    }

  }

  /**
   * Returns the URL for Okta GET requests.
   */
  private function getRequestUrl($query): string {
    return rtrim($this->serverUrl, '/') . '/api/v1/' . $query;
  }

  /**
   * Creates and returns the URL to make POST api call for user creation.
   *
   * @return string
   *   The absolute POST request URL.
   */
  private function postRequestUrl(): string {
    return rtrim($this->serverUrl, '/') . '/api/v1/users?activate=false';
  }

  /**
   * Returns the URL for Okta user deactivation.
   */
  private function deactivateUserUrl($query): string {
    return rtrim($this->serverUrl, '/') . '/api/v1/users/' . $query . '/lifecycle/deactivate';
  }

  /**
   * {@inheritDoc}
   */
  public function put(array $body) {
    // @todo Implement put() method.
  }

  /**
   * {@inheritDoc}
   */
  public function patch(array $patch) {
    // @todo Implement patch() method.
  }

  /**
   * {@inheritDoc}
   */
  public function delete($resource_id) {
    // @todo Implement delete() method.
  }

  /**
   * Sends a deactivation request to Okta.
   */
  public function deactivate(array $body) {
    $header = [
      'Accept' => 'application/json',
      'Authorization' => 'SSWS ' . $this->bearerToken,
      'Content-Type' => 'application/json',
    ];

    $url = $this->deactivateUserUrl($body[0]);
    $options = [
      'headers' => $header,
      'body' => NULL,
    ];

    $this->moLogger->addLog('Query url is ' . $url, __LINE__, __FUNCTION__, __FILE__);
    $this->moLogger->addFormattedLog($options, __LINE__, __FUNCTION__, __FILE__, 'The header and body for resource deactivation request is:');

    try {
      return $this->httpClient->request(
        'POST',
        $url,
        $options,
      );
    }
    catch (GuzzleException $exception) {
      throw new \Exception($exception->getMessage(), $exception->getCode(), $exception);
    }
  }

}
