<?php

declare(strict_types=1);

namespace App\Framework\Sse\ValueObjects;

use App\Framework\LiveComponents\ValueObjects\ComponentId;

/**
 * SSE Channel Value Object
 *
 * Represents a typed channel for Server-Sent Events routing.
 *
 * Channel Format Examples:
 * - global
 * - component:counter:demo
 * - user:123
 * - presence:chat-room-1
 */
final readonly class SseChannel
{
    public function __construct(
        public ChannelScope $scope,
        public ?string $identifier = null
    ) {
    }

    /**
     * Global channel - broadcast to all connected clients
     */
    public static function global(): self
    {
        return new self(ChannelScope::GLOBAL);
    }

    /**
     * Component channel - updates for specific component instance
     */
    public static function forComponent(ComponentId $componentId): self
    {
        return new self(ChannelScope::COMPONENT, $componentId->toString());
    }

    /**
     * User channel - user-specific events (requires auth)
     */
    public static function forUser(string $userId): self
    {
        if (empty($userId)) {
            throw new \InvalidArgumentException('User ID cannot be empty');
        }

        return new self(ChannelScope::USER, $userId);
    }

    /**
     * Presence channel - who is online in room
     */
    public static function presence(string $room = 'global'): self
    {
        if (empty($room)) {
            throw new \InvalidArgumentException('Presence room cannot be empty');
        }

        return new self(ChannelScope::PRESENCE, $room);
    }

    /**
     * System channel - system-wide announcements
     */
    public static function system(): self
    {
        return new self(ChannelScope::SYSTEM);
    }

    /**
     * Create from string representation
     *
     * Examples:
     * - "global" → SseChannel::global()
     * - "component:counter:demo" → SseChannel::forComponent(...)
     * - "user:123" → SseChannel::forUser("123")
     */
    public static function fromString(string $channel): self
    {
        if ($channel === 'global') {
            return self::global();
        }

        if ($channel === 'system') {
            return self::system();
        }

        $parts = explode(':', $channel, 2);

        if (count($parts) < 2) {
            throw new \InvalidArgumentException("Invalid channel format: {$channel}");
        }

        [$scope, $identifier] = $parts;

        return match ($scope) {
            'component' => self::forComponent(ComponentId::fromString($identifier)),
            'user' => self::forUser($identifier),
            'presence' => self::presence($identifier),
            default => throw new \InvalidArgumentException("Unknown channel scope: {$scope}")
        };
    }

    /**
     * Convert to string representation
     */
    public function toString(): string
    {
        if ($this->identifier === null) {
            return $this->scope->value;
        }

        return "{$this->scope->value}:{$this->identifier}";
    }

    /**
     * Check if this channel matches another
     */
    public function matches(self $other): bool
    {
        return $this->toString() === $other->toString();
    }

    /**
     * Check if this is a private channel (requires auth)
     */
    public function isPrivate(): bool
    {
        return $this->scope === ChannelScope::USER;
    }

    /**
     * Check if this is a public channel
     */
    public function isPublic(): bool
    {
        return ! $this->isPrivate();
    }

    /**
     * Get scope
     */
    public function getScope(): ChannelScope
    {
        return $this->scope;
    }

    /**
     * Get identifier
     */
    public function getIdentifier(): ?string
    {
        return $this->identifier;
    }
}
