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

#
# This script authenticates connections to nchan
#  
# Scenario 1: HTTP publisher location
# Headers:
#    Authorization:   the authorization token with allowed channels
#    X-Channel-Type:  publisher
#    X-Original-URI:  the location url
#    X-Channel-Group: the group of channels
#    X-Pub-Channels:  a list of channels
# Allows POST and GET requests
# Publishing to user channels "user:NNN" is allowed only if token has
# property "users=true"
#
# Scenario 2: websocket / event source
# Headers:
#    Authorization:   the authorization token with allowed channels
#    X-Channel-Type:  subscriber / controller
#    X-Original-URI:  the location url
#    X-Channel-Group: the group of channels
#    X-Pub-Channels:  a list of publisher channels (optional)
#    X-Sub-Channels:  a list of subscriber channels (optional)
# Allows POST and GET requests

use IMATHUZH\Messenger\Nchan\NchanRequest;

require_once 'common.php';

try {
    $authorized = false;    // Required for a notice to the controller
    $request = NchanRequest::fromServerRequest();

    logmsg("auth - INFO - request uri " . $request->requestUri);
    logHeaders('auth');

    # Check the existence of token and channel data
    $tokenId = $_SERVER['HTTP_AUTHORIZATION'] ?? reject('Authorization header missing');
    $group = $request->getChannelGroup() ?: reject('Channel group missing');
    
    # Read the token and authorize the access to the channel based on the requester's type
    $token = loadTokenStorage()->loadToken($tokenId);
    logTokens('auth');
    switch ($request->getRequesterType()) {
        case 'publisher':
            logmsg('auth - DEBUG - authorizing publisher');
            $token->authorizesPublisher(
                $request->getChannelGroup(),
                $request->getPubChannels()
            ) or reject('Unauthorized publisher: allowed are ' . implode(",", $token->getPubChannels($group) ?? ['null']));
            break;
        case 'listener':
            logmsg('auth - DEBUG - authorizing listener');
            $authenticated = $token->authorizesPublisher(
                $request->getChannelGroup(),
                $request->getPubChannels()
            ) or reject('Unauthorized publisher: allowed are ' . implode(",", $token->getPubChannels($group) ?? ['null']));
            $token->authorizesListener(
                $request->getChannelGroup(),
                $request->getSubChannels()
            ) or reject('Unauthorized subscriber: allowed are ' . implode(",", $token->getSubChannels($group) ?? ['null']));
            break;
        case 'controller':
            logmsg('auth - DEBUG - authorizing controller');
            $authenticated = $token->authorizesController(
                $request->getChannelGroup(),
                $request->getSubChannels()
            ) or reject('Unauthorized controller: allowed are ' . implode(",", $token->getCtlChannels($group) ?? ['null']));
            break;
        default:
            reject('Unknown requester type: ' . $request->getRequesterType());
    }
    # Access allowed
    logmsg("auth - INFO - success");
    $request->authorize();
    $authorized = true;
} catch (\Exception $e) {
    $request->close(403, "Access denied");
    $error = $e->getMessage() . PHP_EOL . $e->getTraceAsString();
    logmsg("auth - ERROR - $error");
}

$payload = $request->getRequestMetaData();
$payload['authorized'] = $authorized;
if (isset($error)) $payload['error'] = $error;

$request->notifyController('auth', $payload);
logmsg("auth - INFO - done");
