package top.r3944realms.ltdmanager.module

import kotlinx.coroutines.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import top.r3944realms.ltdmanager.core.mail.mail
import top.r3944realms.ltdmanager.module.common.filter.TriggerMessageFilter
import top.r3944realms.ltdmanager.module.common.filter.type.IgnoreSelfFilter
import top.r3944realms.ltdmanager.module.common.filter.type.KeywordFilter
import top.r3944realms.ltdmanager.module.common.filter.type.NewMessageFilter
import top.r3944realms.ltdmanager.napcat.data.ID
import top.r3944realms.ltdmanager.napcat.data.MessageElement
import top.r3944realms.ltdmanager.napcat.data.MessageType
import top.r3944realms.ltdmanager.napcat.data.msghistory.MsgHistorySpecificMsg
import top.r3944realms.ltdmanager.napcat.event.group.GetGroupMemberListEvent
import top.r3944realms.ltdmanager.napcat.request.group.GetGroupMemberListRequest
import top.r3944realms.ltdmanager.napcat.request.other.SendGroupMsgRequest
import top.r3944realms.ltdmanager.utils.LoggerUtil
import top.r3944realms.ltdmanager.whitelist.WhitelistSystemClient
import java.io.File

class WhitelistAuditModule(
    moduleName: String,
    private val mainGroupPollingModule: GroupMessagePollingModule,
    private val whitelistGroupPollingModule: GroupMessagePollingModule,
    private val whitelistClient: WhitelistSystemClient,
    private val mailModule: MailModule?,
    private val filterQqList: Set<Long>,
    private val auditAllowedUsers: Set<Long>,
    private val reActivationKeywords: Set<String>,
    private val auditCommandPrefix: String,
    private val gracePeriodDays: Int,
    private val expiryWarningDays: Int,
    private val pollIntervalMinutes: Long,
    private val selfId: Long,
) : BaseModule(Modules.WHITELIST_AUDIT, moduleName), PersistentState<WhitelistAuditModule.AuditState> {

    private val whitelistGroupId: Long get() = whitelistGroupPollingModule.targetGroupId

    private var scope: CoroutineScope? = null

    private val stateFile: File = getStateFileInternal("whitelist_audit_state.json", name)
    private val stateBackupFile: File = getStateFileInternal("whitelist_audit_state.json.bak", name)

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

    override fun getStateFileInternal(): File = stateFile

    private var auditState: AuditState = loadState()
    override fun getState(): AuditState = auditState

    private var lastMsgRealId: Long = -1
    private var lastMsgTime: Long = 0

    private var lastAuditRealId: Long = -1
    private var lastAuditTime: Long = 0

    private val reactivationFilter by lazy {
        TriggerMessageFilter(
            listOf(
                IgnoreSelfFilter(selfId),
                NewMessageFilter { lastMsgTime to lastMsgRealId },
                KeywordFilter(reActivationKeywords),
            )
        )
    }

    private val auditCommandFilter by lazy {
        TriggerMessageFilter(
            listOf(
                IgnoreSelfFilter(selfId),
                NewMessageFilter { lastAuditTime to lastAuditRealId },
                KeywordFilter(setOf(auditCommandPrefix)),
            )
        )
    }

    override fun onLoad() {
        LoggerUtil.logger.info("[$name] 白名单审计模块已装载")
        LoggerUtil.logger.info("[$name] 白名单群: $whitelistGroupId, 主群: ${mainGroupPollingModule.targetGroupId}")
        LoggerUtil.logger.info("[$name] 审计命令: $auditCommandPrefix")
        LoggerUtil.logger.info("[$name] 检查间隔: ${pollIntervalMinutes}分, 宽限期: ${gracePeriodDays}天, 过期警告: ${expiryWarningDays}天前")
        LoggerUtil.logger.info("[$name] 邮件通知: ${mailModule != null}")

        scope = CoroutineScope(Dispatchers.IO + SupervisorJob())

        scope!!.launch {
            delay(10_000)
            while (isActive && loaded) {
                try {
                    runAuditCycle()
                } catch (e: Exception) {
                    LoggerUtil.logger.error("[$name] 审计周期异常", e)
                }
                delay(pollIntervalMinutes * 60_000)
            }
        }

        scope!!.launch {
            mainGroupPollingModule.messagesFlow.collect { messages ->
                if (loaded) {
                    val filtered = reactivationFilter.filter(messages)
                    if (filtered.isNotEmpty()) handleReActivationMessages(filtered)
                }
            }
        }

        scope!!.launch {
            whitelistGroupPollingModule.messagesFlow.collect { messages ->
                if (loaded) {
                    val filtered = auditCommandFilter.filter(messages)
                    if (filtered.isNotEmpty()) handleAuditCommand(filtered)
                }
            }
        }
    }

    override suspend fun onUnload() {
        saveState(auditState)
        scope?.cancel()
        LoggerUtil.logger.info("[$name] 白名单审计模块已卸载")
    }

    // ======== 审计周期 ========

    private suspend fun runAuditCycle() {
        LoggerUtil.logger.info("[$name] 开始审计周期...")

        val groupMembers = fetchWhitelistGroupMembers() ?: return
        val groupMemberQqSet = groupMembers.map { it.userId }.toSet()
        LoggerUtil.logger.info("[$name] 白名单群成员数: ${groupMemberQqSet.size}")

        val whitelistEntries = whitelistClient.listApproved()
        if (whitelistEntries.isEmpty()) {
            LoggerUtil.logger.info("[$name] 白名单通过数为0，跳过")
            return
        }
        LoggerUtil.logger.info("[$name] 白名单通过数: ${whitelistEntries.size}")

        val now = System.currentTimeMillis()
        val graceMs = gracePeriodDays * 24 * 60 * 60 * 1000L
        val warningMs = expiryWarningDays * 24 * 60 * 60 * 1000L

        for (entry in whitelistEntries) {
            val qqLong = entry.qq.toLongOrNull() ?: continue
            if (qqLong in filterQqList) continue
            if (qqLong in groupMemberQqSet) {
                val key = entry.qq
                if (auditState.entries.containsKey(key) && !auditState.entries[key]!!.reactivated) {
                    auditState = auditState.copy(
                        entries = auditState.entries.toMutableMap().apply { remove(key) }
                    )
                    saveState(auditState)
                    LoggerUtil.logger.info("[$name] $qqLong 重新加入白名单群，清除审计标记")
                }
                continue
            }

            val key = entry.qq
            val existing = auditState.entries[key]

            when {
                existing == null -> {
                    LoggerUtil.logger.info("[$name] $qqLong (${entry.playerName}) 不在白名单群，执行拒绝")
                    val rejected = whitelistClient.reject(entry.id)
                    val newEntry = AuditEntry(
                        qq = entry.qq,
                        playerId = entry.id,
                        playerName = entry.playerName,
                        detectedTime = now,
                        rejectedTime = if (rejected) now else 0
                    )
                    auditState = auditState.copy(
                        entries = auditState.entries.toMutableMap().apply { put(key, newEntry) }
                    )
                    saveState(auditState)
                    if (rejected) {
                        sendGroupNotification(
                            "白名单审计: ${entry.playerName}(QQ:$qqLong) 因退出白名单群已被拒绝\n" +
                            "回复关键词即可在 ${gracePeriodDays}天内重新激活"
                        )
                        sendEmail(
                            qqLong, entry.playerName,
                            "LTD白名单审计通知",
                            "你的白名单因退出白名单QQ群已被暂时拒绝。\n请在 ${gracePeriodDays} 天之内在主群发送关键词重新激活。"
                        )
                    }
                }

                existing.reactivated -> {
                    LoggerUtil.logger.info("[$name] $qqLong (${entry.playerName}) 再次不在白名单群，重新拒绝")
                    whitelistClient.reject(entry.id)
                    sendEmail(
                        qqLong, entry.playerName,
                        "LTD白名单审计通知",
                        "你的白名单再次因退出白名单QQ群被拒绝。请在 ${gracePeriodDays} 天之内在主群发送关键词重新激活。"
                    )
                    val updated = existing.copy(
                        detectedTime = now,
                        rejectedTime = now,
                        warningSentTime = 0,
                        reactivated = false
                    )
                    auditState = auditState.copy(
                        entries = auditState.entries.toMutableMap().apply { put(key, updated) }
                    )
                    saveState(auditState)
                }

                existing.rejectedTime > 0 -> {
                    val elapsed = now - existing.rejectedTime
                    if (elapsed >= graceMs) {
                        LoggerUtil.logger.info("[$name] $qqLong (${entry.playerName}) 宽限期已过，执行删除")
                        whitelistClient.remove(entry.id)
                        auditState = auditState.copy(
                            entries = auditState.entries.toMutableMap().apply { remove(key) }
                        )
                        saveState(auditState)
                        sendGroupNotification(
                            "白名单审计: ${entry.playerName}(QQ:$qqLong) 宽限期已过，白名单已删除"
                        )
                        sendEmail(
                            qqLong, entry.playerName,
                            "LTD白名单已删除",
                            "你的白名单因宽限期已过已被永久删除。请重新申请白名单。"
                        )
                    } else if (elapsed >= graceMs - warningMs && existing.warningSentTime == 0L) {
                        val remainDays = (graceMs - elapsed) / (24 * 60 * 60 * 1000)
                        LoggerUtil.logger.info("[$name] $qqLong (${entry.playerName}) 宽限期即将过期 (剩余${remainDays}天)，发送警告")
                        sendGroupNotification(
                            "⚠️ 白名单审计: ${entry.playerName}(QQ:$qqLong) 宽限期仅剩${remainDays}天\n" +
                            "请在主群发送关键词重新激活，否则将被永久删除"
                        )
                        sendEmail(
                            qqLong, entry.playerName,
                            "LTD白名单即将过期",
                            "你的白名单宽限期仅剩 ${remainDays} 天。请尽快在主群发送关键词重新激活，否则将被永久删除。"
                        )
                        val updated = existing.copy(warningSentTime = now)
                        auditState = auditState.copy(
                            entries = auditState.entries.toMutableMap().apply { put(key, updated) }
                        )
                        saveState(auditState)
                    }
                }
            }
        }
        // 额外检查：已拒绝但不在 approved 列表中的用户（状态已变，API 不再返回）
        val approvedQqSet = whitelistEntries.map { it.qq }.toSet()
        for ((qq, entry) in auditState.entries.toMap()) {
            if (qq in approvedQqSet) continue
            if (entry.rejectedTime == 0L) continue
            val elapsed = now - entry.rejectedTime
            if (elapsed >= graceMs) {
                LoggerUtil.logger.info("[$name] $qq (${entry.playerName}) 宽限期已过（不在approved列表），执行删除")
                whitelistClient.remove(entry.playerId)
                auditState = auditState.copy(
                    entries = auditState.entries.toMutableMap().apply { remove(qq) }
                )
                saveState(auditState)
                sendGroupNotification(
                    "白名单审计: ${entry.playerName}(QQ:$qq) 宽限期已过，白名单已删除"
                )
                sendEmail(
                    qq.toLong(), entry.playerName,
                    "LTD白名单已删除",
                    "你的白名单因宽限期已过已被永久删除。请重新申请白名单。"
                )
            } else if (elapsed >= graceMs - warningMs && entry.warningSentTime == 0L) {
                val remainDays = ((graceMs - elapsed) / (24 * 60 * 60 * 1000)).toInt()
                LoggerUtil.logger.info("[$name] $qq (${entry.playerName}) 宽限期即将过期 (剩余${remainDays}天)")
                sendEmail(
                    qq.toLong(), entry.playerName,
                    "LTD白名单即将过期",
                    "你的白名单宽限期仅剩 ${remainDays} 天。请尽快在主群发送关键词重新激活，否则将被永久删除。"
                )
                val updated = entry.copy(warningSentTime = now)
                auditState = auditState.copy(
                    entries = auditState.entries.toMutableMap().apply { put(qq, updated) }
                )
                saveState(auditState)
            }
        }
        LoggerUtil.logger.info("[$name] 审计周期完成")
    }

    // ======== 重新激活关键词处理 ========

    private suspend fun handleReActivationMessages(messages: List<MsgHistorySpecificMsg>) {
        val msg = messages.maxByOrNull { it.time } ?: return

        val key = msg.userId.toString()
        val entry = auditState.entries[key] ?: return

        updateMsgState(msg)
        val now = System.currentTimeMillis()
        val graceMs = gracePeriodDays * 24 * 60 * 60 * 1000L

        if (entry.rejectedTime > 0 && now - entry.rejectedTime < graceMs) {
            LoggerUtil.logger.info("[$name] ${msg.userId} (${entry.playerName}) 发送关键词，重新激活白名单")
            val approved = whitelistClient.approve(entry.playerId)
            if (approved) {
                val updated = entry.copy(reactivated = true)
                auditState = auditState.copy(
                    entries = auditState.entries.toMutableMap().apply { put(key, updated) }
                )
                saveState(auditState)
                napCatClient.sendUnit(
                    SendGroupMsgRequest(
                        MessageElement.reply(
                            ID.long(msg.realId),
                            "✅ ${entry.playerName} 白名单已重新激活，请在${gracePeriodDays}天内重新加入白名单群"
                        ),
                        ID.long(mainGroupPollingModule.targetGroupId)
                    )
                )
                sendEmail(
                    msg.userId, entry.playerName,
                    "LTD白名单已重新激活",
                    "你的白名单已重新激活。请在 ${gracePeriodDays} 天之内重新加入白名单QQ群，否则将再次被拒绝。"
                )
            }
        } else {
            napCatClient.sendUnit(
                SendGroupMsgRequest(
                    MessageElement.reply(
                        ID.long(msg.realId),
                        "⏰ ${entry.playerName} 宽限期已过，无法重新激活，请重新申请白名单"
                    ),
                    ID.long(mainGroupPollingModule.targetGroupId)
                )
            )
        }
    }

    // ======== 手动审计命令 ========

    private suspend fun handleAuditCommand(messages: List<MsgHistorySpecificMsg>) {
        val msg = messages.maxByOrNull { it.time } ?: return

        if (msg.userId !in auditAllowedUsers) {
            napCatClient.sendUnit(
                SendGroupMsgRequest(
                    MessageElement.reply(ID.long(msg.realId), "你没有权限执行审计命令"),
                    ID.long(whitelistGroupId)
                )
            )
            return
        }

        lastAuditRealId = msg.realId
        lastAuditTime = msg.time

        LoggerUtil.logger.info("[$name] ${msg.userId} 触发手动审计")
        val summary = runAdhocAudit()

        napCatClient.sendUnit(
            SendGroupMsgRequest(
                MessageElement.reply(ID.long(msg.realId), summary),
                ID.long(whitelistGroupId)
            )
        )
    }

    private suspend fun runAdhocAudit(): String {
        val groupMembers = fetchWhitelistGroupMembers() ?: return "获取群成员失败，审计中断"

        val groupMemberQqSet = groupMembers.map { it.userId }.toSet()
        val whitelistEntries = whitelistClient.listApproved()
        if (whitelistEntries.isEmpty()) return "白名单通过数为0，跳过审计"

        val now = System.currentTimeMillis()
        val graceMs = gracePeriodDays * 24 * 60 * 60 * 1000L

        var inGroupOk = 0
        var notInGroupNew = 0
        val rejectedList = mutableListOf<String>()   // 本次被拒绝的用户
        var notInGroupGrace = mutableListOf<String>()
        var notInGroupExpired = 0
        val expiredList = mutableListOf<String>()     // 本次被删除的用户
        var inFilter = 0

        for (entry in whitelistEntries) {
            val qqLong = entry.qq.toLongOrNull() ?: continue
            if (qqLong in filterQqList) { inFilter++; continue }
            if (qqLong in groupMemberQqSet) {
                inGroupOk++
                // 清除已有标记
                val key = entry.qq
                if (auditState.entries.containsKey(key)) {
                    auditState = auditState.copy(
                        entries = auditState.entries.toMutableMap().apply { remove(key) }
                    )
                }
                continue
            }

            val key = entry.qq
            val existing = auditState.entries[key]

            when {
                existing == null -> {
                    notInGroupNew++
                    rejectedList.add("${entry.playerName}($qqLong)")
                    whitelistClient.reject(entry.id)
                    val newEntry = AuditEntry(
                        qq = entry.qq,
                        playerId = entry.id,
                        playerName = entry.playerName,
                        detectedTime = now,
                        rejectedTime = now
                    )
                    auditState = auditState.copy(
                        entries = auditState.entries.toMutableMap().apply { put(key, newEntry) }
                    )
                }
                existing.rejectedTime > 0 && now - existing.rejectedTime >= graceMs -> {
                    notInGroupExpired++
                    expiredList.add("${entry.playerName}($qqLong)")
                    whitelistClient.remove(entry.id)
                    auditState = auditState.copy(
                        entries = auditState.entries.toMutableMap().apply { remove(key) }
                    )
                }
                existing.rejectedTime > 0 -> {
                    val remainDays = ((graceMs - (now - existing.rejectedTime)) / (24 * 60 * 60 * 1000)).toInt()
                    notInGroupGrace.add("${entry.playerName}(剩${remainDays}天)")
                }
            }
        }
        // 额外检查：已拒绝但不在 approved 列表中的用户（API 不再返回）
        val approvedQqSet = whitelistEntries.map { it.qq }.toSet()
        for ((qq, entry) in auditState.entries.toMap()) {
            if (qq in approvedQqSet) continue
            if (entry.rejectedTime == 0L) continue
            val elapsed = now - entry.rejectedTime
            if (elapsed >= graceMs) {
                notInGroupExpired++
                expiredList.add("${entry.playerName}($qq)")
                whitelistClient.remove(entry.playerId)
                auditState = auditState.copy(
                    entries = auditState.entries.toMutableMap().apply { remove(qq) }
                )
            } else {
                val remainDays = ((graceMs - elapsed) / (24 * 60 * 60 * 1000)).toInt()
                notInGroupGrace.add("${entry.playerName}($qq, 剩${remainDays}天)")
            }
        }
        saveState(auditState)

        return buildString {
            appendLine("审计完成")
            appendLine("─".repeat(16))
            appendLine("白名单总数: ${whitelistEntries.size}")
            appendLine("在群正常: $inGroupOk 人")
            if (notInGroupNew > 0) {
                appendLine("新发现不在群(已拒绝): $notInGroupNew 人")
                rejectedList.forEach { appendLine("  • $it") }
            }
            if (notInGroupGrace.isNotEmpty()) {
                appendLine("宽限期中: \n${notInGroupGrace.joinToString()}")
            }
            if (notInGroupExpired > 0) {
                appendLine("已过期删除: $notInGroupExpired 人")
                expiredList.forEach { appendLine("  • $it") }
            }
            if (inFilter > 0) {
                appendLine("过滤列表跳过: $inFilter 人")
            }
//            val total = inGroupOk + notInGroupNew + notInGroupGrace.size + notInGroupExpired + inFilter
//            if (total != whitelistEntries.size + auditState.entries.size) {
//                appendLine("⚠ 计数不闭合: 统计${total} vs 入库${whitelistEntries.size}+状态${auditState.entries.size}")
//            }
        }
    }

    // ======== 群成员获取 ========

    private suspend fun fetchWhitelistGroupMembers(): List<GroupMemberData>? {
        return try {
            val event = napCatClient.send<GetGroupMemberListEvent>(
                GetGroupMemberListRequest(ID.long(whitelistGroupId), false)
            )
            event.data.filter { !it.isRobot }.map {
                GroupMemberData(it.userId, it.nickname)
            }
        } catch (e: Exception) {
            LoggerUtil.logger.error("[$name] 获取群成员列表失败", e)
            null
        }
    }

    private data class GroupMemberData(val userId: Long, val nickname: String)

    // ======== 群通知 ========

    private suspend fun sendGroupNotification(text: String) {
        try {
            napCatClient.sendUnit(
                SendGroupMsgRequest(
                    listOf(MessageElement.text(text)),
                    ID.long(mainGroupPollingModule.targetGroupId)
                )
            )
        } catch (e: Exception) {
            LoggerUtil.logger.error("[$name] 发送群通知失败", e)
        }
    }

    private fun sendEmail(qq: Long, playerName: String, subject: String, body: String) {
        val m = mailModule ?: return
        try {
            m.enqueue(mail {
                to += "${qq}@qq.com"
                this.subject = "【$playerName】$subject"
                this.body = body
                isHtml = false
            })
            LoggerUtil.logger.info("[$name] 已入队邮件: $subject → ${qq}@qq.com")
        } catch (e: Exception) {
            LoggerUtil.logger.error("[$name] 邮件入队失败", e)
        }
    }

    private fun updateMsgState(msg: MsgHistorySpecificMsg) {
        lastMsgRealId = msg.realId
        lastMsgTime = msg.time
    }

    // ======== 持久化 ========

    @Serializable
    data class AuditState(
        val entries: Map<String, AuditEntry> = emptyMap()
    )

    @Serializable
    data class AuditEntry(
        val qq: String,
        val playerId: Int,
        val playerName: String,
        val detectedTime: Long,
        val rejectedTime: Long = 0,
        val warningSentTime: Long = 0,
        val reactivated: Boolean = false,
    )

    override fun loadState(): AuditState {
        return try {
            val file = when {
                stateFile.exists() -> stateFile
                stateBackupFile.exists() -> stateBackupFile
                else -> return AuditState()
            }
            json.decodeFromString(file.readText())
        } catch (e: Exception) {
            LoggerUtil.logger.warn("[$name] 读取状态失败，使用默认值", e)
            AuditState()
        }
    }

    override fun saveState(state: AuditState) {
        try {
            if (stateFile.exists()) stateFile.copyTo(stateBackupFile, overwrite = true)
            stateFile.writeText(json.encodeToString(state))
        } catch (e: Exception) {
            LoggerUtil.logger.error("[$name] 保存状态失败", e)
        }
    }

    override fun info(): String = "白名单审计模块 - 群:$whitelistGroupId, 间隔:${pollIntervalMinutes}分, 宽限:${gracePeriodDays}天, 邮件:${mailModule != null}"
    override fun help(): String = buildString {
        appendLine("白名单审计模块 - 定期检查白名单群成员并自动处理离群用户")
        appendLine("检查间隔: ${pollIntervalMinutes}分钟")
        appendLine("宽限期: ${gracePeriodDays}天")
        appendLine("过期警告: 到期前${expiryWarningDays}天")
        appendLine("邮件通知: ${mailModule != null}")
        appendLine("重新激活关键词: ${reActivationKeywords.joinToString()}")
    }
}
