package com.omixlab.lckcontrol.p2p.webrtc

import android.util.Log
import com.omixlab.lckcontrol.data.local.TokenStore
import com.omixlab.lckcontrol.p2p.discovery.P2pPreferences
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import okhttp3.*
import javax.inject.Inject
import javax.inject.Singleton

@JsonClass(generateAdapter = true)
data class SignalingMessage(
    val type: String,
    val from: String? = null,
    val to: String? = null,
    val sdp: String? = null,
    val sdpType: String? = null,
    val candidate: IceCandidateDto? = null,
    val deviceId: String? = null,
    val deviceType: String? = null,
    val deviceName: String? = null,
)

@Singleton
class RemoteSignalingClient @Inject constructor(
    private val okHttpClient: OkHttpClient,
    private val moshi: Moshi,
    private val tokenStore: TokenStore,
    private val p2pPreferences: P2pPreferences,
) {
    companion object {
        private const val TAG = "RemoteSignaling"
        private const val BASE_URL = "wss://lck.omigame.dev/"
    }

    private var webSocket: WebSocket? = null
    var onOfferReceived: ((String, String) -> Unit)? = null // from, sdp
    var onIceCandidateReceived: ((String, IceCandidateDto) -> Unit)? = null

    fun connect() {
        val token = tokenStore.getJwt() ?: return

        val request = Request.Builder()
            .url("${BASE_URL}signaling/ws?token=$token")
            .build()

        webSocket = okHttpClient.newWebSocket(request, object : WebSocketListener() {
            override fun onOpen(ws: WebSocket, response: Response) {
                Log.d(TAG, "Signaling connected, registering device")
                send(SignalingMessage(
                    type = "register_device",
                    deviceId = p2pPreferences.stableDeviceId,
                    deviceType = "QUEST",
                    deviceName = android.os.Build.MODEL,
                ))
            }

            override fun onMessage(ws: WebSocket, text: String) {
                try {
                    val msg = moshi.adapter(SignalingMessage::class.java).fromJson(text) ?: return
                    when (msg.type) {
                        "offer" -> {
                            msg.sdp?.let { sdp ->
                                onOfferReceived?.invoke(msg.from ?: "", sdp)
                            }
                        }
                        "ice_candidate" -> {
                            msg.candidate?.let { candidate ->
                                onIceCandidateReceived?.invoke(msg.from ?: "", candidate)
                            }
                        }
                        "registered" -> Log.d(TAG, "Device registered on backend")
                    }
                } catch (e: Exception) {
                    Log.e(TAG, "Parse error", e)
                }
            }

            override fun onFailure(ws: WebSocket, t: Throwable, response: Response?) {
                Log.e(TAG, "Signaling failure", t)
            }

            override fun onClosed(ws: WebSocket, code: Int, reason: String) {
                Log.d(TAG, "Signaling closed: $code $reason")
            }
        })
    }

    fun sendAnswer(toDeviceId: String, sdp: String) {
        send(SignalingMessage(
            type = "answer",
            to = toDeviceId,
            sdp = sdp,
            sdpType = "answer",
        ))
    }

    fun sendIceCandidate(toDeviceId: String, candidate: IceCandidateDto) {
        send(SignalingMessage(
            type = "ice_candidate",
            to = toDeviceId,
            candidate = candidate,
        ))
    }

    fun disconnect() {
        webSocket?.close(1000, "Disconnect")
        webSocket = null
    }

    private fun send(message: SignalingMessage) {
        val json = moshi.adapter(SignalingMessage::class.java).toJson(message)
        webSocket?.send(json)
    }
}
