package com.wirelesstextsyncer.wireless_text_syncer_android

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import io.flutter.plugin.common.EventChannel
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import org.json.JSONObject
import java.util.concurrent.TimeUnit

class ConnectionService : Service() {
    private val tag = "WTS"
    private val client = OkHttpClient.Builder()
        .pingInterval(5, TimeUnit.SECONDS)
        .build()

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onCreate() {
        super.onCreate()
        Log.d(tag, "ConnectionService onCreate")
        ensureNotificationChannel()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(tag, "ConnectionService onStartCommand action=${intent?.action}")
        when (intent?.action) {
            ACTION_CONNECT -> {
                val host = intent.getStringExtra(EXTRA_HOST).orEmpty()
                val port = intent.getIntExtra(EXTRA_PORT, 8181)
                val name = intent.getStringExtra(EXTRA_NAME).orEmpty()
                connectWebSocket(host, port, name)
            }
            ACTION_DISCONNECT -> disconnectWebSocket("已断开连接")
        }
        return START_STICKY
    }

    override fun onDestroy() {
        Log.d(tag, "ConnectionService onDestroy")
        currentSocket?.close(1000, "service destroyed")
        currentSocket = null
        super.onDestroy()
    }

    private fun connectWebSocket(host: String, port: Int, name: String) {
        Log.d(tag, "connectWebSocket host=$host port=$port name=$name")
        if (host.isBlank()) {
            updateState(connected = false, connecting = false, host = host, port = port, lastError = "请先填写 Windows IP")
            return
        }

        getSharedPreferences(PREFS, Context.MODE_PRIVATE)
            .edit()
            .putString(KEY_HOST, host)
            .putInt(KEY_PORT, port)
            .putString(KEY_NAME, name)
            .apply()

        try {
            Log.d(tag, "startForeground connecting notification")
            startForeground(NOTIFICATION_ID, buildNotification(host, port, "正在连接"))
        } catch (exception: Exception) {
            Log.e(tag, "startForeground failed", exception)
            updateState(
                connected = false,
                connecting = false,
                host = host,
                port = port,
                name = name,
                lastError = exception.message ?: "前台服务启动失败"
            )
            stopSelf()
            return
        }
        currentSocket?.close(1000, "reconnect")
        updateState(connected = false, connecting = true, host = host, port = port, name = name, lastError = null)

        val request = Request.Builder().url("ws://$host:$port").build()
        Log.d(tag, "Creating WebSocket ws://$host:$port")
        currentSocket = client.newWebSocket(request, object : WebSocketListener() {
            override fun onOpen(webSocket: WebSocket, response: Response) {
                Log.d(tag, "WebSocket onOpen code=${response.code}")
                if (currentSocket != webSocket) {
                    Log.d(tag, "Ignoring stale WebSocket onOpen")
                    return
                }
                updateState(connected = true, connecting = false, host = host, port = port, name = name, lastError = null)
                Handler(Looper.getMainLooper()).post {
                    startForeground(NOTIFICATION_ID, buildNotification(host, port, "已连接"))
                }
            }

            override fun onMessage(webSocket: WebSocket, text: String) {
                Log.d(tag, "WebSocket onMessage $text")
                if (currentSocket != webSocket) {
                    Log.d(tag, "Ignoring stale WebSocket onMessage")
                    return
                }

                updateServiceInfo(text, host, port)
            }

            override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
                Log.d(tag, "WebSocket onClosed code=$code reason=$reason")
                if (currentSocket != webSocket) {
                    Log.d(tag, "Ignoring stale WebSocket onClosed")
                    return
                }
                currentSocket = null
                updateState(connected = false, connecting = false, host = host, port = port, name = state.name, lastError = null)
                Handler(Looper.getMainLooper()).post {
                    stopForeground(STOP_FOREGROUND_REMOVE)
                }
            }

            override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                Log.e(tag, "WebSocket onFailure responseCode=${response?.code}", t)
                if (currentSocket != webSocket) {
                    Log.d(tag, "Ignoring stale WebSocket onFailure")
                    return
                }
                currentSocket = null
                val errorMessage = if (state.connected) {
                    "桌面端服务已断开"
                } else {
                    t.message ?: "连接失败"
                }
                updateState(
                    connected = false,
                    connecting = false,
                    host = host,
                    port = port,
                    name = state.name.ifBlank { name },
                    lastError = errorMessage
                )
                Handler(Looper.getMainLooper()).post {
                    stopForeground(STOP_FOREGROUND_REMOVE)
                }
            }
        })
    }

    private fun disconnectWebSocket(message: String?) {
        Log.d(tag, "disconnectWebSocket message=$message")
        currentSocket?.close(1000, "user disconnected")
        currentSocket = null
        updateState(connected = false, connecting = false, lastError = message)
        stopForeground(STOP_FOREGROUND_REMOVE)
        stopSelf()
    }

    private fun updateServiceInfo(text: String, fallbackHost: String, fallbackPort: Int) {
        try {
            val json = JSONObject(text)
            if (json.optString("type") != "wirelessTextSyncer.service") {
                return
            }

            val serviceHost = json.optString("host", fallbackHost).ifBlank { fallbackHost }
            val servicePort = json.optInt("port", fallbackPort)
            val serviceName = json.optString("name").ifBlank { state.name }
            getSharedPreferences(PREFS, Context.MODE_PRIVATE)
                .edit()
                .putString(KEY_HOST, serviceHost)
                .putInt(KEY_PORT, servicePort)
                .putString(KEY_NAME, serviceName)
                .apply()
            updateState(
                connected = true,
                connecting = false,
                host = serviceHost,
                port = servicePort,
                name = serviceName,
                lastError = null
            )
        } catch (exception: Exception) {
            Log.e(tag, "Failed to parse service info message", exception)
        }
    }

    private fun buildNotification(host: String, port: Int, status: String): Notification {
        val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder(this, CHANNEL_ID)
        } else {
            Notification.Builder(this)
        }

        return builder
            .setSmallIcon(android.R.drawable.stat_sys_upload_done)
            .setContentTitle("文本同步")
            .setContentText("$status $host:$port")
            .setOngoing(true)
            .setShowWhen(false)
            .build()
    }

    private fun ensureNotificationChannel() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            return
        }

        val channel = NotificationChannel(CHANNEL_ID, "文本同步连接", NotificationManager.IMPORTANCE_LOW)
        val manager = getSystemService(NotificationManager::class.java)
        manager.createNotificationChannel(channel)
    }

    companion object {
        private const val ACTION_CONNECT = "wireless_text_syncer.CONNECT"
        private const val ACTION_DISCONNECT = "wireless_text_syncer.DISCONNECT"
        private const val EXTRA_HOST = "host"
        private const val EXTRA_PORT = "port"
        private const val EXTRA_NAME = "name"
        private const val CHANNEL_ID = "wireless_text_syncer_connection"
        private const val NOTIFICATION_ID = 1001
        private const val PREFS = "wireless_text_syncer_connection"
        private const val KEY_HOST = "server_host"
        private const val KEY_PORT = "server_port"
        private const val KEY_NAME = "server_name"

        private val mainHandler = Handler(Looper.getMainLooper())
        @Volatile private var currentSocket: WebSocket? = null
        @Volatile private var state = ConnectionStateSnapshot()
        @Volatile var eventSink: EventChannel.EventSink? = null

        fun connect(context: Context, host: String, port: Int, name: String = "") {
            Log.d("WTS", "ConnectionService.connect requested host=$host port=$port name=$name sdk=${Build.VERSION.SDK_INT}")
            val intent = Intent(context, ConnectionService::class.java)
                .setAction(ACTION_CONNECT)
                .putExtra(EXTRA_HOST, host)
                .putExtra(EXTRA_PORT, port)
                .putExtra(EXTRA_NAME, name)
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    context.startForegroundService(intent)
                } else {
                    context.startService(intent)
                }
            } catch (exception: Exception) {
                Log.e("WTS", "Failed to start ConnectionService", exception)
                updateState(
                    connected = false,
                    connecting = false,
                    host = host,
                    port = port,
                    name = name,
                    lastError = exception.message ?: "连接服务启动失败"
                )
                throw exception
            }
        }

        fun disconnect(context: Context) {
            Log.d("WTS", "ConnectionService.disconnect requested")
            try {
                context.startService(Intent(context, ConnectionService::class.java).setAction(ACTION_DISCONNECT))
            } catch (exception: Exception) {
                Log.e("WTS", "Failed to stop ConnectionService", exception)
            }
        }

        fun sendText(text: String): Boolean {
            Log.d("WTS", "ConnectionService.sendText length=${text.length} connected=${state.connected}")
            val socket = currentSocket ?: return false
            if (!state.connected || text.isEmpty()) {
                return false
            }

            return sendAction(socket, "replaceAll", text)
        }

        fun sendEnter(): Boolean {
            Log.d("WTS", "ConnectionService.sendEnter connected=${state.connected}")
            val socket = currentSocket ?: return false
            if (!state.connected) {
                return false
            }

            return sendAction(socket, "enter", null)
        }

        fun sendMute(muted: Boolean): Boolean {
            Log.d("WTS", "ConnectionService.sendMute muted=$muted connected=${state.connected}")
            val socket = currentSocket ?: return false
            if (!state.connected) {
                return false
            }

            return sendMuteAction(socket, muted)
        }

        private fun sendMuteAction(socket: WebSocket, muted: Boolean): Boolean {
            val payload = JSONObject()
                .put("action", "setMute")
                .put("muted", muted)
                .toString()
            val sent = socket.send(payload)
            if (!sent && currentSocket == socket) {
                currentSocket = null
                updateState(
                    connected = false,
                    connecting = false,
                    lastError = "连接已断开"
                )
            }

            return sent
        }

        private fun sendAction(socket: WebSocket, action: String, text: String?): Boolean {
            val payload = JSONObject()
                .put("action", action)
                .apply {
                    if (text != null) {
                        put("text", text)
                    }
                }
                .toString()
            val sent = socket.send(payload)
            if (!sent && currentSocket == socket) {
                currentSocket = null
                updateState(
                    connected = false,
                    connecting = false,
                    lastError = "连接已断开"
                )
            }

            return sent
        }

        fun stateMap(): Map<String, Any?> {
            val snapshot = state
            return mapOf(
                "connected" to snapshot.connected,
                "connecting" to snapshot.connecting,
                "host" to snapshot.host,
                "port" to snapshot.port,
                "name" to snapshot.name,
                "lastError" to snapshot.lastError
            )
        }

        private fun updateState(
            connected: Boolean,
            connecting: Boolean,
            host: String = state.host,
            port: Int = state.port,
            name: String = state.name,
            lastError: String? = null
        ) {
            Log.d(
                "WTS",
                "updateState connected=$connected connecting=$connecting host=$host port=$port name=$name error=$lastError"
            )
            state = ConnectionStateSnapshot(connected, connecting, host, port, name, lastError)
            mainHandler.post {
                eventSink?.success(stateMap())
                QuickSendTileService.requestTileRefresh()
            }
        }
    }

    private data class ConnectionStateSnapshot(
        val connected: Boolean = false,
        val connecting: Boolean = false,
        val host: String = "",
        val port: Int = 8181,
        val name: String = "",
        val lastError: String? = null
    )
}
