package top.r3944realms.ltdmanager.module.gitea

import com.sun.net.httpserver.HttpExchange
import com.sun.net.httpserver.HttpServer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import top.r3944realms.ltdmanager.module.BaseModule
import top.r3944realms.ltdmanager.module.Modules
import top.r3944realms.ltdmanager.napcat.data.ID
import top.r3944realms.ltdmanager.napcat.data.MessageElement
import top.r3944realms.ltdmanager.napcat.request.other.SendGroupMsgRequest
import top.r3944realms.ltdmanager.utils.LoggerUtil
import java.net.InetSocketAddress
import java.util.concurrent.Executors
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

class GiteaWebhookModule(
    moduleName: String,
    private val port: Int,
    private val webhookPath: String,
    private val secret: String,
    private val targetGroupId: Long,
    private val enabledEvents: Set<GiteaEventType>,
) : BaseModule(Modules.GITEA_WEBHOOK, moduleName) {

    private var server: HttpServer? = null
    private var scope: CoroutineScope? = null

    private val json = Json {
        ignoreUnknownKeys = true
        coerceInputValues = true
    }

    override fun onLoad() {
        scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
        scope?.launch {
            try {
                startServer()
            } catch (e: Exception) {
                LoggerUtil.logger.error("[$name] 启动Webhook服务器失败", e)
            }
        }
        LoggerUtil.logger.info("[$name] Gitea Webhook模块已加载 (端口: $port, 路径: $webhookPath)")
    }

    override suspend fun onUnload() {
        server?.stop(0)
        server = null
        scope?.cancel()
        LoggerUtil.logger.info("[$name] Gitea Webhook模块已卸载")
    }

    private fun startServer() {
        val threadPool = Executors.newFixedThreadPool(2)
        server = HttpServer.create(InetSocketAddress(port), 0).apply {
            createContext(webhookPath) { exchange ->
                scope?.launch { handleRequest(exchange) }
            }
            executor = threadPool
            start()
        }
        LoggerUtil.logger.info("[$name] Webhook服务器已启动在端口 $port$webhookPath")
    }

    private suspend fun handleRequest(exchange: HttpExchange) {
        try {
            if (exchange.requestMethod != "POST") {
                exchange.sendResponseHeaders(405, -1)
                exchange.close()
                return
            }

            val body = exchange.requestBody.bufferedReader().readText()
            val signatureHeader = exchange.requestHeaders.getFirst("X-Gitea-Signature") ?: ""
            val eventTypeHeader = exchange.requestHeaders.getFirst("X-Gitea-Event") ?: ""

            // 验证签名
            if (!verifySignature(body, signatureHeader)) {
                LoggerUtil.logger.warn("[$name] 签名验证失败")
                exchange.sendResponseHeaders(403, -1)
                exchange.close()
                return
            }

            val eventType = GiteaEventType.fromHeader(eventTypeHeader)
            if (eventType == null) {
                LoggerUtil.logger.warn("[$name] 未知事件类型: $eventTypeHeader")
                exchange.sendResponseHeaders(200, -1)
                exchange.close()
                return
            }

            if (eventType !in enabledEvents) {
                LoggerUtil.logger.debug("[$name] 跳过未启用的事件: $eventType")
                exchange.sendResponseHeaders(200, -1)
                exchange.close()
                return
            }

            val message = formatEvent(eventType, body)
            if (message != null) {
                napCatClient.sendUnit(
                    SendGroupMsgRequest(
                        listOf(MessageElement.text(message)),
                        ID.long(targetGroupId)
                    )
                )
            }

            exchange.sendResponseHeaders(200, -1)
            exchange.close()
        } catch (e: Exception) {
            LoggerUtil.logger.error("[$name] 处理Webhook请求失败", e)
            try {
                exchange.sendResponseHeaders(500, -1)
                exchange.close()
            } catch (_: Exception) {}
        }
    }

    private fun verifySignature(payload: String, signatureHeader: String): Boolean {
        if (secret.isEmpty()) return true
        val expectedPrefix = "sha256="
        if (!signatureHeader.startsWith(expectedPrefix)) return false
        val receivedHex = signatureHeader.removePrefix(expectedPrefix)
        val mac = Mac.getInstance("HmacSHA256")
        mac.init(SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256"))
        val computedHex = mac.doFinal(payload.toByteArray(Charsets.UTF_8))
            .joinToString("") { "%02x".format(it) }
        return computedHex.equals(receivedHex, ignoreCase = true)
    }

    private fun formatEvent(eventType: GiteaEventType, body: String): String? {
        return try {
            when (eventType) {
                GiteaEventType.PUSH -> formatPush(json.decodeFromString<PushPayload>(body))
                GiteaEventType.ISSUES -> formatIssues(json.decodeFromString<IssuesPayload>(body))
                GiteaEventType.PULL_REQUEST -> formatPullRequest(json.decodeFromString<PullRequestPayload>(body))
                GiteaEventType.CREATE -> formatCreate(json.decodeFromString<CreatePayload>(body))
                GiteaEventType.DELETE -> formatDelete(json.decodeFromString<DeletePayload>(body))
                GiteaEventType.RELEASE -> formatRelease(json.decodeFromString<ReleasePayload>(body))
                GiteaEventType.REPOSITORY -> formatRepository(json.decodeFromString<RepositoryPayload>(body))
                GiteaEventType.FORK -> formatFork(json.decodeFromString<ForkPayload>(body))
            }
        } catch (e: Exception) {
            LoggerUtil.logger.error("[$name] 解析Webhook事件失败 (type=$eventType)", e)
            null
        }
    }

    private fun formatPush(p: PushPayload): String {
        val repo = p.repository.fullName
        val branch = p.ref.removePrefix("refs/heads/").removePrefix("refs/tags/")
        val pusherUser = p.pusher
        val pusher = pusherUser?.login?.ifEmpty { pusherUser.fullName } ?: p.sender.login.ifEmpty { p.sender.fullName }
        val count = p.totalCommits

        val sb = StringBuilder()
        sb.appendLine("🔨 [$repo] Push 事件")
        sb.appendLine("👤 $pusher 推送了 $count 个提交到 $branch")
        if (p.commits.isNotEmpty()) {
            sb.appendLine("————————————")
            for (commit in p.commits.take(5)) {
                val shortSha = commit.id.take(7)
                val msg = commit.message.lines().first().take(50)
                sb.appendLine("• $shortSha $msg")
            }
            if (p.commits.size > 5) {
                sb.appendLine("• ... 还有 ${p.commits.size - 5} 个提交")
            }
        }
        if (p.compareUrl.isNotEmpty()) {
            sb.appendLine("————————————")
            sb.append("查看: ${p.compareUrl}")
        }
        return sb.toString()
    }

    private fun formatIssues(p: IssuesPayload): String {
        val repo = p.repository.fullName
        val i = p.issue
        val user = p.sender.login.ifEmpty { p.sender.fullName }
        val action = translateAction(p.action)

        val sb = StringBuilder()
        sb.appendLine("📋 [$repo] Issue $action")
        sb.appendLine("👤 $user $action Issue #${i.number}: ${i.title}")
        if (i.body.isNotEmpty()) {
            sb.appendLine("————————————")
            sb.append(i.body.take(100).replace("\n", " "))
        }
        sb.appendLine()
        sb.append("查看: ${i.htmlUrl}")
        return sb.toString()
    }

    private fun formatPullRequest(p: PullRequestPayload): String {
        val repo = p.repository.fullName
        val pr = p.pullRequest
        val user = p.sender.login.ifEmpty { p.sender.fullName }
        val action = translateAction(p.action)
        val head = pr.headBranch.label.ifEmpty { pr.headBranch.ref }
        val base = pr.baseBranch.label.ifEmpty { pr.baseBranch.ref }

        val sb = StringBuilder()
        sb.appendLine("🔀 [$repo] PR $action")
        sb.appendLine("👤 $user $action PR #${pr.number}: ${pr.title}")
        sb.appendLine("$head → $base")
        if (pr.body.isNotEmpty()) {
            sb.appendLine("————————————")
            sb.appendLine(pr.body.take(150).replace("\n", " "))
        }
        sb.appendLine("————————————")
        val stateStr = when {
            pr.merged -> "已合并"
            pr.state == "closed" -> "已关闭"
            else -> "进行中"
        }
        sb.append("状态: $stateStr | 查看: ${pr.htmlUrl}")
        return sb.toString()
    }

    private fun formatCreate(p: CreatePayload): String {
        val repo = p.repository.fullName
        val user = p.sender.login.ifEmpty { p.sender.fullName }
        val typeName = when (p.refType) {
            "branch" -> "分支"
            "tag" -> "标签"
            else -> p.refType
        }
        return "➕ [$repo] 创建了$typeName: $p.ref\n👤 $user"
    }

    private fun formatDelete(p: DeletePayload): String {
        val repo = p.repository.fullName
        val user = p.sender.login.ifEmpty { p.sender.fullName }
        val typeName = when (p.refType) {
            "branch" -> "分支"
            "tag" -> "标签"
            else -> p.refType
        }
        return "❌ [$repo] 删除了$typeName: $p.ref\n👤 $user"
    }

    private fun formatRelease(p: ReleasePayload): String {
        val repo = p.repository.fullName
        val r = p.release
        val author = r.author.login.ifEmpty { r.author.fullName }
        val action = translateAction(p.action)

        val sb = StringBuilder()
        sb.appendLine("📦 [$repo] Release $action")
        sb.appendLine("🏷 ${r.tagName} - ${r.name.ifEmpty { r.tagName }}")
        sb.appendLine("👤 $author")
        if (r.body.isNotEmpty()) {
            sb.appendLine("————————————")
            sb.appendLine(r.body.take(200).replace("\n", " "))
        }
        sb.appendLine("————————————")
        val flags = listOfNotNull(
            if (r.draft) "草稿" else null,
            if (r.prerelease) "预发布" else null
        )
        if (flags.isNotEmpty()) sb.appendLine("标记: ${flags.joinToString(", ")}")
        sb.append("查看: ${r.htmlUrl}")
        return sb.toString()
    }

    private fun formatRepository(p: RepositoryPayload): String {
        val repo = p.repository.fullName
        val user = p.sender.login.ifEmpty { p.sender.fullName }
        val action = translateAction(p.action)
        return "📁 [$repo] Repository $action\n👤 $user"
    }

    private fun formatFork(p: ForkPayload): String {
        val source = p.repository.fullName
        val fork = p.forkedRepo.fullName
        val user = p.sender.login.ifEmpty { p.sender.fullName }
        return "🍴 [$source] Fork\n👤 $user Fork 了仓库 → $fork\n查看: ${p.forkedRepo.htmlUrl}"
    }

    private fun translateAction(action: String): String = when (action.lowercase()) {
        "opened" -> "已创建"
        "closed" -> "已关闭"
        "reopened" -> "已重新打开"
        "edited" -> "已编辑"
        "published" -> "已发布"
        "updated" -> "已更新"
        "created" -> "已创建"
        "deleted" -> "已删除"
        "labeled" -> "已标记"
        "unlabeled" -> "已取消标记"
        "assigned" -> "已指派"
        "unassigned" -> "已取消指派"
        "milestoned" -> "已设里程碑"
        "synchronized" -> "已同步"
        "review_requested" -> "请求审查"
        "transferred" -> "已转移"
        "merged" -> "已合并"
        else -> action
    }

    override fun info(): String = "Gitea Webhook模块 - 监听端口$port, 接收${enabledEvents.size}种事件类型"
    override fun help(): String = """
        Gitea Webhook模块 - 接收Gitea Webhook事件并推送到QQ群
        监听地址: http://<host>:$port$webhookPath
        启用事件: ${enabledEvents.joinToString(", ") { it.headerValue }}
    """.trimIndent()
}
