package com.jidan.jiaoyi.data

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL

data class AiReply(
    val answer: String,
    val suggestions: List<String> = emptyList(),
    val localBrainAvailable: Boolean = false,
    val source: String? = null,
)

data class AppUpdateInfo(
    val versionCode: Int,
    val versionName: String,
    val pageUrl: String,
    val notes: String,
)

data class AiContributionReply(
    val answer: String,
    val autoApplied: Boolean,
    val status: String,
)

class AiClient {
    suspend fun ask(question: String, sessionId: String): AiReply = withContext(Dispatchers.IO) {
        val deadline = System.currentTimeMillis() + 40_000L
        var response = request(
            method = "POST",
            url = "$API_ROOT/api/assistant",
            body = JSONObject()
                .put("question", question.take(160))
                .put("sessionId", sessionId)
                .put("stopId", "global")
                .put("stopName", "胶州")
                .put("arrivals", org.json.JSONArray())
                .toString(),
        )
        if (response.status == 429) {
            return@withContext AiReply("问得有点快，歇一分钟再来。")
        }
        if (response.status !in setOf(200, 202)) {
            throw AiUnavailableException("胶易暂时没接上，稍后再试。")
        }

        var payload = JSONObject(response.body)
        while (response.status == 202 && System.currentTimeMillis() < deadline) {
            val fingerprint = payload.optString("fingerprint")
            if (!fingerprint.matches(Regex("^[0-9a-f]{64}$"))) break
            delay(payload.optLong("pollAfterMs", 900L).coerceIn(600L, 1_500L))
            response = request(
                method = "GET",
                url = "$API_ROOT/api/assistant?fingerprint=$fingerprint",
            )
            if (response.status !in setOf(200, 202)) break
            payload = JSONObject(response.body)
        }

        val answer = payload.optString("answer")
            .ifBlank { payload.optString("fallbackAnswer") }
            .ifBlank { "我这会儿没答上来，换个说法试试。" }
        val suggestionArray = payload.optJSONArray("suggestions")
        val suggestions = buildList {
            if (suggestionArray != null) {
                for (index in 0 until suggestionArray.length()) {
                    suggestionArray.optString(index).takeIf(String::isNotBlank)?.let(::add)
                }
            }
        }.take(4)
        val localBrain = payload.optJSONObject("network")?.optBoolean("localBrain") ?: false
        val declaredSource = payload.optJSONArray("sources")?.optString(0).orEmpty().trim()
        val responseKind = payload.optString("kind").trim().lowercase()
        val source = when {
            declaredSource.isNotBlank() -> declaredSource.take(80)
            responseKind in setOf("local-brain", "local_brain", "localbrain") ->
                "知己本地文字大脑"
            else -> null
        }
        AiReply(
            answer = answer,
            suggestions = suggestions,
            localBrainAvailable = localBrain,
            source = source,
        )
    }

    suspend fun checkForUpdate(currentVersionCode: Int): AppUpdateInfo? =
        withContext(Dispatchers.IO) {
            val response = request(
                method = "GET",
                url = "$API_ROOT/downloads/jiaoyi-ai-latest.json",
            )
            if (response.status != 200) return@withContext null
            val payload = JSONObject(response.body)
            val versionCode = payload.optInt("versionCode", 0)
            val pageUrl = payload.optString("pageUrl")
            if (
                versionCode <= currentVersionCode ||
                !pageUrl.startsWith("https://www.jiaoyi123.cn/")
            ) {
                return@withContext null
            }
            AppUpdateInfo(
                versionCode = versionCode,
                versionName = payload.optString("versionName", "新版"),
                pageUrl = pageUrl,
                notes = payload.optString("notes").take(160),
            )
        }

    suspend fun contribute(
        message: String,
        sessionId: String,
        context: List<AiMessage>,
    ): AiContributionReply = withContext(Dispatchers.IO) {
        val contextPayload = JSONArray().apply {
            context.takeLast(2).forEach { item ->
                if (item.role == "user" || item.role == "assistant") {
                    put(
                        JSONObject()
                            .put("role", item.role)
                            .put("text", item.text.take(300)),
                    )
                }
            }
        }
        val response = request(
            method = "POST",
            url = "$API_ROOT/api/assistant/feedback",
            body = JSONObject()
                .put("context", contextPayload)
                .put("message", message.take(500))
                .put("sessionId", sessionId)
                .put("stopId", "global")
                .put("stopName", "胶州")
                .toString(),
        )
        when (response.status) {
            429 -> throw AiUnavailableException("提交有点快，歇一分钟再补充。")
            413 -> throw AiUnavailableException("这段补充有点长，分成两次发吧。")
        }
        if (response.status !in 200..299) {
            throw AiUnavailableException("这次没记下来，稍后再试。")
        }
        val payload = JSONObject(response.body)
        AiContributionReply(
            answer = payload.optString("answer").ifBlank { "收到了。" },
            autoApplied = payload.optBoolean("autoApplied", false),
            status = payload.optString("status"),
        )
    }

    private fun request(method: String, url: String, body: String? = null): HttpResult {
        val connection = (URL(url).openConnection() as HttpURLConnection).apply {
            requestMethod = method
            connectTimeout = 8_000
            readTimeout = 42_000
            useCaches = false
            instanceFollowRedirects = false
            setRequestProperty("Accept", "application/json")
            setRequestProperty("Cache-Control", "no-store")
            setRequestProperty("User-Agent", "JiaoyiAI-Android/1.0.2")
            if (body != null) {
                doOutput = true
                setRequestProperty("Content-Type", "application/json; charset=utf-8")
            }
        }
        return try {
            if (body != null) {
                connection.outputStream.bufferedWriter(Charsets.UTF_8).use { it.write(body) }
            }
            val status = connection.responseCode
            val stream = if (status in 200..299) connection.inputStream else connection.errorStream
            HttpResult(status, stream?.bufferedReader(Charsets.UTF_8)?.use { it.readText() }.orEmpty())
        } finally {
            connection.disconnect()
        }
    }

    private data class HttpResult(val status: Int, val body: String)

    private companion object {
        const val API_ROOT = "https://www.jiaoyi123.cn"
    }
}

class AiUnavailableException(message: String) : Exception(message)
