<?php

declare(strict_types=1);

namespace Blax\WebRtc\Server;

use Blax\ReactPhpKernel\Contracts\Server;
use Blax\ReactPhpKernel\Server\SocketServerFactory;
use Blax\WebRtc\Signaling\SignalingHandler;
use React\EventLoop\LoopInterface;
use React\Socket\ConnectionInterface;
use React\Socket\SocketServer;

/**
 * A minimal signaling server that attaches to the shared ReactPHP Kernel: it
 * accepts newline-delimited JSON signaling frames and routes them through the
 * SignalingHandler to the MediaEngine.
 *
 * The raw-TCP/JSON transport here is enough to drive the media engine and to
 * test the seam. For browsers, point the client at the WebSocket transport from
 * blax-software/laravel-websockets and feed decoded frames into the SAME
 * SignalingHandler — the routing/media wiring does not change.
 */
final class SignalingServer implements Server
{
    private ?SocketServer $socket = null;

    /**
     * @param  array<string,mixed>  $tls  ReactPHP TLS context, or [] for plain TCP
     */
    public function __construct(
        private string $host,
        private int $port,
        private SignalingHandler $handler,
        private array $tls = [],
    ) {}

    public function name(): string
    {
        return 'webrtc-signaling';
    }

    /** The bound listen address (e.g. "tcp://127.0.0.1:54321"), or null before boot. */
    public function address(): ?string
    {
        return $this->socket?->getAddress();
    }

    public function boot(LoopInterface $loop): void
    {
        $this->socket = SocketServerFactory::create("{$this->host}:{$this->port}", $loop, $this->tls);
        $this->socket->on('connection', fn (ConnectionInterface $conn) => $this->onConnection($conn));
    }

    private function onConnection(ConnectionInterface $conn): void
    {
        $buffer = '';

        $conn->on('data', function ($chunk) use (&$buffer, $conn) {
            $buffer .= $chunk;

            while (($pos = strpos($buffer, "\n")) !== false) {
                $line = trim(substr($buffer, 0, $pos));
                $buffer = substr($buffer, $pos + 1);

                if ($line === '') {
                    continue;
                }

                $message = json_decode($line, true);
                if (! is_array($message)) {
                    $conn->write(json_encode(['type' => 'error', 'error' => 'invalid json'])."\n");

                    continue;
                }

                $response = $this->handler->handle($message);
                if ($response !== null) {
                    $conn->write(json_encode($response)."\n");
                }
            }
        });
    }

    public function shutdown(): void
    {
        $this->socket?->close();
        $this->socket = null;
    }
}
