package com.tsmobile.app.viewmodel

import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.tsmobile.app.TSBridge
import com.tsmobile.app.data.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json

/**
 * 同步状态。
 */
sealed class SyncState {
    object Unsynced : SyncState()      // 已连接但尚无完整数据
    object Syncing : SyncState()       // 调用 ListChannels 和 ListClients
    object Synchronized : SyncState()  // 列表基线可供 UI 使用
    data class SyncFailed(val error: Throwable) : SyncState() // 同步失败
}

/**
 * 频道切换状态机。
 *
 * 状态转换：
 *   Idle → Requesting：用户发起切换请求
 *   Requesting → WaitingServerEvent：ClientMove 命令成功
 *   Requesting → Failed：ClientMove 命令失败
 *   WaitingServerEvent → Idle：收到自己的 OnClientMoved 事件
 *   Failed → Idle：用户重试或取消
 */
sealed class ChannelSwitchState {
    /** 空闲状态，可以发起新的切换 */
    object Idle : ChannelSwitchState()

    /** 正在发送 ClientMove 命令 */
    object Requesting : ChannelSwitchState()

    /** ClientMove 命令成功，等待服务端 OnClientMoved 事件确认 */
    data class WaitingServerEvent(val targetChannelId: String) : ChannelSwitchState()

    /** 切换失败（命令被拒绝或超时） */
    data class Failed(val error: String) : ChannelSwitchState()
}

/**
 * 频道树节点。
 */
data class ChannelTreeNode(
    val channel: ChannelInfo,
    val children: List<ChannelTreeNode>,
    val clients: List<ClientInfo>,
    val isExpanded: Boolean = false,
)

class ChannelViewModel(application: Application) : AndroidViewModel(application) {

    companion object {
        private const val TAG = "ChannelViewModel"
        private const val CHANNEL_LIST_STALE_THRESHOLD = 5 * 60 * 1000L // 5 分钟
    }

    private val json = Json { ignoreUnknownKeys = true }

    // ChatViewModel 引用（由 ServerViewModel 设置）
    var chatViewModel: ChatViewModel? = null

    // 频道列表最后刷新时间
    private var lastChannelRefreshTime: Long = 0

    // --- 同步状态 ---
    private val _syncState = MutableStateFlow<SyncState>(SyncState.Unsynced)
    val syncState: StateFlow<SyncState> = _syncState.asStateFlow()

    // --- 频道数据 ---
    val channels: StateFlow<List<ChannelInfo>> = Repository.channels
    val clients: StateFlow<List<ClientInfo>> = Repository.clients
    val selfClientId: StateFlow<Int> = Repository.selfClientId
    val channelClients: StateFlow<Map<String, List<ClientInfo>>> = Repository.channelClients

    // --- UI 状态 ---
    private val _expandedChannelIds = MutableStateFlow<Set<String>>(emptySet())
    val expandedChannelIds: StateFlow<Set<String>> = _expandedChannelIds.asStateFlow()

    // --- 当前频道 ---
    val currentChannelId: StateFlow<String> = Repository.currentChannelId

    // --- 频道切换状态 ---
    private val _switchState = MutableStateFlow<ChannelSwitchState>(ChannelSwitchState.Idle)
    val switchState: StateFlow<ChannelSwitchState> = _switchState.asStateFlow()

    // 频道切换确认弹窗（有密码时带输入框）
    private val _showSwitchDialog = MutableStateFlow(false)
    val showSwitchDialog: StateFlow<Boolean> = _showSwitchDialog.asStateFlow()

    // 待切换的目标频道
    private val _pendingSwitchChannel = MutableStateFlow<ChannelInfo?>(null)
    val pendingSwitchChannel: StateFlow<ChannelInfo?> = _pendingSwitchChannel.asStateFlow()

    // --- 频道详情卡状态（步骤 11） ---
    private val _channelDetailInfo = MutableStateFlow<ChannelDetailInfo?>(null)
    val channelDetailInfo: StateFlow<ChannelDetailInfo?> = _channelDetailInfo.asStateFlow()

    private val _showChannelDetailCard = MutableStateFlow(false)
    val showChannelDetailCard: StateFlow<Boolean> = _showChannelDetailCard.asStateFlow()

    // 长按打开详情卡时记录的目标频道 ID（用于"切换到此频道"）
    private var detailTargetChannelId: String = ""

    // 等待服务端确认的超时 Job
    private var switchTimeoutJob: Job? = null

    // 竞态标志：服务端事件先于命令响应到达时置 true
    @Volatile
    private var serverEventReceivedEarly = false

    // 防抖：上次打开频道详情的时间戳
    private var lastDetailClickTime: Long = 0

    /**
     * 清理频道 UI 状态（断开连接时调用）
     */
    fun clearChannels() {
        _syncState.value = SyncState.Unsynced
        _expandedChannelIds.value = emptySet()
        _switchState.value = ChannelSwitchState.Idle
        _showSwitchDialog.value = false
        _pendingSwitchChannel.value = null
        switchTimeoutJob?.cancel()
        switchTimeoutJob = null
        _channelDetailInfo.value = null
        _showChannelDetailCard.value = false
        detailTargetChannelId = ""
    }

    /**
     * 获取频道详情信息
     */
    fun fetchChannelDetail(channelId: String) {
        viewModelScope.launch {
            try {
                val detailJson = withContext(Dispatchers.IO) {
                    TSBridge.getChannelDetailInfoJSON(channelId)
                }
                if (detailJson.isNotEmpty() && detailJson != "{}") {
                    _channelDetailInfo.value = json.decodeFromString<ChannelDetailInfo>(detailJson)
                }
            } catch (e: Exception) {
                Log.e(TAG, "Failed to fetch channel detail", e)
            }
        }
    }

    fun openChannelDetailCard(channelId: String) {
        val now = System.currentTimeMillis()
        if (now - lastDetailClickTime < 500) return
        lastDetailClickTime = now
        // 先记录目标频道 ID，再异步获取详情；切频道时直接使用此 ID
        detailTargetChannelId = channelId
        _channelDetailInfo.value = null // 清除旧数据，避免残留上一个频道的信息
        fetchChannelDetail(channelId)
        _showChannelDetailCard.value = true
    }

    fun closeChannelDetailCard() {
        _showChannelDetailCard.value = false
        _channelDetailInfo.value = null
        detailTargetChannelId = ""
    }

    /**
     * 从频道详情卡切换到目标频道。
     * 直接使用 openChannelDetailCard 记录的频道 ID，不依赖异步获取的详情数据。
     */
    fun switchToDetailChannel() {
        val targetId = detailTargetChannelId
        if (targetId.isEmpty()) {
            Log.w(TAG, "switchToDetailChannel: no target channel ID stored")
            return
        }
        closeChannelDetailCard()

        // 从频道列表查找完整信息
        val channel = channels.value.find { it.id == targetId }
        if (channel != null) {
            onChannelClicked(channel)
        } else {
            // 频道列表中找不到，构造最小信息发起切换
            Log.w(TAG, "Channel $targetId not found in channel list, attempting direct switch")
            viewModelScope.launch {
                performChannelSwitch(targetId, "")
            }
        }
    }

    /**
     * 执行首次同步。
     * 由 ServerViewModel 在 OnConnected 后调用。
     * 委托给 Repository.performInitialSync()（使用 Dispatchers.IO 并行请求），
     * 成功后更新 syncState 并自动展开有成员的频道。
     */
    suspend fun performInitialSync() {
        Log.i(TAG, "performInitialSync started, setting Syncing state")
        _syncState.value = SyncState.Syncing

        val success = Repository.performInitialSync()
        Log.i(TAG, "Repository.performInitialSync returned: $success, channels=${Repository.channels.value.size}, clients=${Repository.clients.value.size}")

        if (success) {
            // 自动展开有成员的频道
            autoExpandChannelsWithClients()

            _syncState.value = SyncState.Synchronized
            lastChannelRefreshTime = System.currentTimeMillis()
            Log.i(TAG, "Initial sync completed: ${Repository.channels.value.size} channels, ${Repository.clients.value.size} clients, syncState=Synchronized")
        } else {
            Log.e(TAG, "Initial sync failed, setting SyncFailed state")
            _syncState.value = SyncState.SyncFailed(Exception("数据同步失败"))
        }
    }

    /**
     * 带重试的首次同步。
     */
    suspend fun performInitialSyncWithRetry(maxRetries: Int = 3) {
        var retryCount = 0

        while (retryCount < maxRetries) {
            try {
                performInitialSync()
                if (_syncState.value == SyncState.Synchronized) return
            } catch (e: Exception) {
                Log.w(TAG, "Sync attempt ${retryCount + 1} failed", e)
            }
            retryCount++
            if (retryCount < maxRetries) {
                delay(1000L * retryCount) // 递增延迟
            }
        }

        // 所有重试失败
        if (_syncState.value != SyncState.Synchronized) {
            _syncState.value = SyncState.SyncFailed(Exception("同步失败，已重试 $maxRetries 次"))
        }
    }

    fun retrySync() {
        viewModelScope.launch {
            performInitialSyncWithRetry()
        }
    }

    /**
     * 检查频道列表是否过期（超过 5 分钟未刷新）。
     *
     * SDK 不提供频道创建/更新/删除事件，因此频道列表可能随时间过期。
     * 在用户执行关键操作（如切换频道）时检查并刷新。
     */
    private fun isChannelListStale(): Boolean {
        val elapsed = System.currentTimeMillis() - lastChannelRefreshTime
        return elapsed > CHANNEL_LIST_STALE_THRESHOLD
    }

    /**
     * 如果频道列表过期，自动刷新。
     * 刷新失败时不阻塞业务，使用旧数据。
     */
    suspend fun refreshChannelsIfNeeded() {
        if (!isChannelListStale()) return

        try {
            Log.d(TAG, "Channel list stale, refreshing...")
            val channelsJson = withContext(Dispatchers.IO) {
                TSBridge.getChannelsJSON()
            }
            val channels = json.decodeFromString<List<ChannelInfo>>(channelsJson)
            Repository.updateChannels(channels)
            lastChannelRefreshTime = System.currentTimeMillis()
            Log.d(TAG, "Channel list refreshed: ${channels.size} channels")
        } catch (e: Exception) {
            Log.e(TAG, "Channel refresh failed, using stale data", e)
            // 不阻塞业务，使用旧数据
        }
    }

    /**
     * 处理客户端进入事件（增量同步）。
     * 刷新后对比当前频道成员，检测新加入者并发送系统消息。
     */
    fun handleClientEnter() {
        Log.d(TAG, "Client enter: refreshing client list")
        viewModelScope.launch {
            val snapshot = snapshotCurrentChannelClients()
            Repository.refreshClientList()
            notifyMemberChanges(snapshot, "加入")
        }
    }

    /**
     * 处理客户端离开事件。
     * 刷新后对比当前频道成员，检测离开者并发送系统消息。
     */
    fun handleClientLeave(clientId: Int, reasonMsg: String) {
        Log.d(TAG, "Client leave: $clientId, reason: $reasonMsg")
        viewModelScope.launch {
            val snapshot = snapshotCurrentChannelClients()
            Repository.refreshClientList()
            notifyMemberChanges(snapshot, "离开")
        }
    }

    /**
     * 快照当前频道的成员 ID 和昵称。
     * 离开事件后 clientMap 会更新，所以需要提前保存昵称。
     */
    private fun snapshotCurrentChannelClients(): Map<Int, String> {
        val channelId = Repository.currentChannelId.value
        return Repository.channelClients.value[channelId]
            ?.associate { it.id to it.nickname }
            ?: emptyMap()
    }

    /**
     * 对比快照与刷新后的当前频道成员，检测变动并发送系统消息。
     * @param before 变动前的 {clientId → nickname} 快照
     * @param action "加入" 或 "离开"
     */
    private fun notifyMemberChanges(before: Map<Int, String>, action: String) {
        val channelId = Repository.currentChannelId.value
        val after = Repository.channelClients.value[channelId]
            ?.map { it.id }
            ?.toSet()
            ?: emptySet()

        val changedIds = if (action == "加入") {
            after - before.keys
        } else {
            before.keys - after
        }

        if (changedIds.isEmpty()) return

        val cvm = chatViewModel ?: return
        for (id in changedIds) {
            val nickname = if (action == "加入") {
                Repository.getClient(id)?.nickname ?: id.toString()
            } else {
                before[id] ?: id.toString()
            }
            cvm.addSystemMessage("$nickname $action 当前频道")
        }
    }

    /**
     * 切换频道展开/折叠状态。
     */
    fun toggleExpand(channelId: String) {
        _expandedChannelIds.update { current ->
            current.toMutableSet().apply {
                if (contains(channelId)) remove(channelId) else add(channelId)
            }
        }
    }

    /**
     * 自动展开有成员的频道。
     */
    private fun autoExpandChannelsWithClients() {
        val clientsByChannel = Repository.channelClients.value
        val toExpand = clientsByChannel.filter { it.value.isNotEmpty() }.keys

        // 也展开父频道
        val channels = Repository.channels.value
        val expandedWithParents = toExpand.toMutableSet()

        for (channelId in toExpand) {
            var parentId = channels.find { it.id == channelId }?.parentId
            while (parentId != null && parentId != "0" && parentId !in expandedWithParents) {
                expandedWithParents.add(parentId)
                parentId = channels.find { it.id == parentId }?.parentId
            }
        }

        _expandedChannelIds.update { expandedWithParents }
    }

    /**
     * 构建频道树。
     */
    fun buildChannelTree(
        channels: List<ChannelInfo>,
        clients: List<ClientInfo>,
        expandedIds: Set<String>,
    ): List<ChannelTreeNode> {
        // 预计算：按频道 ID 分组客户端（O(n) 一次，替代递归中 O(n) 每次）
        val clientsByChannel = clients.groupBy { it.channelId }

        // 找出自引用的根频道（id == parentId），这些频道需要特殊处理
        val selfRefRoots = channels.filter { it.id == it.parentId }
        // 排除自引用频道后的正常频道列表
        val normalChannels = channels.filter { it.id != it.parentId }

        // 按 ParentID 分组（只包含正常频道）
        val childrenMap = normalChannels.groupBy { it.parentId }

        // 循环检测：记录已访问的频道 ID，防止 parentId 循环引用导致无限递归
        val visited = mutableSetOf<String>()

        // 递归构建树
        fun buildNode(channel: ChannelInfo): ChannelTreeNode {
            if (!visited.add(channel.id)) {
                // 检测到循环引用，终止递归
                Log.w(TAG, "Cycle detected in channel tree at channel ${channel.id} (${channel.name})")
                return ChannelTreeNode(
                    channel = channel,
                    children = emptyList(),
                    clients = clientsByChannel[channel.id] ?: emptyList(),
                    isExpanded = expandedIds.contains(channel.id),
                )
            }

            val children = (childrenMap[channel.id] ?: emptyList())
                .sortedBy { it.order }
                .map { buildNode(it) }

            return ChannelTreeNode(
                channel = channel,
                children = children,
                clients = clientsByChannel[channel.id] ?: emptyList(),
                isExpanded = expandedIds.contains(channel.id),
            )
        }

        // 其他顶级频道（parentId == "0" 且非自引用）
        val topLevel = (childrenMap["0"] ?: emptyList())
            .sortedBy { it.order }

        // 自引用根频道放在最前面（无子频道），然后是其他顶级频道
        val selfRefNodes = selfRefRoots.map { root ->
            ChannelTreeNode(
                channel = root,
                children = emptyList(),
                clients = clientsByChannel[root.id] ?: emptyList(),
                isExpanded = expandedIds.contains(root.id),
            )
        }

        return selfRefNodes + topLevel.map { buildNode(it) }
    }

    // ─── 频道切换 ─────────────────────────────────────────────

    /**
     * 处理频道点击事件。
     * 对应 UI架构设计.md 频道树交互规则。
     */
    fun onChannelClicked(channel: ChannelInfo) {
        // 如果是当前频道，忽略
        if (channel.id == Repository.currentChannelId.value) {
            Log.d(TAG, "Already in channel ${channel.id}, ignoring click")
            return
        }

        // 检查是否正在切换中
        if (_switchState.value != ChannelSwitchState.Idle) {
            Log.w(TAG, "Channel switch already in progress, ignoring click")
            return
        }

        // 弹出切换确认框（有密码时带输入框）
        _pendingSwitchChannel.value = channel
        _showSwitchDialog.value = true
    }

    /**
     * 用户确认切换（密码频道传入密码，无密码频道传空串）。
     */
    fun confirmSwitchDialog(password: String = "") {
        val channel = _pendingSwitchChannel.value ?: return
        _showSwitchDialog.value = false

        viewModelScope.launch {
            performChannelSwitch(channel.id, password)
        }
    }

    /**
     * 用户取消切换弹窗。
     */
    fun dismissSwitchDialog() {
        _showSwitchDialog.value = false
        _pendingSwitchChannel.value = null
    }

    /**
     * 执行频道切换。
     */
    private suspend fun performChannelSwitch(targetChannelId: String, password: String) {
        Log.d(TAG, "Requesting channel switch to $targetChannelId")

        // 切换前检查频道列表是否过期
        refreshChannelsIfNeeded()

        // 重置竞态标志
        serverEventReceivedEarly = false

        // 状态改为 requesting
        _switchState.value = ChannelSwitchState.Requesting

        try {
            // 发送 ClientMove 命令
            val error = withContext(Dispatchers.IO) {
                TSBridge.moveToChannel(targetChannelId, password)
            }

            if (error.isEmpty()) {
                // 命令成功。但服务端的 OnClientMoved 通知可能先于命令响应到达
                // （Go 事件循环在独立 goroutine 中处理通知），此时 handleSelfMoved
                // 已将状态设为 Idle，无需再等待。
                if (serverEventReceivedEarly) {
                    // 事件已先到达并完成切换（handleSelfMoved 已处理）
                    Log.d(TAG, "ClientMove command accepted, server event already received")
                } else if (_switchState.value is ChannelSwitchState.Requesting) {
                    _switchState.value = ChannelSwitchState.WaitingServerEvent(targetChannelId)

                    // 启动超时检测（10秒）
                    switchTimeoutJob?.cancel()
                    switchTimeoutJob = viewModelScope.launch {
                        delay(10_000)
                        // 超时：如果还在等待状态，视为失败
                        if (_switchState.value is ChannelSwitchState.WaitingServerEvent) {
                            Log.w(TAG, "Channel switch timeout waiting for server event")
                            _switchState.value = ChannelSwitchState.Failed("等待服务端确认超时")
                            // 8 秒后自动清除失败状态
                            delay(8_000)
                            if (_switchState.value is ChannelSwitchState.Failed) {
                                _switchState.value = ChannelSwitchState.Idle
                                _pendingSwitchChannel.value = null
                            }
                        }
                    }

                    Log.d(TAG, "ClientMove command accepted, waiting for server event")
                } else {
                    // 状态已被外部改变（如取消），跳过
                    Log.d(TAG, "ClientMove command accepted, switch state already changed")
                }
            } else {
                // 命令被拒绝
                Log.w(TAG, "ClientMove command rejected: $error")
                _switchState.value = ChannelSwitchState.Failed(mapMoveError(error))
            }
        } catch (e: Exception) {
            Log.e(TAG, "ClientMove command failed", e)
            _switchState.value = ChannelSwitchState.Failed("切换失败：${e.message}")
        }
    }

    /**
     * 处理客户端移动事件（区分自己和他人）。
     */
    fun handleClientMoved(clientId: Int, targetChannelId: String) {
        Log.d(TAG, "Client moved: $clientId -> channel $targetChannelId")

        val selfId = Repository.selfClientId.value

        if (clientId == selfId) {
            // 自身状态同步：这是自己的移动事件
            handleSelfMoved(targetChannelId)
        } else {
            // 增量同步：这是其他用户的移动事件
            handleOtherClientMoved(clientId, targetChannelId)
        }
    }

    /**
     * 处理自己的移动事件。
     * 全量刷新客户端列表 + 更新当前频道 ID。
     */
    private fun handleSelfMoved(targetChannelId: String) {
        val currentState = _switchState.value

        // 全量刷新客户端列表（人员变动统一用全量更新）
        viewModelScope.launch {
            Repository.refreshClientList()
        }

        // 更新当前频道 ID
        Repository.setCurrentChannelId(targetChannelId)

        when (currentState) {
            is ChannelSwitchState.WaitingServerEvent -> {
                // 正常流程：确认切换完成
                Log.d(TAG, "Channel switch confirmed by server: target=$targetChannelId")
                switchTimeoutJob?.cancel()
                _switchState.value = ChannelSwitchState.Idle
                _pendingSwitchChannel.value = null
            }
            is ChannelSwitchState.Requesting -> {
                // 罕见情况：事件先于命令响应到达
                // 设置标志位，让 performChannelSwitch 不再进入 WaitingServerEvent
                Log.d(TAG, "Server event arrived before command response")
                serverEventReceivedEarly = true
                switchTimeoutJob?.cancel()
                _switchState.value = ChannelSwitchState.Idle
                _pendingSwitchChannel.value = null
            }
            else -> {
                // 非切换流程中的移动（例如被管理员移动）
                Log.d(TAG, "Self moved by external action to channel $targetChannelId")
                _switchState.value = ChannelSwitchState.Idle
            }
        }
    }

    /**
     * 处理其他用户的移动事件。
     * 全量刷新客户端列表（人员变动统一用全量更新）。
     * 根据移动方向检测"加入"或"离开"当前频道。
     */
    private fun handleOtherClientMoved(clientId: Int, targetChannelId: String) {
        viewModelScope.launch {
            val currentChannelId = Repository.currentChannelId.value
            val snapshot = snapshotCurrentChannelClients()
            Repository.refreshClientList()

            if (targetChannelId == currentChannelId) {
                // 移入当前频道 → 检测"加入"
                notifyMemberChanges(snapshot, "加入")
            } else if (snapshot.containsKey(clientId)) {
                // 从当前频道移出 → 检测"离开"
                notifyMemberChanges(snapshot, "离开")
            }
        }
    }

    /**
     * 重试频道切换（失败后）。
     */
    fun retryChannelSwitch() {
        val channel = _pendingSwitchChannel.value ?: return
        _switchState.value = ChannelSwitchState.Idle

        viewModelScope.launch {
            performChannelSwitch(channel.id, "")
        }
    }

    /**
     * 取消频道切换。
     */
    fun cancelChannelSwitch() {
        switchTimeoutJob?.cancel()
        _switchState.value = ChannelSwitchState.Idle
        _pendingSwitchChannel.value = null
        _showSwitchDialog.value = false
    }

    /**
     * 映射 MoveToChannel 错误信息为用户友好的提示。
     */
    private fun mapMoveError(error: String): String {
        return when {
            error.contains("password", ignoreCase = true) -> "密码错误"
            error.contains("permission", ignoreCase = true) -> "权限不足"
            error.contains("full", ignoreCase = true) -> "频道已满"
            error.contains("banned", ignoreCase = true) -> "你已被该频道封禁"
            else -> "切换失败：$error"
        }
    }
}
