<?php

namespace Drupal\miniorange_2fa\Controller;

use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\miniorange_2fa\MoAuthUtilities;
use Drupal\user\Controller\UserAuthenticationController;
use Drupal\user\UserAuthInterface;
use Drupal\user\UserStorageInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Serializer;

/**
 * Provides a custom login controller to handle Two-Factor Authentication (2FA).
 * This controller overrides the default login process.
 */

class MoTfaController extends UserAuthenticationController {

   /**
   * Constructs a new MoTfaController object.
   *
   * @param \Drupal\Core\Flood\FloodInterface $user_flood_control
   *   The flood control handler.
   * @param \Drupal\user\UserStorageInterface $user_storage
   *   The user storage service.
   * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
   *   The CSRF token generator.
   * @param \Drupal\user\UserAuthInterface $user_auth
   *   The user authentication service.
   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
   *   The route provider.
   * @param \Symfony\Component\Serializer\Serializer $serializer
   *   The serializer service.
   * @param array $serializer_formats
   *   Supported serialization formats.
   * @param \Psr\Log\LoggerInterface $logger
   *   The logger service.
   */

  public function __construct(
    FloodInterface $user_flood_control,
    UserStorageInterface $user_storage,
    CsrfTokenGenerator $csrf_token,
    UserAuthInterface $user_auth,
    RouteProviderInterface $route_provider,
    Serializer $serializer,
    array $serializer_formats,
    LoggerInterface $logger,

  ) {
    parent::__construct($user_flood_control, $user_storage, $csrf_token, $user_auth, $route_provider, $serializer, $serializer_formats, $logger);
  }

   /**
   * Dependency injection through the service container.
   *
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   *   The service container.
   *
   * @return static
   *   Returns an instance of this class.
   */

  public static function create(ContainerInterface $container) {
    try {
      $flood_service = $container->get('user.flood_control');
    } catch (ServiceNotFoundException $e) {
      $flood_service = $container->get('flood');
    }

    if ($container->hasParameter('serializer.formats') && $container->has('serializer')) {
      $serializer = $container->get('serializer');
      $formats = $container->getParameter('serializer.formats');
    } else {
      $formats = ['json'];
      $serializer = new Serializer([], [new JsonEncoder()]);
    }

    return new static(
      $flood_service,
      $container->get('entity_type.manager')->getStorage('user'),
      $container->get('csrf_token'),
      $container->get('user.auth'),
      $container->get('router.route_provider'),
      $serializer,
      $formats,
      $container->get('logger.factory')->get('user'),
    );
  }

  /**
   * Custom login without generating a cookie.
   */
  public function validateLoginRequest(Request $request) {
    $format = $this->getRequestFormat($request);
    $content = $request->getContent();
    $credentials = $this->serializer->decode($content, $format);
    $utilities = new MoAuthUtilities();
    $variables_and_values = ['mo_auth_enable_two_factor'];
    $mo_db_values = $utilities->miniOrange_set_get_configurations($variables_and_values, 'GET');

    if (empty($credentials['name'])) {
      throw new BadRequestHttpException('Missing credentials.name.');
    }

    /** @var \Drupal\user\UserInterface[] $users */
    
    $users = $this->userStorage->loadByProperties(['name' => $credentials['name']]);
    if (count($users) !== 1) {
      throw new BadRequestHttpException('Sorry, unrecognized username or password.');
    }

    if (empty($mo_db_values['mo_auth_enable_two_factor']) || $mo_db_values['mo_auth_enable_two_factor'] !== TRUE) {
      return parent::login($request);
    }

    throw new AccessDeniedHttpException('The user has not been activated or is blocked');
  }
}
