<?php declare(strict_types=1);
/**
 * @package messenger
 * @author kputyr
 * @date 10.02.2026
 * 
 * This script redirects requests internally to messenger channels 
 * using nginx's accel redirect feature. Usage:
 *  1. Generate a token for the client with the following content:
 *      - group: the group name of the messenger channel
 *      - controller: true for the controller channel (default: false)
 *      - pub: a comma-separated list of publisher channels
 *      - sub: a comma-separated list of subscriber channels
 *     At least one of controller, pub or sub must be present.
 *  2. Generate a random alpha-numeric string 'sip'
 *  3. Store the token in an URL-encoded form in the QFQ session: $_SESSION['qfq'][{sip}]
 *  4. The client can then connect to the messenger channel via /messenger/{sip}
 *  5. The script will read the token from the session, validate it and redirect
 *     the request internally to the appropriate messenger channel URL.
 */

namespace IMATHUZH\Messenger;

require_once 'request.php';

logHeaders('redirect');

$request = NchanRequest::fromServerRequest();
logmsg("redirect - INFO - request uri " . $request->requestUri);
try {
    # Determine the token and channel base from the request headers
    $token = $_SERVER['HTTP_AUTHORIZATION'] ?? $_GET['token'] ?? reject('Authorization header missing');
    $group = $request->getChannelGroup() ?: reject('Channel group missing');
    # Load QFQ session and close it immediately to avoid locking the session file for too long.
    session_start(['read_and_close' => true]);
    # Load the token
    logTokens('redirect');
    $tokenMgr = new SessionTokenManager($_SESSION);
    switch ($request->getRequesterType()) {
        case 'listener':
            $channelData = $tokenMgr->loadToken($token)[$group] ?? reject("Invalid room token '$token' for '$group'");
            $request->setPubChannels($channelData['pub'] ?? [])
                    ->setSubChannels($channelData['sub'] ?? [])
                    ->generateUrls();
            logmsg('redirect - DEBUG - type: listener');
            logmsg('redirect - DEBUG - pub channels: ' . implode(",", $channelData['pub']) . ' - ' . $request->getFlattenedPubChannels());
            logmsg('redirect - DEBUG - sub channels: ' . implode(",", $channelData['sub']) . ' - ' . $request->getFlattenedSubChannels());
            logmsg('redirect - DEBUG - client id: ' . $request->getClientId());
            $channelPattern = '/messenger/:group:/room/:user:/pub:pub:/sub:sub:';
            break;
        case 'controller':
            $channelData = $tokenMgr->loadToken($token)[$group] ?? reject("Invalid controller token '$token' for '$group'");
            $request->setPubChannels([])
                    ->setSubChannels($channelData['ctl'] ?? ['ctl:all'])
                    ->generateUrls();
            logmsg('redirect - DEBUG - type: controller');
            logmsg('redirect - DEBUG - sub channels: ' . implode(",", $channelData['ctl']) . ' - ' . $request->getFlattenedSubChannels());
            logmsg('redirect - DEBUG - client id: ' . $request->getClientId());
            $channelPattern = '/messenger/:group:/controller/:user:/sub:sub:';
            break;
        default:
            reject('Unknown requester type: ' . $request->getRequesterType());
    }
    # Send the redirect response to nginx
    if ($_SERVER['REQUEST_METHOD'] === 'POST') { 
        logmsg("redirect - INFO - forwarding the message to " . $request->publishUrl);
        $request->forward(file_get_contents('php://input'));
    } else {
        $channelUrl = strtr($channelPattern, [
            ':group:' => $request->getChannelGroup(),
            ':user:' => $request->getClientId() ?: '-',
            ':pub:' => $request->getFlattenedPubChannels(),
            ':sub:' => $request->getFlattenedSubChannels()
        ]);
        logmsg("redirect - INFO - redirecting to " . $channelUrl);
        http_response_code(200);
        header("X-Accel-Redirect: $channelUrl");
        header('X-Accel-Buffering: no');
//        fastcgi_finish_request();
//        $request->accelRedirect($request->channelUrl);
    }
    # Notify the controller about the redirect for logging and debugging purposes.
    $payload = $request->getRequestMetaData();
    $payload['redirectUrl'] = $channelUrl;
    $request->notifyController('redirect', $payload);
    logmsg("redirect - INFO - done");
} catch (\Exception $e) {
    logmsg("redirect - ERROR - " . $e->getMessage());
    $request->close(403, 'Access denied: ' . $e->getMessage());
}
