<?php

declare(strict_types=1);

namespace App\Framework\OAuth\ValueObjects;

/**
 * OAuth Token Scope Value Object
 *
 * Represents OAuth scopes (space-separated as per RFC 6749)
 */
final readonly class TokenScope
{
    /**
     * @param array<string> $scopes
     */
    public function __construct(
        private array $scopes,
    ) {
        if (empty($scopes)) {
            throw new \InvalidArgumentException('Token scope cannot be empty');
        }

        foreach ($scopes as $scope) {
            if (! is_string($scope) || empty(trim($scope))) {
                throw new \InvalidArgumentException('Invalid scope value');
            }
        }
    }

    /**
     * Create from space-separated string (OAuth standard format)
     */
    public static function fromString(string $scopeString): self
    {
        $scopes = array_values(array_filter(
            array_map('trim', explode(' ', $scopeString)),
            fn ($scope) => ! empty($scope)
        ));

        return new self($scopes);
    }

    /**
     * Create from array of scopes
     *
     * @param array<string> $scopes
     */
    public static function fromArray(array $scopes): self
    {
        return new self($scopes);
    }

    /**
     * Get scopes as array
     *
     * @return array<string>
     */
    public function toArray(): array
    {
        return $this->scopes;
    }

    /**
     * Get scopes as space-separated string (OAuth standard format)
     */
    public function toString(): string
    {
        return implode(' ', $this->scopes);
    }

    /**
     * Check if scope includes a specific permission
     */
    public function includes(string $scope): bool
    {
        return in_array($scope, $this->scopes, true);
    }

    /**
     * Check if scope includes all specified permissions
     *
     * @param array<string> $requiredScopes
     */
    public function includesAll(array $requiredScopes): bool
    {
        foreach ($requiredScopes as $required) {
            if (! $this->includes($required)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Check if scope includes any of the specified permissions
     *
     * @param array<string> $scopes
     */
    public function includesAny(array $scopes): bool
    {
        foreach ($scopes as $scope) {
            if ($this->includes($scope)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Add additional scopes
     *
     * @param array<string> $additionalScopes
     */
    public function withAdditional(array $additionalScopes): self
    {
        return new self([...$this->scopes, ...$additionalScopes]);
    }

    /**
     * Remove specific scopes
     *
     * @param array<string> $scopesToRemove
     */
    public function without(array $scopesToRemove): self
    {
        $filtered = array_filter(
            $this->scopes,
            fn ($scope) => ! in_array($scope, $scopesToRemove, true)
        );

        if (empty($filtered)) {
            throw new \InvalidArgumentException('Cannot remove all scopes');
        }

        return new self(array_values($filtered));
    }

    public function __toString(): string
    {
        return $this->toString();
    }
}
