package dev.kaizen.app.live

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Binder
import android.os.IBinder
import android.util.Log
import dev.kaizen.app.MainActivity
import dev.kaizen.app.R
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

/**
 * Foreground Service for Live Call — keeps mic + WS alive when the app is
 * backgrounded or the screen rotates. Shows a persistent notification with
 * call duration and an end button.
 *
 * Lifecycle: started by ChatScreen → binds → [startCall] → audio flows
 * → [endCall] or notification "Beenden" → cleanup → stopSelf.
 */
class LiveCallService : Service() {

    companion object {
        private const val TAG = "LiveCallService"
        private const val CHANNEL_ID = "kaizen_live_call"
        private const val NOTIFICATION_ID = 9001
        private const val ACTION_END = "dev.kaizen.app.LIVE_CALL_END"

        fun intent(context: Context): Intent = Intent(context, LiveCallService::class.java)
    }

    // ─── Public state ─────────────────────────────────────────────────────

    private val _status = MutableStateFlow(CallStatus.IDLE)
    val status: StateFlow<CallStatus> = _status

    private val _subtitle = MutableStateFlow("")
    val subtitle: StateFlow<String> = _subtitle

    private val _amplitude = MutableStateFlow(0f)
    val amplitude: StateFlow<Float> = _amplitude

    private val _userSpeaking = MutableStateFlow(false)
    val userSpeaking: StateFlow<Boolean> = _userSpeaking

    var onTurnEnd: ((ServerEvent.TurnEnd) -> Unit)? = null
    var onError: ((String, String?) -> Unit)? = null

    // ─── Internal ─────────────────────────────────────────────────────────

    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
    private var client: LiveClient? = null
    private var audio: AudioPipeline? = null
    private var durationJob: Job? = null
    private var startTimeMs = 0L

    // ─── Binder ───────────────────────────────────────────────────────────

    inner class LocalBinder : Binder() {
        val service: LiveCallService get() = this@LiveCallService
    }

    private val binder = LocalBinder()
    override fun onBind(intent: Intent?): IBinder = binder

    // ─── Lifecycle ────────────────────────────────────────────────────────

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent?.action == ACTION_END) {
            endCall()
            return START_NOT_STICKY
        }
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        endCall()
        scope.cancel()
        super.onDestroy()
    }

    // ─── Start / End ──────────────────────────────────────────────────────

    fun startCall(baseUrl: String, token: String, model: String, voice: String, conversationId: String?) {
        if (_status.value != CallStatus.IDLE) return

        _status.value = CallStatus.CONNECTING

        startForeground(NOTIFICATION_ID, buildNotification("Verbinde…"), ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE)

        val pipeline = AudioPipeline(this)
        audio = pipeline

        val liveClient = LiveClient(baseUrl, token, scope)
        client = liveClient

        pipeline.onAmplitude = { amp -> _amplitude.value = amp }

        scope.launch {
            liveClient.events.collect { event -> handleEvent(event, pipeline) }
        }

        pipeline.onChunk = { base64 -> liveClient.sendAudio(base64) }

        liveClient.connect(model, voice, conversationId)
    }

    fun endCall() {
        if (_status.value == CallStatus.IDLE) return

        client?.end()
        audio?.release()
        durationJob?.cancel()

        client?.dispose()
        client = null
        audio = null
        durationJob = null

        _status.value = CallStatus.IDLE
        _subtitle.value = ""
        _amplitude.value = 0f
        _userSpeaking.value = false

        stopForeground(STOP_FOREGROUND_REMOVE)
        stopSelf()
    }

    fun sendText(text: String) {
        client?.sendText(text)
    }

    fun interrupt() {
        client?.interrupt()
    }

    // ─── Event handling ───────────────────────────────────────────────────

    private fun handleEvent(event: ServerEvent, pipeline: AudioPipeline) {
        when (event) {
            is ServerEvent.Ready -> {
                _status.value = CallStatus.LISTENING

                if (!pipeline.initPlayback()) {
                    Log.e(TAG, "Playback init failed")
                }
                if (!pipeline.startCapture(scope)) {
                    Log.e(TAG, "Capture init failed")
                    endCall()
                    return
                }

                startTimeMs = System.currentTimeMillis()
                startDurationTimer()
                updateNotification("Anruf aktiv")
            }

            is ServerEvent.Resumed -> {
                _status.value = CallStatus.LISTENING
                updateNotification("Anruf fortgesetzt")
            }

            is ServerEvent.Audio -> {
                if (_status.value != CallStatus.SPEAKING) {
                    _status.value = CallStatus.SPEAKING
                    pipeline.setDucking(true)
                }
                pipeline.playChunk(event.data)
            }

            is ServerEvent.Text -> {
                _subtitle.value += event.delta
            }

            is ServerEvent.Transcript -> {
                if (event.final) {
                    _subtitle.value = event.text
                }
            }

            is ServerEvent.Vad -> {
                _userSpeaking.value = event.speaking
                if (event.speaking) {
                    _status.value = CallStatus.LISTENING
                    _subtitle.value = ""
                    pipeline.setDucking(false)
                }
            }

            is ServerEvent.Interrupted -> {
                pipeline.flushPlayback()
                pipeline.setDucking(false)
                _status.value = CallStatus.LISTENING
            }

            is ServerEvent.TurnEnd -> {
                pipeline.setDucking(false)
                _status.value = CallStatus.LISTENING
                onTurnEnd?.invoke(event)
            }

            is ServerEvent.ToolCall -> {
                if (event.status == "running") {
                    _subtitle.value = "${event.name}…"
                }
            }

            is ServerEvent.Backpressure -> {
                // LiveClient handles throttling internally
            }

            is ServerEvent.GoAway -> {
                if (event.reason == "timeout") {
                    endCall()
                }
            }

            is ServerEvent.Error -> {
                onError?.invoke(event.code, event.message)
                if (event.code in setOf("auth_failed", "restricted_model", "restricted_feature", "budget_exceeded", "concurrent_limit")) {
                    endCall()
                }
            }
        }
    }

    // ─── Duration timer ───────────────────────────────────────────────────

    private fun startDurationTimer() {
        durationJob?.cancel()
        durationJob = scope.launch {
            while (isActive) {
                delay(1000)
                val elapsed = (System.currentTimeMillis() - startTimeMs) / 1000
                val min = elapsed / 60
                val sec = elapsed % 60
                updateNotification(String.format("Anruf · %d:%02d", min, sec))
            }
        }
    }

    // ─── Notification ─────────────────────────────────────────────────────

    private fun createNotificationChannel() {
        val channel = NotificationChannel(
            CHANNEL_ID,
            getString(R.string.app_name),
            NotificationManager.IMPORTANCE_LOW,
        ).apply {
            description = "Live Call"
            setShowBadge(false)
        }
        getSystemService(NotificationManager::class.java)?.createNotificationChannel(channel)
    }

    private fun buildNotification(text: String): Notification {
        val openIntent = PendingIntent.getActivity(
            this, 0,
            Intent(this, MainActivity::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
            },
            PendingIntent.FLAG_IMMUTABLE,
        )

        val endIntent = PendingIntent.getService(
            this, 1,
            Intent(this, LiveCallService::class.java).apply { action = ACTION_END },
            PendingIntent.FLAG_IMMUTABLE,
        )

        return Notification.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle(getString(R.string.app_name))
            .setContentText(text)
            .setContentIntent(openIntent)
            .addAction(Notification.Action.Builder(null, "Beenden", endIntent).build())
            .setOngoing(true)
            .build()
    }

    private fun updateNotification(text: String) {
        getSystemService(NotificationManager::class.java)
            ?.notify(NOTIFICATION_ID, buildNotification(text))
    }
}

enum class CallStatus {
    IDLE,
    CONNECTING,
    LISTENING,
    SPEAKING,
}
