<?php

declare(strict_types=1);

namespace Blax\WebRtc;

use Blax\ReactPhpKernel\Kernel;
use Blax\WebRtc\Authorization\DefaultRoomAuthorizer;
use Blax\WebRtc\Console\Commands\InstallSfuBinary;
use Blax\WebRtc\Console\Commands\RunSfuSidecar;
use Blax\WebRtc\Console\Commands\StartWebRtcServer;
use Blax\WebRtc\Contracts\CallEventListener;
use Blax\WebRtc\Contracts\MediaEngine;
use Blax\WebRtc\Contracts\RecordingStore;
use Blax\WebRtc\Contracts\RoomAuthorizer;
use Blax\WebRtc\Contracts\RoomRegistry;
use Blax\WebRtc\Contracts\RoomStore;
use Blax\WebRtc\Events\NullCallEventListener;
use Blax\WebRtc\Media\NullMediaEngine;
use Blax\WebRtc\Media\Rust\BinaryManager;
use Blax\WebRtc\Media\Rust\SidecarSupervisor;
use Blax\WebRtc\Realtime\OpenAiRealtimeBridge;
use Blax\WebRtc\Recording\FileRecordingStore;
use Blax\WebRtc\Rooms\ConfigRoomRegistry;
use Blax\WebRtc\Rooms\RoomManager;
use Blax\WebRtc\Rooms\Stores\ArrayRoomStore;
use Blax\Ws\WebSocketServer;
use Illuminate\Support\ServiceProvider;

class WebRtcServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/webrtc.php', 'webrtc');

        // Resolve the configured media engine wherever a MediaEngine is type-hinted.
        $this->app->bind(MediaEngine::class, function ($app) {
            $class = (string) config('webrtc.media_engine', NullMediaEngine::class);

            return $app->make($class);
        });

        // Room authorization (private/presence) + the call-event log — host-overridable.
        $this->app->bind(RoomAuthorizer::class, function ($app) {
            return $app->make((string) config('webrtc.authorizer', DefaultRoomAuthorizer::class));
        });
        $this->app->bind(CallEventListener::class, function ($app) {
            return $app->make((string) config('webrtc.events', NullCallEventListener::class));
        });

        // Room membership store — swappable so a fork-per-message host (learn-atc's
        // existing WS) can back it with Cache/Redis instead of process memory. The
        // RoomManager is per-resolve so a fresh worker reads current state.
        $this->app->bind(RoomStore::class, function ($app) {
            return $app->make((string) config('webrtc.room_store', ArrayRoomStore::class));
        });
        $this->app->bind(RoomManager::class, function ($app) {
            return new RoomManager($app->make(RoomStore::class));
        });

        // The backend-defined room registry: which rooms exist and each room's
        // transport (p2p vs server-handled). Defaults to the config-driven registry;
        // a host binds its own (e.g. a DB/Eloquent-backed registry so rooms are
        // created + edited at runtime) via `webrtc.room_registry`.
        $this->app->bind(RoomRegistry::class, function ($app) {
            $class = config('webrtc.room_registry');

            return $class
                ? $app->make($class)
                : new ConfigRoomRegistry((array) config('webrtc.rooms', []));
        });

        // Where a call's audio is stored (per participant). Host may override the class.
        $this->app->bind(RecordingStore::class, function () {
            $recording = (array) config('webrtc.recording', []);

            return new FileRecordingStore(
                (string) ($recording['path'] ?? 'webrtc'),
                (string) ($recording['format'] ?? 'webm'),
            );
        });

        // The Rust SFU sidecar (backend-hosted rooms): binary management + the
        // process supervisor RustMediaEngine boots plug-and-play on first use.
        $this->app->singleton(BinaryManager::class, function () {
            return new BinaryManager((array) config('webrtc.sfu', []));
        });
        $this->app->singleton(SidecarSupervisor::class, function ($app) {
            return new SidecarSupervisor($app->make(BinaryManager::class), (array) config('webrtc.sfu', []));
        });

        // Realtime AI bridge (provider hidden from the browser, model swappable server-side).
        $this->app->bind(OpenAiRealtimeBridge::class, function () {
            $endpoint = (string) config('webrtc.bridge.openai.endpoint', 'wss://api.openai.com/v1/realtime');

            return new OpenAiRealtimeBridge(endpoint: $endpoint);
        });
    }

    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/webrtc.php' => $this->app->configPath('webrtc.php'),
            ], 'webrtc-config');

            // The Rust SFU sidecar commands have no extra PHP dependencies.
            $this->commands([
                InstallSfuBinary::class,
                RunSfuSidecar::class,
            ]);

            // The bundled ReactPHP relay server needs blax-software/laravel-ws +
            // reactphp-kernel (dev-only deps). A host that only consumes the domain
            // layer (rooms/recording/realtime bridge) on its OWN transport won't have
            // them — checking the dependency classes first avoids autoloading the
            // command (and its imports) when they're absent.
            if (class_exists(WebSocketServer::class) && class_exists(Kernel::class)) {
                $this->commands([
                    StartWebRtcServer::class,
                ]);
            }
        }
    }
}
