package com.omixlab.lckcontrol.sdk

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.hardware.HardwareBuffer
import android.os.IBinder
import android.os.ParcelFileDescriptor
import com.omixlab.lckcontrol.shared.ConnectedClientInfo
import com.omixlab.lckcontrol.shared.ILckControlCallback
import com.omixlab.lckcontrol.shared.ILckControlService
import com.omixlab.lckcontrol.shared.ILckStreamingCallback
import com.omixlab.lckcontrol.shared.ILckStreamingService
import com.omixlab.lckcontrol.shared.LinkedAccount
import com.omixlab.lckcontrol.shared.StreamPlan
import com.omixlab.lckcontrol.shared.StreamPlanConfig
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class LckControlClient(private val context: Context) {

    companion object {
        private const val SERVICE_PACKAGE = "com.omixlab.lckcontrol"
        private const val SERVICE_CLASS = "$SERVICE_PACKAGE.service.LckControlService"
        private const val PERMISSION = "$SERVICE_PACKAGE.permission.USE_LCK_CONTROL"
        private const val ACTION_BIND_STREAMING = "$SERVICE_PACKAGE.BIND_STREAMING"
    }

    private var service: ILckControlService? = null
    private var streamingService: ILckStreamingService? = null
    private var clientId: String? = null

    private val _connected = MutableStateFlow(false)
    val connected: StateFlow<Boolean> = _connected.asStateFlow()

    private val _authenticated = MutableStateFlow(false)
    val authenticated: StateFlow<Boolean> = _authenticated.asStateFlow()

    private val _streamPlans = MutableStateFlow<List<StreamPlan>>(emptyList())
    val streamPlans: StateFlow<List<StreamPlan>> = _streamPlans.asStateFlow()

    private val _streamingState = MutableStateFlow("IDLE")
    val streamingState: StateFlow<String> = _streamingState.asStateFlow()

    private val _streamingConnected = MutableStateFlow(false)
    val streamingConnected: StateFlow<Boolean> = _streamingConnected.asStateFlow()

    private val callback = object : ILckControlCallback.Stub() {
        override fun onStreamPlansChanged(plans: List<StreamPlan>) {
            _streamPlans.value = plans
        }

        override fun onStreamPlanUpdated(plan: StreamPlan) {
            _streamPlans.value = _streamPlans.value.map {
                if (it.planId == plan.planId) plan else it
            }
        }

        override fun onClientRegistered(id: String) {}
        override fun onClientUnregistered(id: String) {}

        override fun onAuthStateChanged(authenticated: Boolean) {
            _authenticated.value = authenticated
        }
    }

    private val streamingCallback = object : ILckStreamingCallback.Stub() {
        override fun onBufferReleased(bufferIndex: Int) {
            onBufferReleasedListener?.invoke(bufferIndex)
        }

        override fun onStreamingStateChanged(state: String) {
            _streamingState.value = state
        }

        override fun onStreamingError(code: Int, message: String) {
            onStreamingErrorListener?.invoke(code, message)
        }

        override fun onStreamingStats(videoBitrate: Long, audioBitrate: Long, fps: Int, droppedFrames: Int) {
            onStreamingStatsListener?.invoke(videoBitrate, audioBitrate, fps, droppedFrames)
        }
    }

    /** Listener for buffer release events (game can reuse the buffer). */
    var onBufferReleasedListener: ((Int) -> Unit)? = null

    /** Listener for streaming errors. */
    var onStreamingErrorListener: ((Int, String) -> Unit)? = null

    /** Listener for streaming stats updates. */
    var onStreamingStatsListener: ((Long, Long, Int, Int) -> Unit)? = null

    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
            service = ILckControlService.Stub.asInterface(binder)
            service?.registerCallback(callback)
            _connected.value = true
            _authenticated.value = service?.isAuthenticated ?: false
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            service = null
            clientId = null
            _connected.value = false
            _authenticated.value = false
        }
    }

    private val streamingConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
            streamingService = ILckStreamingService.Stub.asInterface(binder)
            streamingService?.registerStreamingCallback(streamingCallback)
            _streamingConnected.value = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            streamingService = null
            _streamingConnected.value = false
            _streamingState.value = "IDLE"
        }
    }

    fun bind(): Boolean {
        val intent = Intent().apply {
            component = ComponentName(SERVICE_PACKAGE, SERVICE_CLASS)
        }
        return context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    fun unbind() {
        service?.let { svc ->
            clientId?.let { svc.unregisterClient(it) }
            svc.unregisterCallback(callback)
        }
        try {
            context.unbindService(connection)
        } catch (_: IllegalArgumentException) {
            // Already unbound
        }
        service = null
        clientId = null
        _connected.value = false
        _authenticated.value = false
    }

    // ── Streaming service ────────────────────────────────

    fun bindStreaming(): Boolean {
        val intent = Intent(ACTION_BIND_STREAMING).apply {
            component = ComponentName(SERVICE_PACKAGE, SERVICE_CLASS)
        }
        return context.bindService(intent, streamingConnection, Context.BIND_AUTO_CREATE)
    }

    fun unbindStreaming() {
        streamingService?.let { svc ->
            svc.unregisterStreamingCallback(streamingCallback)
        }
        try {
            context.unbindService(streamingConnection)
        } catch (_: IllegalArgumentException) {}
        streamingService = null
        _streamingConnected.value = false
        _streamingState.value = "IDLE"
    }

    // ── Texture pool ─────────────────────────────────────

    fun registerTexturePool(buffers: Array<HardwareBuffer>, width: Int, height: Int, format: Int) {
        streamingService?.registerTexturePool(buffers, width, height, format)
    }

    fun unregisterTexturePool() {
        streamingService?.unregisterTexturePool()
    }

    // ── Frame submission (called from game render thread) ──

    fun submitVideoFrame(bufferIndex: Int, timestampNs: Long, gpuFenceFd: ParcelFileDescriptor?) {
        streamingService?.submitVideoFrame(bufferIndex, timestampNs, gpuFenceFd)
    }

    fun submitAudioFrame(pcmData: ByteArray, timestampNs: Long, sampleRate: Int, channels: Int, bitsPerSample: Int) {
        streamingService?.submitAudioFrame(pcmData, timestampNs, sampleRate, channels, bitsPerSample)
    }

    fun isStreaming(): Boolean {
        return streamingService?.isStreaming ?: false
    }

    // ── Auth ────────────────────────────────────────────

    fun isAuthenticated(): Boolean {
        return service?.isAuthenticated ?: false
    }

    fun login() {
        service?.login()
    }

    // ── Client registration ─────────────────────────────

    fun registerAsClient(clientName: String, packageName: String): String? {
        val id = service?.registerClient(clientName, packageName)
        clientId = id
        return id
    }

    fun getConnectedClients(): List<ConnectedClientInfo> {
        return service?.connectedClients ?: emptyList()
    }

    // ── Accounts ────────────────────────────────────────

    fun getLinkedAccounts(): List<LinkedAccount> {
        return service?.linkedAccounts ?: emptyList()
    }

    // ── Stream plans ────────────────────────────────────

    fun getStreamPlans(): List<StreamPlan> {
        return service?.streamPlans ?: emptyList()
    }

    fun getStreamPlan(planId: String): StreamPlan? {
        return service?.getStreamPlan(planId)
    }

    fun createStreamPlan(config: StreamPlanConfig): StreamPlan? {
        return service?.createStreamPlan(config)
    }

    fun createDefaultPlan(clientName: String): StreamPlan? {
        return service?.createDefaultPlan(clientName)
    }

    fun prepareStreamPlan(planId: String): StreamPlan? {
        return service?.prepareStreamPlan(planId)
    }

    fun startStreamPlan(planId: String): Boolean {
        return service?.startStreamPlan(planId) ?: false
    }

    fun endStreamPlan(planId: String): Boolean {
        return service?.endStreamPlan(planId) ?: false
    }

    fun setClientActivePlan(planId: String) {
        clientId?.let { service?.setClientActivePlan(it, planId) }
    }
}
