<?php

namespace IMATHUZH\Qfq\Core\Helper;

use IMATHUZH\Messenger\HttpPublisher;
use IMATHUZH\Messenger\MessageDispatcher;
use IMATHUZH\Qfq\Core\Store\Store;

/**
 * Thin adapter over the upstream nchan-messages MessageDispatcher/HttpPublisher.
 *
 * Centralises how QFQ talks to the server-side nchan publisher endpoint so
 * there is exactly one place that knows the URL pattern, reads the publish
 * base URL from the system store and joins it with the messenger group. All
 * server-initiated publishing (Chat notifications, form message parameter)
 * goes through this helper.
 *
 * The target is the auth-less `messenger-local.conf` location bound to the
 * loopback or trusted network by the admin — no token needed on the PHP→nchan
 * hop. `SYSTEM_MESSENGER_PUBLISH_URL` is the base (e.g. `http://127.0.0.1/messenger/`)
 * and the HTTP POST lands at `<base>/<group>/pub/<channels>`.
 */
class MessengerPublisher {

    /**
     * Ensures that the nchan-messages hook autoloader bridge file exists.
     *
     * The upstream nchan-messages hooks (auth.php, sub.php, …) load classes
     * via `php/vendor/composer/autoload.php` inside the package directory.
     * When nchan-messages is installed as a Composer dependency of QFQ, that
     * file must delegate to QFQ's top-level `vendor/autoload.php` — otherwise
     * the hooks crash with "class not found" and WebSocket connections fail
     * (no hello message → broken channel index → messages not dispatched).
     *
     * This method writes a tiny PHP bridge file at the expected location.
     * It is idempotent and only writes when the bridge is missing or stale.
     * Called automatically before any TokenStorage usage so the hooks work
     * from the very first page load after installation.
     */
    public static function ensureAutoloader(): void
    {
        $bridgeDir = dirname(__DIR__, 3) . '/vendor/imathuzh/nchan-messages/php/vendor/composer';
        $bridgePath = $bridgeDir . '/autoload.php';
        $expected = "<?php\nrequire_once dirname(__DIR__, 5) . '/autoload.php';\n";

        // Fast path: bridge already correct (single stat + read on tiny file)
        if (is_file($bridgePath) && file_get_contents($bridgePath) === $expected) {
            return;
        }

        @mkdir($bridgeDir, 0755, true);
        // Remove potential symlink before writing
        if (is_link($bridgePath)) {
            @unlink($bridgePath);
        }
        file_put_contents($bridgePath, $expected);
    }

    /**
     * Posts a message to one or more channels on the nchan publisher endpoint.
     *
     * Silent no-op when the publisher URL is not configured or no channels
     * were passed — callers can opt in to treating publish failures as fatal
     * by inspecting the returned error list.
     *
     * @param string $group     messenger group (nchan channel group)
     * @param array  $channels  target channel names
     * @param string $name      message name delivered to subscribers
     * @param mixed  $payload   JSON-serialisable payload delivered to subscribers
     * @return array            list of upstream publisher errors — empty on success
     *                          or when publishing is disabled
     */
    public static function publish(string $group, array $channels, string $name, $payload): array {
        if (empty($channels)) {
            return [];
        }
        $publishUrl = Store::getVar(SYSTEM_MESSENGER_PUBLISH_URL, STORE_SYSTEM) ?: '';
        if ($publishUrl === '') {
            return [];
        }
        // HttpPublisher replaces :pub: with the comma-joined channel list.
        $urlPattern = rtrim($publishUrl, '/') . '/' . $group . '/pub/:pub:';
        return (new MessageDispatcher())
            ->addPublisher(new HttpPublisher($urlPattern))
            ->publishMessage($channels, $name, $payload);
    }
}
