<?php

namespace Drupal\rest_api_authentication;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\Html;

/**
 * Validates API requests using Basic Authentication.
 */
class ApiAuthenticationBasicAuth {

  /**
   * Validates the API request.
   *
   * @param mixed $request
   *   The request object.
   * @param array $app_data
   *   The application-specific configuration data (optional).
   *
   * @return array
   *   An array containing the validation result.
   */
  public static function validateApiRequest($request, array $app_data = []) {
    $api_error = [];
    $api_error['user'] = NULL;
    $authorization_header = "";
    if (!empty(\Drupal::config('rest_api_authentication.settings')->get('custom_header')) || \Drupal::config('rest_api_authentication.settings')->get('custom_header') != NULL) {
      $authorization_header = $request->headers->get(\Drupal::config('rest_api_authentication.settings')->get('custom_header'));
    }

    if ($request->headers->get('AUTHORIZATION') != NULL || $request->headers->get('AUTHORISATION') != NULL) {
      $authorization_header = $request->headers->get('AUTHORIZATION') != NULL ? $request->headers->get('AUTHORIZATION') : $request->headers->get('AUTHORISATION');
    }

    // html::escape() is used to filtering or escaping or XSS vulnerabilities.
    $authorization_header = Html::escape($authorization_header);

    if (empty($authorization_header) || $authorization_header == "") {
      return [
        'status' => 'error',
        'http_code' => '401',
        'error' => 'MISSING_AUTHORIZATION_HEADER',
        'error_description' => 'Authorization header not received.',
      ];
    }
    if (!preg_match('/\Basic\b/', $authorization_header)) {
      return [
        'status' => 'error',
        'http_code' => '401',
        'error' => 'INVALID_AUTHORIZATION_HEADER_TOKEN_TYPE',
        'error_description' => 'Authorization header must be the type of Basic.',
      ];
    }
    $authorization_header_array = explode(" ", $authorization_header);
    $authorization_header_value = $authorization_header_array[1];
    $decoded_authorization_header = base64_decode($authorization_header_value, TRUE);
    if ($decoded_authorization_header === FALSE) {
      return [
        'status' => 'error',
        'http_code' => '400',
        "error" => "INVALID_BASE64",
        'error_description' => 'Invalid base64 encoding',
      ];
    }
    $creds = explode(":", $decoded_authorization_header);

    if (isset($creds[0]) && isset($creds[1])) {
      $name = $creds[0];
      if (empty($name)) {
        return [
          'status' => 'error',
          'http_code' => '401',
          "error" => "MISSING_USERNAME",
          'error_description' => 'Username Not Found',
        ];
      }
      $pwd = $creds[1];

      if (!(\Drupal::service('user.auth')->authenticate($name, $pwd))) {
        return [
          'status' => 'error',
          'http_code' => '401',
          "error" => "INVALID_CREDENTIALS",
          'error_description' => 'Invalid username or password',
        ];
      }
      try {
        $users = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties(['name' => $name]);
      }
      catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
        \Drupal::logger('rest_api_authentication')->error('Basic Auth :' . $e->getMessage());
      }
      $user = !empty($users) ? reset($users) : NULL;
      if ($user->isBlocked()) {
        return [
          'status' => 'error',
          'http_code' => '403',
          "error" => "USER_BLOCKED",
          'error_description' => 'Access denied. The account has not been activated or is blocked.',
        ];
      }
      $api_error['status'] = 'SUCCESS';
      $api_error['user'] = $user;
    }
    else {
      $api_error = [
        'status' => 'error',
        'http_code' => '400',
        "error" => "INCOMPLETE_REQUEST",
        'error_description' => 'Incomplete request',
      ];
    }
    return $api_error;
  }

}
