<?php

namespace MintyPHP\Support;

use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Auth\AuthServicesFactory;
use MintyPHP\Service\Directory\DirectoryServicesFactory;

class Guard
{
    private const TENANT_REFRESH_INTERVAL = 60;
    private static ?AccessServicesFactory $accessServicesFactory = null;
    private static ?PermissionGateway $permissionGateway = null;

    public static function requireLogin(string $redirect = 'login'): void
    {
        if (!isset($_SESSION['user'])) {
            Flash::error('Login required', 'login', 'login_required');
            Router::redirect($redirect);
        }
        self::refreshTenantContext();
        if (!empty($_SESSION['no_active_tenant'])) {
            (new AuthServicesFactory())->createAuthService()->logout();
            Flash::error('No active tenant assigned', 'login', 'no_active_tenant');
            Router::redirect($redirect);
        }
    }

    private static function validateCurrentTenant(): void
    {
        $currentTenant = $_SESSION['current_tenant'] ?? null;
        if (!$currentTenant || empty($currentTenant['id'])) {
            return;
        }

        $tenantId = (int) $currentTenant['id'];
        $tenant = (new DirectoryServicesFactory())->createTenantService()->findById($tenantId);
        if ($tenant) {
            return;
        }

        // Current tenant was deleted, refresh session data
        $userId = (int) ($_SESSION['user']['id'] ?? 0);
        if ($userId > 0) {
            (new AuthServicesFactory())->createAuthService()->loadTenantDataIntoSession($userId);
        } else {
            unset($_SESSION['current_tenant']);
        }
    }

    private static function refreshTenantContext(): void
    {
        $userId = (int) ($_SESSION['user']['id'] ?? 0);
        if ($userId <= 0) {
            return;
        }
        $last = (int) ($_SESSION['tenant_context_refreshed_at'] ?? 0);
        $now = time();
        if ($last > 0 && ($now - $last) < self::TENANT_REFRESH_INTERVAL) {
            return;
        }
        (new AuthServicesFactory())->createAuthService()->loadTenantDataIntoSession($userId);
        $_SESSION['tenant_context_refreshed_at'] = $now;
    }

    public static function requirePermission(string $permissionKey, string $redirect = 'admin'): void
    {
        self::requireLogin();
        self::validateCurrentTenant();
        $userId = (int) ($_SESSION['user']['id'] ?? 0);
        if (!$userId || !self::permissionGateway()->userHas($userId, $permissionKey)) {
            Flash::error('Permission denied', $redirect, 'permission_denied');
            Router::redirect($redirect);
        }
    }

    public static function requirePermissionOrForbidden(string $permissionKey): void
    {
        self::requireLogin();
        self::validateCurrentTenant();
        $userId = (int) ($_SESSION['user']['id'] ?? 0);
        if (!$userId || !self::permissionGateway()->userHas($userId, $permissionKey)) {
            if (Request::wantsJson()) {
                http_response_code(403);
                header('Content-Type: application/json; charset=utf-8');
                echo json_encode(['error' => 'forbidden']);
                exit;
            }
            Router::redirect('error/forbidden?url=' . urlencode(Request::pathWithQuery()));
        }
    }

    private static function permissionGateway(): PermissionGateway
    {
        if (self::$permissionGateway instanceof PermissionGateway) {
            return self::$permissionGateway;
        }

        self::$permissionGateway = self::accessServicesFactory()->createPermissionGateway();
        return self::$permissionGateway;
    }

    private static function accessServicesFactory(): AccessServicesFactory
    {
        if (self::$accessServicesFactory instanceof AccessServicesFactory) {
            return self::$accessServicesFactory;
        }

        self::$accessServicesFactory = new AccessServicesFactory();
        return self::$accessServicesFactory;
    }
}
