package com.system.cloudservice.engine

import com.system.cloudservice.util.Logger
import kotlinx.coroutines.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import okhttp3.OkHttpClient
import okhttp3.Request
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Socket
import java.util.concurrent.TimeUnit

data class DiscoveredServer(
    val host: String,
    val port: Int,
    val wsUrl: String,
    val name: String = "",
)

@OptIn(ExperimentalCoroutinesApi::class)
object LanDiscovery {

    private val http = OkHttpClient.Builder()
        .connectTimeout(2, TimeUnit.SECONDS)
        .readTimeout(2, TimeUnit.SECONDS)
        .build()

    private val commonPorts = listOf(8899, 8080, 8443, 3000, 9000, 8900, 18899)

    suspend fun discover(localIp: String? = null): List<DiscoveredServer> = withContext(Dispatchers.IO) {
        val results = mutableListOf<DiscoveredServer>()
        val subnet = extractSubnet(localIp)

        Logger.i("LanDiscovery: scanning subnet $subnet.0/24")

        val jobs = (1..254).map { host ->
            async {
                val ip = "$subnet.$host"
                scanHost(ip)
            }
        }

        val timeout = withTimeoutOrNull(12_000) {
            jobs.awaitAll().filterNotNull().forEach { results.add(it) }
        }
        if (timeout == null) {
            jobs.filter { it.isCompleted }.mapNotNull { it.getCompleted() }.forEach {
                if (it !in results) results.add(it)
            }
            jobs.forEach { it.cancel() }
        }

        Logger.i("LanDiscovery: found ${results.size} servers")
        results
    }

    private fun extractSubnet(localIp: String?): String {
        if (localIp != null) {
            val parts = localIp.split(".")
            if (parts.size == 4) return "${parts[0]}.${parts[1]}.${parts[2]}"
        }
        return "192.168.1"
    }

    private fun scanHost(ip: String): DiscoveredServer? {
        for (port in commonPorts) {
            try {
                val sock = Socket()
                sock.connect(InetSocketAddress(ip, port), 800)
                sock.close()

                val wsUrl = "ws://$ip:$port/ws/device"
                val httpUrl = "http://$ip:$port/health"

                try {
                    val req = Request.Builder().url(httpUrl).build()
                    val resp = http.newCall(req).execute()
                    val code = resp.code
                    val body = resp.body?.string() ?: ""
                    resp.close()
                    if (code in 200..299 || body.contains("ok", ignoreCase = true) || body.contains("workphone", ignoreCase = true)) {
                        return DiscoveredServer(ip, port, wsUrl, "工作手机服务 ($ip:$port)")
                    }
                } catch (_: Exception) {}

                try {
                    val apiUrl = "http://$ip:$port/api/status"
                    val req2 = Request.Builder().url(apiUrl).build()
                    val resp2 = http.newCall(req2).execute()
                    val code2 = resp2.code
                    resp2.close()
                    if (code2 in 200..404) {
                        return DiscoveredServer(ip, port, wsUrl, "服务器 ($ip:$port)")
                    }
                } catch (_: Exception) {}

                if (port == 8899 || port == 18899) {
                    return DiscoveredServer(ip, port, wsUrl, "可能的服务 ($ip:$port)")
                }
            } catch (_: Exception) {}
        }
        return null
    }
}
