<?php
declare(strict_types=1);

/** WebRTC signaling store (offer/answer/ICE) — REST polling, no MCU/SFU. */
final class LiveCastSignalingRepository {
    private static bool $schemaEnsured = false;

    public static function ensureSchema(): void {
        if (self::$schemaEnsured) {
            return;
        }
        $pdo = Database::pdo();
        Database::withMigrationLock($pdo, 'live_cast_signal_messages', static function () use ($pdo): void {
            if (!Database::tableExists($pdo, 'live_cast_signal_messages')) {
                if (Database::isMysql()) {
                    $pdo->exec(
                        "CREATE TABLE live_cast_signal_messages (
                            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                            session_id VARCHAR(96) NOT NULL,
                            peer_role VARCHAR(16) NOT NULL DEFAULT 'caster',
                            msg_type VARCHAR(24) NOT NULL,
                            payload_json LONGTEXT NOT NULL,
                            created_at_ms BIGINT NOT NULL,
                            KEY idx_live_signal_session_id (session_id, id),
                            KEY idx_live_signal_session_type (session_id, msg_type, id)
                        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
                    );
                } else {
                    $pdo->exec(
                        "CREATE TABLE live_cast_signal_messages (
                            id INTEGER PRIMARY KEY AUTOINCREMENT,
                            session_id TEXT NOT NULL,
                            peer_role TEXT NOT NULL DEFAULT 'caster',
                            msg_type TEXT NOT NULL,
                            payload_json TEXT NOT NULL,
                            created_at_ms INTEGER NOT NULL
                        )"
                    );
                    $pdo->exec('CREATE INDEX IF NOT EXISTS idx_live_signal_session_id ON live_cast_signal_messages (session_id, id)');
                }
            }
        });
        self::$schemaEnsured = true;
    }

    /** @param array<string,mixed> $payload */
    public static function post(string $sessionId, string $peerRole, string $msgType, array $payload): int {
        self::ensureSchema();
        $sessionId = trim($sessionId);
        $peerRole = strtolower(trim($peerRole));
        $msgType = strtolower(trim($msgType));
        if ($sessionId === '' || $msgType === '') {
            return 0;
        }
        if (!in_array($peerRole, ['caster', 'viewer', 'mobile'], true)) {
            $peerRole = 'caster';
        }
        $now = (int) floor(microtime(true) * 1000);
        $pdo = Database::pdo();
        $stmt = $pdo->prepare(
            'INSERT INTO live_cast_signal_messages (session_id, peer_role, msg_type, payload_json, created_at_ms)
             VALUES (?, ?, ?, ?, ?)'
        );
        $stmt->execute([
            $sessionId,
            $peerRole,
            $msgType,
            safe_json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            $now,
        ]);
        return (int) $pdo->lastInsertId();
    }

    /** @return list<array<string,mixed>> */
    public static function poll(string $sessionId, int $sinceId, ?string $msgType = null, ?string $fromRole = null): array {
        self::ensureSchema();
        $sessionId = trim($sessionId);
        if ($sessionId === '') {
            return [];
        }
        $sinceId = max(0, $sinceId);
        $where = ['session_id = ?', 'id > ?'];
        $params = [$sessionId, $sinceId];
        if ($msgType !== null && trim($msgType) !== '') {
            $where[] = 'msg_type = ?';
            $params[] = strtolower(trim($msgType));
        }
        if ($fromRole !== null && trim($fromRole) !== '') {
            $where[] = 'peer_role = ?';
            $params[] = strtolower(trim($fromRole));
        }
        $pdo = Database::pdo();
        $stmt = $pdo->prepare(
            'SELECT id, session_id, peer_role, msg_type, payload_json, created_at_ms
             FROM live_cast_signal_messages
             WHERE ' . implode(' AND ', $where) . '
             ORDER BY id ASC LIMIT 200'
        );
        $stmt->execute($params);
        $out = [];
        foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $payload = json_decode((string) ($row['payload_json'] ?? ''), true);
            $out[] = [
                'id' => (int) ($row['id'] ?? 0),
                'session_id' => (string) ($row['session_id'] ?? ''),
                'peer_role' => (string) ($row['peer_role'] ?? ''),
                'msg_type' => (string) ($row['msg_type'] ?? ''),
                'payload' => is_array($payload) ? $payload : [],
                'created_at_ms' => (int) ($row['created_at_ms'] ?? 0),
            ];
        }
        return $out;
    }

    /** @return array<string,mixed>|null */
    public static function latest(string $sessionId, string $msgType, ?string $fromRole = null): ?array {
        self::ensureSchema();
        $rows = self::poll($sessionId, 0, $msgType, $fromRole);
        if ($rows === []) {
            return null;
        }
        return $rows[count($rows) - 1];
    }

    public static function purgeSession(string $sessionId): void {
        self::ensureSchema();
        $sessionId = trim($sessionId);
        if ($sessionId === '') {
            return;
        }
        $stmt = Database::pdo()->prepare('DELETE FROM live_cast_signal_messages WHERE session_id = ?');
        $stmt->execute([$sessionId]);
    }
}
