<?php

namespace MintyPHP\Service\Auth;

use MintyPHP\Auth;
use MintyPHP\Repository\User\UserReadRepository;
use MintyPHP\Repository\User\UserWriteRepository;
use MintyPHP\Router;
use MintyPHP\Service\User\UserAccountService;
use MintyPHP\Service\User\UserTenantContextService;
use MintyPHP\Session;
use MintyPHP\Support\Flash;

class AuthService
{
    public function __construct(
        private readonly UserReadRepository $userReadRepository,
        private readonly UserWriteRepository $userWriteRepository,
        private readonly UserAccountService $userAccountService,
        private readonly UserTenantContextService $userTenantContextService,
        private readonly RememberMeService $rememberMeService,
        private readonly EmailVerificationService $emailVerificationService,
        private readonly AuthPermissionGateway $permissionGateway,
        private readonly AuthTenantSsoGateway $tenantSsoGateway,
        private readonly AuthSavedFilterGateway $savedFilterGateway
    ) {
    }

    public function login(string $email, string $password): array
    {
        $email = trim($email);

        // Check if email is verified before attempting login
        $existingUser = $this->userReadRepository->findByEmail($email);
        if ($existingUser && empty($existingUser['email_verified_at'])) {
            return [
                'ok' => false,
                'message' => 'Please verify your email first',
                'flash_key' => 'login_not_verified',
                'needs_verification' => true,
                'email' => $email,
            ];
        }

        $user = Auth::login($email, $password);

        if (!$user) {
            return [
                'ok' => false,
                'message' => 'Email/password not valid',
                'flash_key' => 'login_invalid',
            ];
        }

        if (!($_SESSION['user']['active'] ?? 1)) {
            Auth::logout();
            return [
                'ok' => false,
                'message' => 'Account is inactive',
                'flash_key' => 'login_inactive',
            ];
        }

        $userId = (int) ($_SESSION['user']['id'] ?? 0);
        if ($userId > 0 && !$this->isLocalPasswordLoginAllowedForUser($userId)) {
            Auth::logout();
            return [
                'ok' => false,
                'message' => 'Password login is disabled for all assigned tenants',
                'flash_key' => 'login_password_disabled_all_tenants',
            ];
        }
        if ($userId > 0) {
            $this->permissionGateway->warmUserPermissions($userId, true);
            $this->loadTenantDataIntoSession($userId);
            if (!empty($_SESSION['no_active_tenant'])) {
                Auth::logout();
                return [
                    'ok' => false,
                    'message' => 'No active tenant assigned',
                    'flash_key' => 'login_no_active_tenant',
                ];
            }
            $this->userWriteRepository->updateLastLogin($userId, 'local');
        }

        return ['ok' => true];
    }

    private function isLocalPasswordLoginAllowedForUser(int $userId): bool
    {
        if ($userId <= 0) {
            return false;
        }

        $availableTenants = $this->userTenantContextService->getAvailableTenants($userId);
        if (!$availableTenants) {
            return true;
        }

        foreach ($availableTenants as $tenant) {
            $tenantId = (int) ($tenant['id'] ?? 0);
            if ($tenantId <= 0) {
                continue;
            }
            if ($this->tenantSsoGateway->isLocalPasswordLoginAllowed($tenantId)) {
                return true;
            }
        }

        return false;
    }

    public function canLoginToTenant(int $userId, int $tenantId): bool
    {
        if ($userId <= 0 || $tenantId <= 0) {
            return false;
        }

        foreach ($this->userTenantContextService->getAvailableTenants($userId) as $tenant) {
            if ((int) ($tenant['id'] ?? 0) === $tenantId) {
                return true;
            }
        }

        return false;
    }

    public function loginUserById(int $userId, ?int $preferredTenantId = null, string $loginProvider = 'local'): array
    {
        if ($userId <= 0) {
            return ['ok' => false, 'error' => 'user_not_found'];
        }

        $user = $this->userReadRepository->find($userId);
        if (!$user) {
            return ['ok' => false, 'error' => 'user_not_found'];
        }
        if (!((int) ($user['active'] ?? 1) === 1)) {
            return ['ok' => false, 'error' => 'user_inactive'];
        }

        Session::regenerate();
        $_SESSION['user'] = $user;

        if ($preferredTenantId !== null && $preferredTenantId > 0) {
            $setTenant = $this->userTenantContextService->setCurrentTenant($userId, $preferredTenantId);
            if ($setTenant['ok'] ?? false) {
                $_SESSION['user']['current_tenant_id'] = $preferredTenantId;
            }
        }

        $this->permissionGateway->warmUserPermissions($userId, true);
        $this->loadTenantDataIntoSession($userId);
        if (!empty($_SESSION['no_active_tenant'])) {
            Auth::logout();
            return ['ok' => false, 'error' => 'login_no_active_tenant'];
        }

        $this->userWriteRepository->updateLastLogin($userId, $loginProvider);
        return ['ok' => true, 'user' => $user];
    }

    public function loginAndRedirect(
        string $email,
        string $password,
        string $successTarget = 'admin',
        string $failTarget = 'login',
        bool $remember = false
    ): void {
        $result = $this->login($email, $password);

        if (!($result['ok'] ?? false)) {
            // Check if user needs to verify email
            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');
            }

            $message = $result['message'] ?? 'Email/password not valid';
            $key = $result['flash_key'] ?? 'login_invalid';
            Flash::error($message, $failTarget, $key);
            Router::redirect($failTarget);
        }

        if ($remember) {
            $userId = (int) ($_SESSION['user']['id'] ?? 0);
            if ($userId > 0) {
                $this->rememberMeService->rememberUser($userId);
            }
        }
        Router::redirect($successTarget);
    }

    public function register(array $data): array
    {
        $email = trim((string) ($data['email'] ?? ''));
        $result = $this->userAccountService->register($data);
        if (!($result['ok'] ?? false)) {
            return $result;
        }

        // Get the created user to send verification email
        $createdUser = $this->userReadRepository->findByEmail($email);
        if ($createdUser && isset($createdUser['id'])) {
            $userId = (int) $createdUser['id'];
            $this->emailVerificationService->sendVerification($userId);
        }

        // Don't login - user needs to verify email first
        return ['ok' => true, 'email' => $email, 'needs_verification' => true];
    }

    public function logout(): void
    {
        $this->rememberMeService->forgetCurrentUser();
        Auth::logout();
    }

    public function logoutAndRedirect(string $target = 'login'): void
    {
        $this->rememberMeService->forgetCurrentUser();
        Auth::logout();
        Router::redirect($target);
    }

    public function refreshSessionAuthState(int $userId): array
    {
        if ($userId <= 0) {
            return [
                'ok' => false,
                'logout_required' => true,
                'reason' => 'user_missing',
            ];
        }

        $snapshot = $this->userReadRepository->findAuthzSnapshot($userId);
        if (!$snapshot) {
            return [
                'ok' => false,
                'logout_required' => true,
                'reason' => 'user_not_found',
            ];
        }

        if ((int) ($snapshot['active'] ?? 0) !== 1) {
            return [
                'ok' => false,
                'logout_required' => true,
                'reason' => 'inactive',
            ];
        }

        $sessionVersion = (int) ($_SESSION['user']['authz_version'] ?? 0);
        $dbVersion = max(1, (int) ($snapshot['authz_version'] ?? 1));
        $refreshed = false;

        if ($sessionVersion !== $dbVersion) {
            $user = $this->userReadRepository->find($userId);
            if (!$user || (int) ($user['active'] ?? 0) !== 1) {
                return [
                    'ok' => false,
                    'logout_required' => true,
                    'reason' => 'inactive',
                ];
            }

            $_SESSION['user'] = $user;
            $this->permissionGateway->warmUserPermissions($userId, true);
            $this->loadTenantDataIntoSession($userId);
            $refreshed = true;
        }

        if (!empty($_SESSION['no_active_tenant'])) {
            return [
                'ok' => false,
                'logout_required' => true,
                'reason' => 'no_active_tenant',
                'refreshed' => $refreshed,
            ];
        }

        return [
            'ok' => true,
            'logout_required' => false,
            'refreshed' => $refreshed,
        ];
    }

    public function loadTenantDataIntoSession(int $userId): void
    {
        if ($userId <= 0) {
            return;
        }

        // Load available tenants first
        $availableTenants = $this->userTenantContextService->getAvailableTenants($userId);
        $_SESSION['available_tenants'] = $availableTenants;
        $_SESSION['available_departments_by_tenant'] = $this->userTenantContextService->getAvailableDepartmentsByTenant($userId);
        $_SESSION['address_book_saved_filters'] = $this->savedFilterGateway->listAddressBookFilters($userId);

        if (!$availableTenants) {
            $_SESSION['no_active_tenant'] = true;
            unset($_SESSION['current_tenant']);
            return;
        }
        $_SESSION['no_active_tenant'] = false;

        // Load current tenant (with fallback to first available)
        $currentTenant = $this->userTenantContextService->getCurrentTenant($userId);
        if (!$currentTenant) {
            // Fallback: use first available tenant
            $currentTenant = $availableTenants[0];
        }
        if ($currentTenant) {
            $_SESSION['current_tenant'] = $currentTenant;
        }
    }
}
