package com.aiosman.ravenow.data

import android.content.Context
import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.R
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.api.DictItem
import com.aiosman.ravenow.data.api.PointsBalance
import com.aiosman.ravenow.data.api.PointsChangeLogsResponse
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext

/**
 * 积分服务
 *
 * 提供积分余额查询和积分变更日志查询功能
 */
object PointService {

    // ========== 定价表（字典 points_rules）相关 ==========
    private val dictService: DictService = DictServiceImpl()
    private val gson = Gson()

    /**
     * 积分规则key常量
     * 对应积分规则JSON中的key值
     */
    object PointsRuleKey {
        // 获得积分类型（add）
        /** 每日登录奖励 */
        const val DAILY_LOGIN = "daily_login"
        /** 用户注册奖励 */
        const val USER_REGISTER = "user_register"

        // 消费积分类型（sub）
        /** 添加Agent记忆 */
        const val ADD_AGENT_MEMORY = "add_agent_memory"
        /** 增加房间容量 */
        const val ADD_ROOM_CAP = "add_room_cap"
        /** 创建房间 */
        const val CREATE_ROOM = "create_room"
        /** 创建定时事件 */
        const val CREATE_SCHEDULE_EVENT = "create_schedule_event"
        /** 房间私密模式 */
        const val ROOM_PRIVATE = "room_private"
        /** Agent私密模式 */
        const val SPEND_AGENT_PRIVATE = "spend_agent_private"
        /** 自定义聊天背景 */
        const val SPEND_CHAT_BACKGROUND = "spend_chat_background"
        /** 房间记忆添加 */
        const val SPEND_ROOM_MEMORY = "spend_room_memory"
    }

    sealed class RuleAmount {
        data class Fixed(val value: Int) : RuleAmount()
        data class Range(val min: Int, val max: Int) : RuleAmount()
    }
    data class PointsRules(
        val add: Map<String, RuleAmount>,
        val sub: Map<String, RuleAmount>
    )

    private val _pointsRules = MutableStateFlow<PointsRules?>(null)
    val pointsRules: StateFlow<PointsRules?> = _pointsRules.asStateFlow()

    suspend fun refreshPointsRules(key: String = "points_rules") {
        withContext(Dispatchers.IO) {
            try {
                val dict = dictService.getDictByKey(key)
                val rules = parsePointsRules(dict)
                _pointsRules.value = rules
            } catch (_: Exception) {
                _pointsRules.value = null
            }
        }
    }

    // ========== 群聊人数限制（字典 points-rule）相关 ==========
    /**
     * 群聊人数限制配置
     * @param defaultMaxTotal 初始最大人数（默认值）
     * @param maxTotal 最大人数（上限）
     */
    data class RoomMaxMembers(
        val defaultMaxTotal: Int,
        val maxTotal: Int
    )

    private val _roomMaxMembers = MutableStateFlow<RoomMaxMembers?>(null)
    val roomMaxMembers: StateFlow<RoomMaxMembers?> = _roomMaxMembers.asStateFlow()

    /**
     * 刷新群聊人数限制配置（从外部字典表加载 points-rule）
     * 加载时机与 refreshPointsRules 一致
     */
    suspend fun refreshRoomMaxMembers(key: String = "points-rule") {
        withContext(Dispatchers.IO) {
            try {
                val dict = dictService.getOutsideDictByKey(key)
                val config = parseRoomMaxMembers(dict)
                _roomMaxMembers.value = config
            } catch (_: Exception) {
                _roomMaxMembers.value = null
            }
        }
    }

    /**
     * 解析群聊人数限制配置
     * 解析格式：{"room":{"default":{"max-total":5},"max":{"max-total":200}}}
     */
    private fun parseRoomMaxMembers(dict: DictItem): RoomMaxMembers? {
        val raw = dict.value
        val jsonStr = when (raw) {
            is String -> raw
            else -> gson.toJson(raw)
        }
        return try {
            val root = JsonParser.parseString(jsonStr).asJsonObject
            val roomObj = root.getAsJsonObject("room")
            val defaultObj = roomObj?.getAsJsonObject("default")
            val maxObj = roomObj?.getAsJsonObject("max")
            
            val defaultMaxTotal = defaultObj?.get("max-total")?.takeIf { it.isJsonPrimitive }?.asInt ?: 5
            val maxTotal = maxObj?.get("max-total")?.takeIf { it.isJsonPrimitive }?.asInt ?: 200
            
            RoomMaxMembers(
                defaultMaxTotal = defaultMaxTotal,
                maxTotal = maxTotal
            )
        } catch (_: Exception) {
            null
        }
    }

    private fun parsePointsRules(dict: DictItem): PointsRules? {
        val raw = dict.value
        val jsonStr = when (raw) {
            is String -> raw
            else -> gson.toJson(raw)
        }
        return try {
            val root = JsonParser.parseString(jsonStr).asJsonObject
            fun parseBlock(block: JsonObject?): Map<String, RuleAmount> {
                if (block == null) return emptyMap()
                return block.entrySet().associate { entry ->
                    val key = entry.key
                    val v: JsonElement = entry.value
                    val amount: RuleAmount = when {
                        v.isJsonPrimitive && v.asJsonPrimitive.isNumber -> {
                            RuleAmount.Fixed(v.asInt)
                        }
                        v.isJsonPrimitive && v.asJsonPrimitive.isString -> {
                            val s = v.asString.trim()
                            if (s.contains("-")) {
                                val parts = s.split("-")
                                    .map { it.trim() }
                                    .filter { it.isNotEmpty() }
                                val min = parts.getOrNull(0)?.toIntOrNull()
                                val max = parts.getOrNull(1)?.toIntOrNull()
                                when {
                                    min != null && max != null -> RuleAmount.Range(min, max)
                                    min != null -> RuleAmount.Fixed(min)
                                    else -> RuleAmount.Fixed(0)
                                }
                            } else {
                                RuleAmount.Fixed(s.toIntOrNull() ?: 0)
                            }
                        }
                        v.isJsonObject -> {
                            val obj = v.asJsonObject
                            val min = obj.get("min")?.takeIf { it.isJsonPrimitive }?.asInt
                            val max = obj.get("max")?.takeIf { it.isJsonPrimitive }?.asInt
                            val value = obj.get("value")?.takeIf { it.isJsonPrimitive }?.asInt
                            when {
                                min != null && max != null -> RuleAmount.Range(min, max)
                                value != null -> RuleAmount.Fixed(value)
                                else -> RuleAmount.Fixed(0)
                            }
                        }
                        else -> RuleAmount.Fixed(0)
                    }
                    key to amount
                }
            }
            val addObj = root.getAsJsonObject("add")
            val subObj = root.getAsJsonObject("sub")
            PointsRules(
                add = parseBlock(addObj),
                sub = parseBlock(subObj)
            )
        } catch (_: Exception) {
            null
        }
    }

    // 全局可观察的积分余额（仅内存，不落盘）
    private val _pointsBalance = MutableStateFlow<PointsBalance?>(null)
    val pointsBalance: StateFlow<PointsBalance?> = _pointsBalance.asStateFlow()

    // 当前已加载的用户ID，用于处理用户切换
    @Volatile private var currentUserId: Int? = null

    /** 设置当前用户ID；当用户切换时，会清空旧的积分数据以避免串号 */
    fun setCurrentUser(userId: Int?) {
        if (currentUserId != userId) {
            currentUserId = userId
            _pointsBalance.value = null
        }
    }

    /** 清空内存中的积分状态（用于登出或用户切换） */
    fun clear() {
        _pointsBalance.value = null
        currentUserId = null
    }

    /**
     * 刷新当前用户的积分余额（进入应用并完成登录态初始化后调用）
     * - 若为游客或无 token，则清空并返回
     */
    suspend fun refreshMyPointsBalance(includeStatistics: Boolean = true) {
        withContext(Dispatchers.IO) {
            if (AppStore.isGuest || AppStore.token == null) {
                clear()
                return@withContext
            }
            val balance = getMyPointsBalance(includeStatistics)
            _pointsBalance.value = balance
        }
    }

    /**
     * 获取当前用户积分余额
     *
     * @param includeStatistics 是否包含统计信息（累计获得和累计消费），默认 true
     * @return 积分余额信息，包含当前余额和可选的统计数据
     * @throws Exception 网络请求失败或服务器返回错误
     *
     * 示例：
     * ```kotlin
     * try {
     *     // 获取包含统计信息的积分余额
     *     val balance = PointService.getMyPointsBalance()
     *     println("当前余额: ${balance.balance}")
     *     println("累计获得: ${balance.totalEarned}")
     *     println("累计消费: ${balance.totalSpent}")
     *
     *     // 仅获取当前余额
     *     val simpleBalance = PointService.getMyPointsBalance(includeStatistics = false)
     *     println("当前余额: ${simpleBalance.balance}")
     * } catch (e: Exception) {
     *     println("获取积分余额失败: ${e.message}")
     * }
     * ```
     */
    suspend fun getMyPointsBalance(includeStatistics: Boolean = true): PointsBalance {
        return withContext(Dispatchers.IO) {
            val response = ApiClient.api.getMyPointsBalance(includeStatistics)
            if (response.isSuccessful) {
                response.body()?.data ?: throw Exception("响应数据为空")
            } else {
                throw Exception("获取积分余额失败: ${response.code()} ${response.message()}")
            }
        }
    }

    /**
     * 获取当前用户积分变更日志列表
     *
     * @param page 页码，默认 1
     * @param pageSize 每页数量，默认 20
     * @param changeType 变更类型筛选（"add": 增加, "subtract": 减少, "adjust": 调整），null 表示不筛选
     * @param startTime 开始时间，格式：YYYY-MM-DD，null 表示不限制
     * @param endTime 结束时间，格式：YYYY-MM-DD，null 表示不限制
     * @return 积分变更日志列表响应，包含日志列表和分页信息
     * @throws Exception 网络请求失败或服务器返回错误
     *
     * 示例：
     * ```kotlin
     * try {
     *     // 获取最近的积分变更日志
     *     val logs = PointService.getMyPointsChangeLogs(page = 1, pageSize = 20)
     *     println("总记录数: ${logs.total}")
     *     logs.list.forEach { log ->
     *         println("${log.createdAt}: ${log.changeType} ${log.amount} (${log.reason})")
     *     }
     *
     *     // 筛选积分增加记录
     *     val earnLogs = PointService.getMyPointsChangeLogs(changeType = "add")
     *
     *     // 查询指定时间范围的记录
     *     val rangeLogs = PointService.getMyPointsChangeLogs(
     *         startTime = "2024-01-01",
     *         endTime = "2024-01-31"
     *     )
     * } catch (e: Exception) {
     *     println("获取积分变更日志失败: ${e.message}")
     * }
     * ```
     */
    suspend fun getMyPointsChangeLogs(
            page: Int = 1,
            pageSize: Int = 20,
            changeType: String? = null,
            startTime: String? = null,
            endTime: String? = null
    ): PointsChangeLogsResponse {
        return withContext(Dispatchers.IO) {
            val response =
                    ApiClient.api.getMyPointsChangeLogs(
                            page = page,
                            pageSize = pageSize,
                            changeType = changeType,
                            startTime = startTime,
                            endTime = endTime
                    )
            if (response.isSuccessful) {
                response.body() ?: throw Exception("响应数据为空")
            } else {
                throw Exception("获取积分变更日志失败: ${response.code()} ${response.message()}")
            }
        }
    }

    /** 积分变更类型常量 */
    object ChangeType {
        /** 积分增加 */
        const val ADD = "add"
        /** 积分减少 */
        const val SUBTRACT = "subtract"
        /** 积分调整 */
        const val ADJUST = "adjust"
    }

    /** 积分变更原因常量 */
    object ChangeReason {
        // 获得积分类型
        /** 新用户注册奖励 */
        const val EARN_REGISTER = "earn_register"
        /** 每日签到奖励 */
        const val EARN_DAILY = "earn_daily"
        /** 任务完成奖励 */
        const val EARN_TASK = "earn_task"
        /** 邀请好友奖励 */
        const val EARN_INVITE = "earn_invite"
        /** 充值获得 */
        const val EARN_RECHARGE = "earn_recharge"

        // 消费积分类型
        /** 创建群聊 */
        const val SPEND_GROUP_CREATE = "spend_group_create"
        /** 扩容群聊 */
        const val SPEND_GROUP_EXPAND = "spend_group_expand"
        /** Agent 私密模式 */
        const val SPEND_AGENT_PRIVATE = "spend_agent_private"
        /** Agent 记忆添加 */
        const val SPEND_AGENT_MEMORY = "spend_agent_memory"
        /** 房间记忆添加 */
        const val SPEND_ROOM_MEMORY = "spend_room_memory"
        /** 自定义聊天背景 */
        const val SPEND_CHAT_BACKGROUND = "spend_chat_background"
        /** 定时事件解锁 */
        const val SPEND_SCHEDULE_EVENT = "spend_schedule_event"
    }

    /**
     * 获取变更原因的描述（支持多语言）
     *
     * @param context Context 用于获取资源
     * @param reason 变更原因代码
     * @return 本地化描述
     */
    fun getReasonDescription(context: Context, reason: String): String {
        val resourceId = when (reason) {
            ChangeReason.EARN_REGISTER -> R.string.earn_register
            ChangeReason.EARN_DAILY -> R.string.earn_daily
            ChangeReason.EARN_TASK -> R.string.earn_task
            ChangeReason.EARN_INVITE -> R.string.earn_invite
            ChangeReason.EARN_RECHARGE -> R.string.earn_recharge
            ChangeReason.SPEND_GROUP_CREATE -> R.string.spend_group_create
            ChangeReason.SPEND_GROUP_EXPAND -> R.string.spend_group_expand
            ChangeReason.SPEND_AGENT_PRIVATE -> R.string.spend_agent_private
            ChangeReason.SPEND_AGENT_MEMORY -> R.string.spend_agent_memory
            ChangeReason.SPEND_ROOM_MEMORY -> R.string.spend_room_memory
            ChangeReason.SPEND_CHAT_BACKGROUND -> R.string.spend_chat_background
            ChangeReason.SPEND_SCHEDULE_EVENT -> R.string.spend_schedule_event
            else -> null
        }
        return resourceId?.let { context.getString(it) } ?: reason // 未知原因，返回原始代码
    }

    /**
     * 获取变更类型的中文描述
     *
     * @param changeType 变更类型代码
     * @return 中文描述
     */
    fun getChangeTypeDescription(changeType: String): String {
        return when (changeType) {
            ChangeType.ADD -> "增加"
            ChangeType.SUBTRACT -> "减少"
            ChangeType.ADJUST -> "调整"
            else -> changeType
        }
    }
}
