<?php

declare(strict_types=1);

namespace App\Framework\Sse;

use App\Framework\Sse\ValueObjects\ChannelScope;
use App\Framework\Sse\ValueObjects\SseChannel;

/**
 * SSE Channel Authorizer
 *
 * Handles authorization for SSE channel subscriptions.
 *
 * Authorization Rules:
 * - GLOBAL: Public - anyone can subscribe
 * - SYSTEM: Public - anyone can subscribe
 * - COMPONENT: Public - anyone can subscribe
 * - PRESENCE: Public - anyone can subscribe (room-based)
 * - USER: Private - only the user can subscribe to their own channel
 */
final readonly class SseChannelAuthorizer
{
    /**
     * Check if a user can subscribe to a channel
     */
    public function canSubscribe(SseChannel $channel, ?string $userId): bool
    {
        return match ($channel->getScope()) {
            ChannelScope::GLOBAL, ChannelScope::SYSTEM, ChannelScope::COMPONENT, ChannelScope::PRESENCE => true,
            ChannelScope::USER => $this->canAccessUserChannel($channel, $userId),
        };
    }

    /**
     * Check if user can access a user-specific channel
     */
    private function canAccessUserChannel(SseChannel $channel, ?string $userId): bool
    {
        // User must be authenticated
        if ($userId === null) {
            return false;
        }

        // User can only access their own channel
        return $channel->getIdentifier() === $userId;
    }

    /**
     * Filter authorized channels for a user
     *
     * @param array<SseChannel> $channels
     * @return array<SseChannel>
     */
    public function filterAuthorizedChannels(array $channels, ?string $userId): array
    {
        return array_filter(
            $channels,
            fn (SseChannel $channel) => $this->canSubscribe($channel, $userId)
        );
    }

    /**
     * Get unauthorized channels for a user
     *
     * @param array<SseChannel> $channels
     * @return array<SseChannel>
     */
    public function getUnauthorizedChannels(array $channels, ?string $userId): array
    {
        return array_filter(
            $channels,
            fn (SseChannel $channel) => ! $this->canSubscribe($channel, $userId)
        );
    }
}
