<?php

use MintyPHP\Router;
use MintyPHP\Service\Auth\AuthServicesFactory;
use MintyPHP\Service\Security\SecurityServicesFactory;
use MintyPHP\Service\Tenant\TenantServicesFactory;
use MintyPHP\Service\User\UserServicesFactory;
use MintyPHP\Session;
use MintyPHP\Support\Flash;

if (!empty($_SESSION['user']['id'])) {
  if (Flash::has()) {
    Flash::keep();
  }
  Router::redirect("admin");
  return;
}

$tenantHint = trim((string) ($_GET['tenant'] ?? $_POST['tenant_hint'] ?? ''));
$stage = 'resolve_email';
$email = '';
$tenantCandidates = [];
$tenantCandidateById = [];
$selectedTenantId = 0;
$selectedTenant = null;
$selectedTenantMethods = ['local' => false, 'microsoft' => false, 'microsoft_reason' => ''];
$setLoginWarning = static function (string $message): void {
  Flash::error($message, 'login', 'login_stage_warning');
};
$setRateLimitWarning = static function (int $retryAfterSeconds) use ($setLoginWarning): void {
  $retryAfterSeconds = max(1, $retryAfterSeconds);
  http_response_code(429);
  header('Retry-After: ' . $retryAfterSeconds);
  $setLoginWarning(t('Too many login attempts. Please wait and try again.'));
};
$rateLimiterService = (new SecurityServicesFactory())->createRateLimiterService();

$loginResolveScope = 'login.resolve.ip';
$loginResolveLimit = 25;
$loginResolveWindow = 600;
$loginResolveBlock = 300;

$loginPasswordScope = 'login.password.email_ip';
$loginPasswordLimit = 5;
$loginPasswordWindow = 900;
$loginPasswordBlock = 900;
$authServicesFactory = new AuthServicesFactory();
$authService = $authServicesFactory->createAuthService();
$rememberMeService = $authServicesFactory->createRememberMeService();
$tenantSsoService = $authServicesFactory->createTenantSsoService();
$tenantAvatarService = (new TenantServicesFactory())->createTenantAvatarService();
$userServicesFactory = new UserServicesFactory();
$userReadRepository = $userServicesFactory->createUserReadRepository();
$userTenantContextService = $userServicesFactory->createUserTenantContextService();

$resolveLoginCandidates = static function (string $inputEmail) use ($userReadRepository, $userTenantContextService, $tenantSsoService, $tenantAvatarService): array {
  $emailValue = strtolower(trim($inputEmail));
  if ($emailValue === '' || !filter_var($emailValue, FILTER_VALIDATE_EMAIL)) {
    return ['ok' => false];
  }

  $user = $userReadRepository->findByEmail($emailValue);
  if (!$user || (int) ($user['active'] ?? 0) !== 1) {
    return ['ok' => false];
  }

  $userId = (int) ($user['id'] ?? 0);
  if ($userId <= 0) {
    return ['ok' => false];
  }

  $tenants = $userTenantContextService->getAssignedActiveTenants($userId);
  if (!$tenants) {
    return ['ok' => false];
  }

  $candidates = [];
  $candidateById = [];
  foreach ($tenants as $tenant) {
    $tenantId = (int) ($tenant['id'] ?? 0);
    if ($tenantId <= 0) {
      continue;
    }
    $tenantSlug = $tenantSsoService->tenantLoginSlug($tenant);
    if ($tenantSlug === '') {
      continue;
    }
    $methods = $tenantSsoService->resolveTenantLoginMethods($tenantId);
    $tenantUuid = (string) ($tenant['uuid'] ?? '');
    $hasAvatar = $tenantUuid !== '' && $tenantAvatarService->hasAvatar($tenantUuid);
    $candidate = [
      'id' => $tenantId,
      'uuid' => $tenantUuid,
      'description' => (string) ($tenant['description'] ?? ''),
      'slug' => $tenantSlug,
      'has_avatar' => $hasAvatar,
      'avatar_url' => $hasAvatar
        ? lurl('auth/tenant-avatar-file?uuid=' . rawurlencode($tenantUuid) . '&size=256')
        : '',
      'methods' => $methods,
    ];
    $candidates[] = $candidate;
    $candidateById[$tenantId] = $candidate;
  }

  if (!$candidates) {
    return ['ok' => false];
  }

  return [
    'ok' => true,
    'email' => $emailValue,
    'user' => $user,
    'candidates' => $candidates,
    'candidate_by_id' => $candidateById,
  ];
};

$resolveSelectedTenantId = static function (array $candidateByIdValue, int $requestedTenantIdValue, string $tenantHintValue): int {
  if ($requestedTenantIdValue > 0 && isset($candidateByIdValue[$requestedTenantIdValue])) {
    return $requestedTenantIdValue;
  }

  $hint = trim(strtolower($tenantHintValue));
  if ($hint !== '') {
    foreach ($candidateByIdValue as $candidate) {
      $candidateSlug = strtolower((string) ($candidate['slug'] ?? ''));
      if ($candidateSlug !== '' && $candidateSlug === $hint) {
        return (int) ($candidate['id'] ?? 0);
      }
    }
  }

  $first = reset($candidateByIdValue);
  if (!is_array($first)) {
    return 0;
  }
  return (int) ($first['id'] ?? 0);
};

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  if (!Session::checkCsrfToken()) {
    Flash::error(t('Form expired, please try again'), 'login', 'login_csrf_invalid');
    Router::redirect('login');
    return;
  }

  $postedStage = trim((string) ($_POST['stage'] ?? 'resolve_email'));
  $email = trim((string) ($_POST['email'] ?? ''));
  $clientIp = trim((string) ($_SERVER['REMOTE_ADDR'] ?? ''));
  if ($clientIp === '') {
    $clientIp = 'unknown';
  }

  if ($postedStage === 'reset') {
    $email = '';
    $stage = 'resolve_email';
  } else {
    if ($postedStage === 'resolve_email') {
      $resolveLimitResult = $rateLimiterService->isBlocked($loginResolveScope, $clientIp);
      if (!($resolveLimitResult['allowed'] ?? true)) {
        $setRateLimitWarning((int) ($resolveLimitResult['retry_after'] ?? 60));
        return;
      }
    }

    $resolved = $resolveLoginCandidates($email);
    if (!$resolved['ok']) {
      $stage = 'resolve_email';
      $setLoginWarning(t('We could not continue with these login details'));
      if ($postedStage === 'resolve_email') {
        $resolveFailureResult = $rateLimiterService->registerFailure(
          $loginResolveScope,
          $clientIp,
          $loginResolveLimit,
          $loginResolveWindow,
          $loginResolveBlock
        );
        if (!($resolveFailureResult['allowed'] ?? true)) {
          $setRateLimitWarning((int) ($resolveFailureResult['retry_after'] ?? $loginResolveBlock));
        }
      }
    } else {
      $email = (string) $resolved['email'];
      $tenantCandidates = $resolved['candidates'];
      $tenantCandidateById = $resolved['candidate_by_id'];
      $postedTenantId = (int) ($_POST['tenant_id'] ?? 0);

      if (in_array($postedStage, ['choose_tenant', 'login_password'], true)
        && ($postedTenantId <= 0 || !isset($tenantCandidateById[$postedTenantId]))) {
        $stage = 'choose_tenant';
        $setLoginWarning(t('We could not continue with these login details'));
        $selectedTenantId = $resolveSelectedTenantId(
          $tenantCandidateById,
          0,
          trim((string) ($_POST['tenant_hint'] ?? $tenantHint))
        );
        $selectedTenant = $tenantCandidateById[$selectedTenantId] ?? null;
        $selectedTenantMethods = is_array($selectedTenant['methods'] ?? null)
          ? $selectedTenant['methods']
          : $selectedTenantMethods;
      } else {
        $selectedTenantId = $resolveSelectedTenantId(
          $tenantCandidateById,
          $postedTenantId,
          trim((string) ($_POST['tenant_hint'] ?? $tenantHint))
        );
        $selectedTenant = $tenantCandidateById[$selectedTenantId] ?? null;
        if (!is_array($selectedTenant)) {
          $stage = 'resolve_email';
          $setLoginWarning(t('We could not continue with these login details'));
        } else {
          $selectedTenantMethods = $selectedTenant['methods'];
          $selectedTenantSlug = trim((string) $selectedTenant['slug']);
          $isMicrosoftOnlyTenant = empty($selectedTenantMethods['local'])
            && !empty($selectedTenantMethods['microsoft'])
            && $selectedTenantSlug !== '';

          if (
            $postedStage === 'resolve_email'
            && count($tenantCandidates) === 1
            && $isMicrosoftOnlyTenant
          ) {
            Router::redirect('auth/microsoft/start?tenant=' . rawurlencode($selectedTenantSlug));
            return;
          }

          if (
            $postedStage === 'choose_tenant'
            && $isMicrosoftOnlyTenant
          ) {
            Router::redirect('auth/microsoft/start?tenant=' . rawurlencode($selectedTenantSlug));
            return;
          }

          if ($postedStage === 'resolve_email') {
            $stage = count($tenantCandidates) === 1 ? 'methods' : 'choose_tenant';
          } elseif ($postedStage === 'choose_tenant') {
            $stage = 'methods';
          } elseif ($postedStage === 'login_password') {
            $stage = 'methods';
            $passwordAttemptKey = strtolower(trim($email)) . '|' . $clientIp;
            $passwordLimitResult = $rateLimiterService->isBlocked($loginPasswordScope, $passwordAttemptKey);
            if (!($passwordLimitResult['allowed'] ?? true)) {
              $setRateLimitWarning((int) ($passwordLimitResult['retry_after'] ?? 60));
              return;
            }

            if (empty($selectedTenantMethods['local'])) {
              $setLoginWarning(t('Password login is disabled for this tenant. Please use Microsoft login.'));
            } else {
              $password = (string) ($_POST['password'] ?? '');
              $remember = !empty($_POST['remember']);
              $result = $authService->login($email, $password);
              if (!($result['ok'] ?? false)) {
                if (!empty($result['needs_verification'])) {
                  $_SESSION['email_verification_email'] = $result['email'] ?? $email;
                  Flash::error(t('Please verify your email first'), 'verify-email', 'login_not_verified');
                  Router::redirect('verify-email');
                  return;
                }
                $setLoginWarning(t((string) ($result['message'] ?? 'Email/password not valid')));
                $passwordFailureResult = $rateLimiterService->registerFailure(
                  $loginPasswordScope,
                  $passwordAttemptKey,
                  $loginPasswordLimit,
                  $loginPasswordWindow,
                  $loginPasswordBlock
                );
                if (!($passwordFailureResult['allowed'] ?? true)) {
                  $setRateLimitWarning((int) ($passwordFailureResult['retry_after'] ?? $loginPasswordBlock));
                }
              } else {
                $userId = (int) ($_SESSION['user']['id'] ?? 0);
                if ($userId <= 0 || !$authService->canLoginToTenant($userId, $selectedTenantId)) {
                  $authService->logout();
                  $setLoginWarning(t('No access to this tenant'));
                  $passwordFailureResult = $rateLimiterService->registerFailure(
                    $loginPasswordScope,
                    $passwordAttemptKey,
                    $loginPasswordLimit,
                    $loginPasswordWindow,
                    $loginPasswordBlock
                  );
                  if (!($passwordFailureResult['allowed'] ?? true)) {
                    $setRateLimitWarning((int) ($passwordFailureResult['retry_after'] ?? $loginPasswordBlock));
                  }
                } else {
                  $setTenant = $userTenantContextService->setCurrentTenant($userId, $selectedTenantId);
                  if (!($setTenant['ok'] ?? false)) {
                    $authService->logout();
                    $setLoginWarning(t('No access to this tenant'));
                    $passwordFailureResult = $rateLimiterService->registerFailure(
                      $loginPasswordScope,
                      $passwordAttemptKey,
                      $loginPasswordLimit,
                      $loginPasswordWindow,
                      $loginPasswordBlock
                    );
                    if (!($passwordFailureResult['allowed'] ?? true)) {
                      $setRateLimitWarning((int) ($passwordFailureResult['retry_after'] ?? $loginPasswordBlock));
                    }
                  } else {
                    $rateLimiterService->reset($loginPasswordScope, $passwordAttemptKey);
                    $authService->loadTenantDataIntoSession($userId);
                    if ($remember) {
                      $rememberMeService->rememberUser($userId);
                    }
                    Router::redirect('admin');
                    return;
                  }
                }
              }
            }
          } else {
            $stage = 'resolve_email';
            $setLoginWarning(t('We could not continue with these login details'));
          }
        }
      }
    }
  }
}
