<?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;

/**
 * Handles Keycloak API requests for user provisioning.
 */
class moUserKeycloakAPIHandler implements moAPIHandlerInterface {
  /**
   * Keycloak base domain URL.
   *
   * @var string
   *   Keycloak base domain URL.
   */
  private string $keycloakDomain;

  /**
   * Keycloak client ID.
   *
   * @var string
   *   Keycloak client ID.
   */
  private string $keycloakClientId;

  /**
   * Keycloak client secret.
   *
   * @var string
   *   Keycloak client secret.
   */
  private string $keycloakClientSecret;

  /**
   * Keycloak realm name.
   *
   * @var string
   *   Keycloak realm name.
   */
  private string $keycloakRealm;

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

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

  /**
   * Logger for handler diagnostics.
   *
   * @var \Drupal\user_provisioning\Helpers\moUserProvisioningLogger
   *   Logger instance.
   */
  private moUserProvisioningLogger $moLogger;

  /**
   * Constructs the Keycloak API handler.
   */
  public function __construct() {
    $this->config = \Drupal::config('keycloak_user_provisioning.settings');
    $this->httpClient = \Drupal::httpClient();
    $this->moLogger = new moUserProvisioningLogger();
    $this->keycloakClientId = $this->config->get('keycloak_user_provisioning_client_id');
    $this->keycloakClientSecret = $this->config->get('keycloak_user_provisioning_client_secret');
    $this->keycloakRealm = $this->config->get('keycloak_user_provisioning_realm');
    $this->keycloakDomain = $this->config->get('keycloak_user_provisioning_base_url');
  }

  /**
   * Retrieves an access token from Keycloak.
   */
  public function getKeyCloakAccessToken() {

    $this->config = \Drupal::config('keycloak_user_provisioning.settings');
    $this->httpClient = \Drupal::httpClient();
    $this->moLogger = new moUserProvisioningLogger();
    $this->keycloakClientId = $this->config->get('keycloak_user_provisioning_client_id');
    $this->keycloakClientSecret = $this->config->get('keycloak_user_provisioning_client_secret');
    $this->keycloakRealm = $this->config->get('keycloak_user_provisioning_realm');
    $this->keycloakDomain = $this->config->get('keycloak_user_provisioning_base_url');

    $token_url = rtrim($this->keycloakDomain, '/') .
            '/realms/' . $this->keycloakRealm .
            '/protocol/openid-connect/token';

    $response = $this->httpClient->request('POST', $token_url, [
      'timeout' => 30,
      'headers' => [
        'Content-Type' => 'application/x-www-form-urlencoded',
      ],
      'form_params' => [
        'grant_type' => 'client_credentials',
        'client_id' => $this->keycloakClientId,
        'client_secret' => $this->keycloakClientSecret,
      ],
    ]);

    $token = json_decode($response->getBody()->getContents(), TRUE);

    if (json_last_error() !== JSON_ERROR_NONE) {
      throw new \Exception('Invalid JSON response from Keycloak');
    }

    return $token['access_token'];
  }

  /**
   * Sends a GET request to Keycloak.
   *
   * @throws \Exception
   */
  public function get(string $query) {
    $access_token = $this->getKeyCloakAccessToken();

    if (empty($access_token)) {
      throw new \Exception('Unable to get access token');
    }
    $url = $this->getUsersEndPointUrl($query);

    $options = [
      'timeout' => 30,
      'headers' => [
        'Authorization' => 'Bearer ' . $access_token,
        'Content-Type' => 'application/json',
      ],
    ];

    return $this->httpClient->request('GET', $url, $options);
  }

  /**
   * Builds the Keycloak users endpoint URL.
   */
  public function getUsersEndPointUrl(string $query) {
    return rtrim($this->keycloakDomain, '/') . '/admin/realms/' . $this->keycloakRealm . '/users/' . $query;
  }

  /**
   * Sends a POST request to Keycloak.
   */
  public function post(array $body) {
    $this->config = \Drupal::config('keycloak_user_provisioning.settings');
    $this->httpClient = \Drupal::httpClient();
    $this->moLogger = new moUserProvisioningLogger();
    $this->keycloakClientId = $this->config->get('keycloak_user_provisioning_client_id');
    $this->keycloakClientSecret = $this->config->get('keycloak_user_provisioning_client_secret');
    $this->keycloakRealm = $this->config->get('keycloak_user_provisioning_realm');
    $this->keycloakDomain = $this->config->get('keycloak_user_provisioning_base_url');
    $access_token = $this->getKeyCloakAccessToken();
    $header = [
      'Accept' => 'application/json',
      'Authorization' => 'Bearer ' . $access_token,
      'Content-Type' => 'application/json',
    ];
    $user_creation_url = rtrim($this->keycloakDomain, '/')
            . '/admin/realms/' . $this->keycloakRealm . '/users';
    $options = [
      'headers' => $header,
      'json' => $body,
    ];

    return $this->httpClient->request(
          'POST',
          $user_creation_url,
          $options,
      );
  }

  /**
   * {@inheritDoc}
   */
  #[\Override] public function put(array $body) {
    // @todo Implement put() method.
  }

  /**
   * {@inheritDoc}
   */
  #[\Override] public function patch(array $patch) {
    // @todo Implement patch() method.
  }

  /**
   * {@inheritDoc}
   */
  #[\Override] public function delete($resource_id) {
    // @todo Implement delete() method.
  }

  /**
   * {@inheritDoc}
   */
  #[\Override] public function deactivate(array $body) {
    // @todo Implement deactivate() method.
  }

}
