<?php

use MintyPHP\Http\IntendedUrlService;
use MintyPHP\Http\RequestRuntimeInterface;
use MintyPHP\Http\SessionStoreInterface;
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\Support\Flash;

$requestRuntime = app(RequestRuntimeInterface::class);
$sessionStore = app(SessionStoreInterface::class);
$intendedUrlService = app(IntendedUrlService::class);

// Store return_to from query parameter (set by session timeout or auth guard redirect)
// or from hidden form field (preserved through multi-stage login POST flow).
$returnTo = trim((string) (requestInput()->queryAll()['return_to'] ?? ''));
if ($returnTo === '') {
    $returnTo = trim((string) (requestInput()->bodyAll()['return_to'] ?? ''));
}

if ($returnTo !== '') {
    $intendedUrlService->store($returnTo, $sessionStore);
}

// Expose sanitized return_to for hidden form fields so it survives across
// POST stages even if the query string or session is lost.
$returnToSanitized = $intendedUrlService->sanitize($returnTo !== '' ? $returnTo : ($intendedUrlService->retrieve($sessionStore) ?? ''));

$session = $sessionStore->all();

if (!empty($session['user']['id'])) {
    Router::redirect($intendedUrlService->retrieveAndForget($sessionStore));
    return;
}

$tenantHint = trim((string) (requestInput()->queryAll()['tenant'] ?? requestInput()->bodyAll()['tenant_hint'] ?? ''));
$stage = 'resolve_email';
$email = '';
$tenantCandidates = [];
$tenantCandidateById = [];
$selectedTenantId = 0;
$selectedTenant = null;
$selectedTenantMethods = ['local' => false, 'microsoft' => false, 'microsoft_reason' => '', 'ldap' => false, 'ldap_reason' => ''];
$errorBag = formErrors();
$setLoginWarning = static function (string $message) use ($errorBag): void {
    $errorBag->addGlobal($message);
};
$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 = (app(SecurityServicesFactory::class))->createRateLimiterService();

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

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

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

    $user = $userReadRepository->findByEmail($emailValue);
    $isKnownActiveUser = $user && (int) ($user['active'] ?? 0) === 1 && (int) ($user['id'] ?? 0) > 0;

    $tenants = [];
    if ($isKnownActiveUser) {
        $userId = (int) $user['id'];
        $tenants = $userTenantContextService->getAssignedActiveTenants($userId);
    }

    // Fallback: domain-based SSO discovery for users not yet provisioned locally
    $discovery = false;
    $discoveryMethodsByTenantId = [];
    if (!$tenants) {
        $discoveryResults = $tenantSsoService->discoverTenantsByEmailDomain($emailValue);
        if (!$discoveryResults) {
            return ['ok' => false];
        }
        $discovery = true;
        $tenants = array_map(static fn (array $r): array => $r['tenant'], $discoveryResults);
        foreach ($discoveryResults as $r) {
            $discoveryMethodsByTenantId[(int) $r['id']] = $r['methods'];
        }
    }

    $candidates = [];
    $candidateById = [];
    foreach ($tenants as $tenant) {
        $tenantId = (int) ($tenant['id'] ?? 0);
        if ($tenantId <= 0) {
            continue;
        }
        $tenantSlug = $tenantSsoService->tenantLoginSlug($tenant);
        if ($tenantSlug === '') {
            continue;
        }
        $methods = $discovery
            ? ($discoveryMethodsByTenantId[$tenantId] ?? ['local' => false, 'microsoft' => false, 'microsoft_reason' => '', 'ldap' => false, 'ldap_reason' => ''])
            : $tenantSsoService->resolveTenantLoginMethods($tenantId);
        $tenantUuid = (string) ($tenant['uuid'] ?? '');
        $hasLogoLight = $tenantUuid !== '' && $tenantLogoService->hasLogo($tenantUuid, \MintyPHP\Service\Tenant\TenantLogoService::THEME_LIGHT);
        $hasLogoDark = $tenantUuid !== '' && $tenantLogoService->hasLogo($tenantUuid, \MintyPHP\Service\Tenant\TenantLogoService::THEME_DARK);
        $hasAvatar = $hasLogoLight || $hasLogoDark;
        $logoTheme = $hasLogoLight ? 'light' : ($hasLogoDark ? 'dark' : '');
        $candidate = [
          'id' => $tenantId,
          'uuid' => $tenantUuid,
          'description' => (string) ($tenant['description'] ?? ''),
          'slug' => $tenantSlug,
          'has_avatar' => $hasAvatar,
          'avatar_url' => $hasAvatar
            ? lurl('auth/tenant-logo-file?uuid=' . rawurlencode($tenantUuid) . '&theme=' . rawurlencode($logoTheme) . '&size=256')
            : '',
          'methods' => $methods,
        ];
        $candidates[] = $candidate;
        $candidateById[$tenantId] = $candidate;
    }

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

    return [
      'ok' => true,
      'email' => $emailValue,
      'user' => $isKnownActiveUser ? $user : null,
      'candidates' => $candidates,
      'candidate_by_id' => $candidateById,
      'discovery' => $discovery,
    ];
};

$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 (requestInput()->isMethod('POST')) {
    if (!actionRequireCsrf('login', flashOnFailure: false, redirectOnFailure: false)) {
        $errorBag->addGlobal(t('Form expired, please try again'));
        $stage = 'resolve_email';
    } else {
        $postedStage = trim((string) (requestInput()->bodyAll()['stage'] ?? 'resolve_email'));
        $email = trim((string) (requestInput()->bodyAll()['email'] ?? ''));
        $clientIp = trim((string) ($requestRuntime->ip()));
        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) (requestInput()->bodyAll()['tenant_id'] ?? 0);

                if (in_array($postedStage, ['choose_tenant', 'login_password', 'login_ldap'], 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) (requestInput()->bodyAll()['tenant_hint'] ?? $tenantHint))
                    );
                    $selectedTenant = $tenantCandidateById[$selectedTenantId] ?? null;
                    $selectedTenantMethods = is_array($selectedTenant['methods'] ?? null)
                      ? $selectedTenant['methods']
                      : $selectedTenantMethods;
                } else {
                    $selectedTenantId = $resolveSelectedTenantId(
                        $tenantCandidateById,
                        $postedTenantId,
                        trim((string) (requestInput()->bodyAll()['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'])
                          && empty($selectedTenantMethods['ldap'])
                          && $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_ldap') {
                            $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['ldap'])) {
                                $setLoginWarning(t('LDAP login is not available for this tenant.'));
                            } else {
                                $ldapUsername = trim((string) (requestInput()->bodyAll()['ldap_username'] ?? ''));
                                $ldapPassword = (string) (requestInput()->bodyAll()['password'] ?? '');
                                $remember = !empty(requestInput()->bodyAll()['remember']);

                                $ldapAuthService = $authServicesFactory->createLdapAuthService();
                                $ssoUserLinkService = $authServicesFactory->createSsoUserLinkService();

                                $tenantRecord = app(\MintyPHP\Service\Auth\AuthGatewayFactory::class)
                                    ->createAuthTenantGateway()
                                    ->findById($selectedTenantId);
                                if (!$tenantRecord) {
                                    $setLoginWarning(t('We could not continue with these login details'));
                                } else {
                                    $ldapConfigResult = $tenantSsoService->getEffectiveLdapProviderConfig($tenantRecord);
                                    if (!($ldapConfigResult['ok'] ?? false)) {
                                        $setLoginWarning(t('LDAP login is not available for this tenant.'));
                                    } else {
                                        $ldapConfig = $ldapConfigResult['config'];
                                        $allowedDomains = trim((string) ($ldapConfig['allowed_domains'] ?? ''));
                                        if ($allowedDomains !== '') {
                                            $domainList = array_filter(array_map('trim', explode(',', $allowedDomains)));
                                            if (!$tenantSsoService->isEmailDomainAllowed($email, $domainList)) {
                                                $setLoginWarning(t('Your email domain is not allowed for LDAP login on this tenant.'));
                                                return;
                                            }
                                        }

                                        $ldapResult = $ldapAuthService->authenticate($ldapConfig, $ldapUsername !== '' ? $ldapUsername : $email, $ldapPassword);
                                        if (!($ldapResult['ok'] ?? false)) {
                                            $ldapError = $ldapResult['error'] ?? 'unknown';
                                            if ($ldapError === 'credentials_empty') {
                                                $setLoginWarning(t('Please enter your username and password.'));
                                            } elseif ($ldapError === 'connection_failed') {
                                                $setLoginWarning(t('LDAP server is not reachable. Please try again later.'));
                                            } else {
                                                $setLoginWarning(t('Username or password not valid'));
                                            }
                                            $passwordFailureResult = $rateLimiterService->registerFailure(
                                                $loginPasswordScope,
                                                $passwordAttemptKey,
                                                $loginPasswordLimit,
                                                $loginPasswordWindow,
                                                $loginPasswordBlock
                                            );
                                            if (!($passwordFailureResult['allowed'] ?? true)) {
                                                $setRateLimitWarning((int) ($passwordFailureResult['retry_after'] ?? $loginPasswordBlock));
                                            }
                                        } else {
                                            $linkResult = $ssoUserLinkService->linkOrProvisionLdapUser($tenantRecord, $ldapResult, $ldapConfig);
                                            if (!($linkResult['ok'] ?? false)) {
                                                $linkError = $linkResult['error'] ?? 'unknown';
                                                if ($linkError === 'user_inactive') {
                                                    $setLoginWarning(t('Your account is deactivated.'));
                                                } elseif ($linkError === 'email_missing') {
                                                    $setLoginWarning(t('Your LDAP account does not have an email address configured.'));
                                                } else {
                                                    $setLoginWarning(t('Login could not be completed. Please contact your administrator.'));
                                                }
                                            } else {
                                                $ldapUserId = (int) ($linkResult['user_id'] ?? 0);
                                                $loginResult = $authService->loginUserById($ldapUserId, $selectedTenantId, 'ldap');
                                                if (!($loginResult['ok'] ?? false)) {
                                                    $setLoginWarning(t('Login could not be completed. Please contact your administrator.'));
                                                } else {
                                                    $rateLimiterService->reset($loginPasswordScope, $passwordAttemptKey);
                                                    if ($remember || $tenantSsoService->shouldAutoRememberOnLdapLogin($selectedTenantId)) {
                                                        $rememberMeService->rememberUser($ldapUserId);
                                                    }
                                                    $sessionStore->set('session_started_at', time());
                                                    $sessionStore->set('session_last_activity', time());
                                                    Router::redirect($intendedUrlService->retrieveAndForget($sessionStore));
                                                    return;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        } 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) (requestInput()->bodyAll()['password'] ?? '');
                                $remember = !empty(requestInput()->bodyAll()['remember']);
                                $result = $authService->login($email, $password);
                                if (!($result['ok'] ?? false)) {
                                    if (!empty($result['needs_verification'])) {
                                        $sessionStore->set('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 {
                                    $authenticatedUser = $sessionStore->get('user', []);
                                    $userId = (int) (is_array($authenticatedUser) ? ($authenticatedUser['id'] ?? 0) : 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);
                                            }
                                            $sessionStore->set('session_started_at', time());
                                            $sessionStore->set('session_last_activity', time());
                                            Router::redirect($intendedUrlService->retrieveAndForget($sessionStore));
                                            return;
                                        }
                                    }
                                }
                            }
                        } else {
                            $stage = 'resolve_email';
                            $setLoginWarning(t('We could not continue with these login details'));
                        }
                    }
                }
            }
        }
    }
}

$errors = $errorBag->toFlatList();
