<?php declare(strict_types=1);
/**
 * @package messenger
 * @author kputyr
 * @date 23.03.2026
 */

namespace IMATHUZH\Messenger;

interface TokenInterface extends \IteratorAggregate
{
    /** Channel filtering flag: keep special channels <prefix>.<channel-name> */
    const KEEP_SPECIAL = 1;
    /** Channel filtering flag: keep channel patterns */
    const KEEP_PATTERNS = 2;
    /** Channel filtering flag: keep all entries */
    const KEEP_ALL = 0xFF;

    /**
     * Constructs an access token for a controller.
     * 
     * Channels must be specified as an associative array or a comma-separated list
     * of channels.
     * 
     * **Example**
     * ```
     * TokenInterface::createControllerToken('group1', ['ch1','ch2']);
     * ```
     * creates a token for controlling `ch1` and `ch2` from group `group1` and
     * ```
     * createControllerToken('group2', 'ch*');
     * ```
     * creates a token for controlling all channels starting with `ch` from group `group2`.
     * 
     * @param string $group           The name of the group of channels
     * @param array|string $channels  A list of channels
     */
    public static function createControllerToken(string $group, array|string $channels): static;

    /**
     * Constructs an access token for a publisher.
     * 
     * Channels must be specified as an associative array or a comma-separated list
     * of channels.
     * 
     * **Example**
     * ```
     * createPublisherToken('group1', ['ch1','ch2']);
     * ```
     * creates a token allowing to publish on `ch1` and `ch2` from group `group1` and
     * ```
     * createPublisherToken('group2', 'ch*');
     * ```
     * creates a token allowing to publish on all channels starting with `ch` from group `group2`.
     * 
     * @param string $group           The name of the group of channels
     * @param array|string $channels  A list of channels
     */
    public static function createPublisherToken(string $group, array|string $channels): static;

    /**
     * Constructs an access token for a listener.
     * 
     * Channels must be specified as an associative array or a comma-separated list
     * of channels.
     * 
     * **Example**
     * ```
     * createListenerToken('group1', ['ch1','ch2']);
     * ```
     * creates a token allowing to listen on `ch1` and `ch2` from group `group1` and
     * ```
     * createListenerToken('group2', 'ch*');
     * ```
     * creates a token allowing to listen on all channels starting with `ch` from group `group2`.
     * 
     * @param string $group           The name of the group of channels
     * @param array|string $channels  A list of channels
     */
    public static function createListenerToken(string $group, array|string $channels): static;

    /**
     * Constructs an access token for a messenger location.
     * 
     * Channels must be specified as an associative array or a comma-separated list
     * of channels.
     * 
     * **Example**
     * ```
     * createMessengerToken('group1', ['ch1','ch2'], ['ch3']);
     * ```
     * creates a token allowing to publish to `ch1` and `ch2` and listen on 'ch3' from group `group1` and
     * ```
     * createMessengerToken('group2', 'ch*', 'user:*');
     * ```
     * creates a token allowing to publish on all channels starting with `ch` and listen
     * on all user from group `group2`.
     * 
     * @param string $group           The name of the group of channels
     * @param array|string $pubChannels  A list of publisher channels
     * @param array|string $subChannels  A list of listener channels
     */
    public static function createMessengerToken(string $group, array|string $pubChannels, array|string $subChannels): static;

    /**
     * Merges a list of tokens into one token, ensuring that channels are not duplicated.
     * 
     * @param TokenInterface[] $tokens  A list of token to be merged
     * @return TokenInterface           The merged token
     */
    public static function mergeTokens(self ...$tokens): static;

    /**
     * Converts the list of channels to an array if necessary, removes invalid names
     * and trims the valid ones.
     * 
     * @param array|string   a list of channels to sanitize
     * @return array         a sanitized list
     */
    public static function sanitizeChannels(array|string $channels): array;

    /**
     * Converts the token to raw data
     */
    public function toArray(): array;

    /**
     * Returns the authorized publisher channels associated with a given group.
     * 
     * This command may return an empty array if the token grants access to publisher
     * channels in this group, but not entry matches the provided filter.
     * 
     * @param string $group       The queried channel group
     * @param int $channelFlags   Flags to filter channels: a union of KEEP_NNN constants
     * @return array|null   null if the token does not grant publisher access for this group
     */
    public function getPubChannels(string $group, int $channelFlags=0): ?array;

    /**
     * Returns the authorized subscriber channels associated with a given group
     * 
     * This command may return an empty array if the token grants access to publisher
     * channels in this group, but not entry matches the provided filter.
     * 
     * @param string $group       The queried channel group
     * @param int $channelFlags   Flags to filter channels: a union of KEEP_NNN constants
     * @return array|null   null if the token does not grant subscriber access for this group
     */
    public function getSubChannels(string $group, int $channelFlags=0): ?array;

    /**
     * Returns the authorized controller channels associated with a given group
     * 
     * This command may return an empty array if the token grants access to publisher
     * channels in this group, but not entry matches the provided filter.
     * 
     * @param string $group       The queried channel group
     * @param int $channelFlags   Flags to filter channels: a union of KEEP_NNN constants
     * @return array|null   null if the token does not grant controller access for this group
     */
    public function getCtlChannels(string $group, int $channelFlags=0): ?array;

    /**
     * Checks if the token grants publishing access to provided channels.
     * 
     * @param string $group   The group of channels
     * @param string $channel The channels to be authorized
     * @return bool True if the channel is authorized by the token, false otherwise
     */
    public function authorizesPublisher(string $group, array $channels): bool;

    /**
     * Checks if the token grants listening access to provided channels
     * 
     * @param string $group   The group of channels
     * @param string $channel The channels to be authorized
     * @return bool True if the channel is authorized by the token, false otherwise
     */
    public function authorizesListener(string $group, array $channels): bool;

    /**
     * Checks if the token grants controlling access to provided channels
     * 
     * @param string $group   The group of channels
     * @param string $channel The channels to be authorized
     * @return bool True if the channel is authorized by the token, false otherwise
     */
    public function authorizesController(string $group, array $channels): bool;

}
