<?php

namespace MintyPHP\Http;

use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Auth\AuthScopeGateway;
use MintyPHP\Service\Auth\AuthServicesFactory;
use MintyPHP\Service\User\UserServicesFactory;

class ApiAuth
{
    private static ?array $currentUser = null;
    private static ?array $currentPermissions = null;
    private static ?int $currentTenantId = null;
    private static ?int $tokenTenantId = null;
    private static ?array $currentTokenRecord = null;
    private static bool $authenticated = false;

    /**
     * Extract the full Bearer token from the Authorization header.
     */
    public static function extractBearerToken(): string
    {
        $header = trim((string) ($_SERVER['HTTP_AUTHORIZATION'] ?? ''));
        if ($header === '') {
            $header = trim((string) ($_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''));
        }
        if ($header === '' || stripos($header, 'Bearer ') !== 0) {
            return '';
        }

        return trim(substr($header, 7));
    }

    /**
     * Attempt to authenticate from Authorization: Bearer header.
     */
    public static function authenticate(): bool
    {
        self::$currentUser = null;
        self::$currentPermissions = null;
        self::$currentTenantId = null;
        self::$tokenTenantId = null;
        self::$currentTokenRecord = null;
        self::$authenticated = false;

        $bearerToken = self::extractBearerToken();
        if ($bearerToken === '') {
            return false;
        }

        $authServicesFactory = new AuthServicesFactory();
        $scopeGateway = $authServicesFactory->createAuthScopeGateway();
        $result = $authServicesFactory->createApiTokenService()->validate($bearerToken);
        if ($result === null) {
            return false;
        }

        $user = $result['user'];
        $userId = (int) ($user['id'] ?? 0);
        $userServicesFactory = new UserServicesFactory();
        $userRoleRepository = $userServicesFactory->createUserRoleRepository();
        $rolePermissionRepository = (new AccessServicesFactory())->createRolePermissionRepository();
        $userTenantContextService = $userServicesFactory->createUserTenantContextService();

        // Load permissions directly (bypass session cache)
        $roleIds = $userRoleRepository->listRoleIdsByUserId($userId);
        $permissions = $rolePermissionRepository->listPermissionKeysByRoleIds($roleIds);

        // Resolve tenant context
        $tokenTenantId = $result['tenant_id'];
        if ($tokenTenantId !== null) {
            $userTenantIds = $scopeGateway->getUserTenantIds($userId);
            if (!in_array($tokenTenantId, $userTenantIds, true)) {
                return false;
            }
            $currentTenantId = $tokenTenantId;
            self::$tokenTenantId = $tokenTenantId;
        } else {
            $currentTenantId = $userTenantContextService->getCurrentTenantId($userId);
            self::$tokenTenantId = null;
        }

        self::$currentUser = $user;
        self::$currentPermissions = $permissions;
        self::$currentTenantId = $currentTenantId;
        self::$currentTokenRecord = $result['token_record'];
        self::$authenticated = true;

        return true;
    }

    public static function isAuthenticated(): bool
    {
        return self::$authenticated;
    }

    public static function user(): ?array
    {
        return self::$currentUser;
    }

    public static function userId(): int
    {
        return (int) (self::$currentUser['id'] ?? 0);
    }

    public static function permissions(): array
    {
        return self::$currentPermissions ?? [];
    }

    public static function hasPermission(string $key): bool
    {
        return in_array($key, self::$currentPermissions ?? [], true);
    }

    public static function tenantId(): ?int
    {
        return self::$currentTenantId;
    }

    public static function scopedTenantId(): ?int
    {
        return self::$tokenTenantId;
    }

    public static function tokenRecord(): ?array
    {
        return self::$currentTokenRecord;
    }

    public static function tokenId(): ?int
    {
        $id = (int) (self::$currentTokenRecord['id'] ?? 0);
        return $id > 0 ? $id : null;
    }

    public static function isTenantScopedToken(): bool
    {
        return (self::$tokenTenantId ?? 0) > 0;
    }

    /**
     * Require both tenant-scope and token-tenant access for a resource.
     */
    public static function requireResourceAccess(string $resource, int $resourceId): void
    {
        $scopeGateway = (new AuthServicesFactory())->createAuthScopeGateway();
        if (!$scopeGateway->canAccess($resource, $resourceId, self::userId())) {
            ApiResponse::notFound();
        }
        self::requireTokenTenantAccess($resource, $resourceId);
    }

    public static function requireTokenTenantAccess(string $resource, int $resourceId): void
    {
        if (!self::isTenantScopedToken()) {
            return;
        }

        $tenantId = (int) (self::$tokenTenantId ?? 0);
        if ($tenantId <= 0) {
            ApiResponse::forbidden();
        }

        $scopeGateway = (new AuthServicesFactory())->createAuthScopeGateway();
        if (!$scopeGateway->resourceBelongsToTenant($resource, $resourceId, $tenantId)) {
            ApiResponse::notFound();
        }
    }

    /**
     * Check if the current user can self-manage API tokens.
     */
    public static function canSelfManageTokens(): bool
    {
        return self::hasPermission(\MintyPHP\Service\Access\PermissionService::USERS_SELF_UPDATE)
            || self::hasPermission(\MintyPHP\Service\Access\PermissionService::API_TOKENS_MANAGE);
    }

    public static function requireSelfManageTokens(): void
    {
        if (!self::canSelfManageTokens()) {
            ApiResponse::forbidden('api_tokens_self_manage_forbidden');
        }
    }
}
