package com.narutohuo.xindazhou.common.socketio

import android.app.Application
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import com.narutohuo.xindazhou.common.auth.AuthManager
import com.narutohuo.xindazhou.common.auth.utils.JWTUtil
import com.narutohuo.xindazhou.common.config.ServerConfigManager
import com.narutohuo.xindazhou.common.log.LogHelper
import com.narutohuo.xindazhou.common.socketio.impl.SocketIOClient
import com.narutohuo.xindazhou.common.socketio.model.SocketIOResponse
import com.narutohuo.xindazhou.core.storage.StorageImpl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch

/**
 * SocketIO 管理器（业务封装层）
 * 
 * 与 AuthManager、VersionUpdateManager 类似，提供统一的 Socket.IO 封装
 * 自动处理连接、重连、Token 刷新等，外部只需要订阅即可
 * 
 * 职责：
 * ✅ 自动处理连接（包括 Token 刷新）
 * ✅ 自动处理重连
 * ✅ 提供消息订阅接口
 * ✅ 外部只需要订阅即可
 * 
 * 使用方式：
 * ```kotlin
 * // 在 AppInitializer 中初始化（可选，会自动连接）
 * SocketIOManager.initialize(application)
 * 
 * // 在 ViewModel 中订阅消息
 * viewModelScope.launch {
 *     SocketIOManager.shared.subscribe("community_message").collect { response ->
 *         // 处理社区消息
 *     }
 * }
 * ```
 */
object SocketIOManager : DefaultLifecycleObserver {
    
    private const val TAG = "SocketIOManager"
    
    // 单例实例
    val shared = SocketIOManager
    
    private val socketClient = SocketIOClient.getInstance()
    private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    
    private var application: Application? = null
    private var serverURL: String? = null
    private var isInitialized = false
    
    // 连接状态（使用 replay = 1 确保新订阅者能立即获取最新状态）
    private val _connectionState = MutableSharedFlow<Boolean>(extraBufferCapacity = 1, replay = 1)
    val connectionState: SharedFlow<Boolean> = _connectionState.asSharedFlow()
    
    init {
        // 初始化连接状态（同步获取当前状态）
        _connectionState.tryEmit(isConnected())
        
        // 订阅连接事件
        subscribeConnectionEvents()
    }
    
    // 消息订阅（使用 SharedFlow）
    private val eventFlows = mutableMapOf<String, MutableSharedFlow<SocketIOResponse>>()
    
    // Token 刷新防抖
    @Volatile
    private var isRefreshingToken = false
    
    @Volatile
    private var lastTokenRefreshTime = 0L
    
    // 重连防抖
    @Volatile
    private var isReconnecting = false
    
    @Volatile
    private var lastReconnectTime = 0L
    
    /**
     * 初始化并自动连接（如果用户已登录）
     * 
     * 在 AppInitializer 中调用一次即可
     * 后续会自动处理连接、重连、Token 刷新等
     * 
     * 注意：
     * - 如果用户未登录，不会立即连接，等待登录成功后调用 ensureConnected()
     * - 如果用户已登录，会自动连接
     * 
     * @param application Application 实例（用于生命周期监听）
     */
    fun initialize(application: Application) {
        if (isInitialized) {
            LogHelper.d(TAG, "已初始化，跳过")
            return
        }
        
        this.application = application
        isInitialized = true
        
        // 注册 App 生命周期监听
        try {
            ProcessLifecycleOwner.get().lifecycle.addObserver(this)
            LogHelper.d(TAG, "生命周期监听已注册")
        } catch (e: Exception) {
            LogHelper.e(TAG, "注册生命周期监听失败", e)
        }
        
        // 如果用户已登录，自动连接
        appScope.launch {
            if (AuthManager.isLoggedIn()) {
                LogHelper.d(TAG, "用户已登录，自动连接 Socket.IO...")
                connect()
            } else {
                LogHelper.d(TAG, "用户未登录，等待登录成功后连接")
                // 更新连接状态为 false（用户未登录）
                _connectionState.emit(false)
            }
        }
        
        LogHelper.d(TAG, "SocketIOManager 初始化完成（自动处理连接和Token刷新）")
    }
    
    /**
     * 确保已连接（如果未连接则自动连接）
     * 
     * 在用户登录成功后调用，确保 Socket.IO 已连接
     * 如果已连接，则不做任何操作
     * 如果未连接，会自动处理连接（包括 Token 刷新）
     * 
     * 使用场景：
     * - 用户登录成功后调用
     * - 应用启动后检查连接状态时调用
     * - 前台重连时调用
     */
    fun ensureConnected() {
        appScope.launch {
            // 检查是否已登录
            if (!AuthManager.isLoggedIn()) {
                LogHelper.d(TAG, "用户未登录，无法连接")
                _connectionState.emit(false)
                return@launch
            }
            
            // 如果已连接，更新状态并返回
            if (isConnected()) {
                LogHelper.d(TAG, "已连接，状态正常")
                _connectionState.emit(true)
                return@launch
            }
            
            // 未连接，尝试重连
            LogHelper.d(TAG, "检测到未连接，开始重连...")
            checkAndReconnect()
        }
    }
    
    /**
     * 连接 Socket.IO（自动处理 Token 刷新）
     */
    private suspend fun connect() {
        LogHelper.d(TAG, "=== Socket.IO 连接开始 ===")
        
        // 获取服务器地址
        val url = serverURL ?: ServerConfigManager.getSocketIOUrl()
        if (url.isEmpty()) {
            LogHelper.e(TAG, "服务器地址未配置，无法连接")
            _connectionState.emit(false)
            return
        }
        
        this.serverURL = url
        
        // 获取 Token，如果为空或过期则刷新
        var token = StorageImpl.getString("access_token")
        
        // 如果 Token 为空，尝试刷新
        if (token.isEmpty()) {
            LogHelper.d(TAG, "Token 为空，尝试刷新...")
            token = refreshTokenIfNeeded() ?: ""
            if (token.isEmpty()) {
                LogHelper.e(TAG, "Token 为空且刷新失败，无法连接")
                _connectionState.emit(false)
                return
            }
        }
        
        // 如果 Token 已过期，尝试刷新
        if (!JWTUtil.isTokenValid(token)) {
            LogHelper.w(TAG, "Token 已过期，尝试刷新...")
            val expiresAt = JWTUtil.getExpiresAt(token)
            if (expiresAt != null) {
                val expiresDate = java.util.Date(expiresAt * 1000) // 转换为毫秒
                LogHelper.d(TAG, "Token 过期时间: $expiresDate")
            }
            token = refreshTokenIfNeeded() ?: token // 如果刷新失败，仍然尝试使用旧 Token（由服务端判断）
            
            // 如果刷新后仍然无效，返回
            if (token.isEmpty() || !JWTUtil.isTokenValid(token)) {
                LogHelper.e(TAG, "Token 刷新失败或仍然无效，无法连接")
                _connectionState.emit(false)
                return
            }
            LogHelper.d(TAG, "Token 刷新成功，使用新 Token 连接...")
        }
        
        LogHelper.d(TAG, "正在连接到服务器: $url")
        socketClient.connect(url, token)
    }
    
    /**
     * 断开连接
     */
    fun disconnect() {
        LogHelper.d(TAG, "断开 Socket.IO 连接")
        socketClient.disconnect()
        appScope.launch {
            _connectionState.emit(false)
        }
    }
    
    /**
     * 检查连接状态
     */
    fun isConnected(): Boolean {
        return socketClient.isConnected()
    }
    
    /**
     * 自动重连（如果断开）
     */
    private suspend fun checkAndReconnect() {
        if (isConnected()) {
            return
        }
        
        // 防重复重连（5秒内最多重连一次）
        val currentTime = System.currentTimeMillis()
        if (isReconnecting || (currentTime - lastReconnectTime < 5000)) {
            LogHelper.d(TAG, "正在重连或刚重连过，跳过")
            return
        }
        
        isReconnecting = true
        lastReconnectTime = currentTime
        
        try {
            LogHelper.d(TAG, "检测到断开连接，开始重连...")
            
            // 如果未初始化，先初始化
            if (!isInitialized) {
                LogHelper.d(TAG, "检测到未初始化，先执行初始化...")
                val app = application ?: run {
                    LogHelper.e(TAG, "Application 未设置，无法初始化")
                    return
                }
                initialize(app)
                return
            }
            
            // 刷新 Token 后重连
            val newToken = refreshTokenIfNeeded()
            if (!newToken.isNullOrEmpty()) {
                LogHelper.d(TAG, "Token 已刷新，使用新 Token 重连...")
                delay(1000) // 延迟1秒，避免过于频繁的重连尝试
                connect()
            } else {
                LogHelper.e(TAG, "Token 刷新失败，无法重连")
            }
        } finally {
            isReconnecting = false
        }
    }
    
    /**
     * 订阅消息（返回 SharedFlow）
     * 
     * 各个业务模块使用此方法订阅需要的消息类型
     * 
     * 注意：
     * - 如果未连接，会自动尝试连接
     * - 如果用户未登录，会等待登录成功后连接
     * 
     * 示例：
     * ```kotlin
     * // CommunityViewModel.kt
     * viewModelScope.launch {
     *     SocketIOManager.shared.subscribe("community_message").collect { response ->
     *         // 处理社区消息
     *     }
     * }
     * ```
     */
    fun subscribe(eventName: String): SharedFlow<SocketIOResponse> {
        return eventFlows.getOrPut(eventName) {
            MutableSharedFlow<SocketIOResponse>(extraBufferCapacity = 1).also { flow ->
                // 订阅 Socket.IO 事件
                socketClient.on(eventName) { response ->
                    appScope.launch {
                        flow.emit(response)
                    }
                }
                
                // 如果未连接，尝试连接（使用 ensureConnected 统一处理）
                if (!isConnected()) {
                    ensureConnected()
                }
            }
        }.asSharedFlow()
    }
    
    /**
     * 发送消息
     */
    fun emit(eventName: String, data: Any) {
        if (!isConnected()) {
            LogHelper.w(TAG, "Socket.IO 未连接，无法发送事件: $eventName，尝试重新连接...")
            appScope.launch {
                checkAndReconnect()
            }
            return
        }
        
        socketClient.emit(eventName, data)
    }
    
    // MARK: - 生命周期监听
    
    /**
     * App 进入前台时调用
     * 
     * 检查 SocketIO 连接状态，如果断开则自动重连
     * 确保应用回到前台时，Socket.IO 连接状态正常
     */
    override fun onStart(owner: LifecycleOwner) {
        LogHelper.d(TAG, "App 进入前台，检查 SocketIO 连接状态")
        
        // 使用 ensureConnected() 统一处理连接逻辑
        ensureConnected()
    }
    
    /**
     * App 进入后台时调用
     * 
     * 注意：通常不断开 SocketIO 连接，因为：
     * 1. SocketIO 连接是轻量级的
     * 2. 用户可能需要在后台接收消息
     * 3. 系统会在内存不足时自动清理
     */
    override fun onStop(owner: LifecycleOwner) {
        LogHelper.d(TAG, "App 进入后台")
        // 不断开连接，保持连接以便接收消息
    }
    
    // MARK: - Private Methods
    
    /**
     * 订阅连接事件
     */
    private fun subscribeConnectionEvents() {
        socketClient.on("connect") { response ->
            appScope.launch {
                _connectionState.emit(true)
                LogHelper.d(TAG, "✅ Socket.IO 连接成功")
            }
        }
        
        socketClient.on("disconnect") { response ->
            appScope.launch {
                _connectionState.emit(false)
                LogHelper.d(TAG, "❌ Socket.IO 断开连接: ${response.data}")
                
                // 自动重连（延迟2秒）
                delay(2000)
                checkAndReconnect()
            }
        }
        
        socketClient.on("connect_error") { response ->
            appScope.launch {
                _connectionState.emit(false)
                val errorMsg = response.data
                LogHelper.e(TAG, "❌ Socket.IO 连接错误: $errorMsg")
                
                // 如果是 Token 错误，刷新后重连
                if (errorMsg.contains("token", ignoreCase = true) || 
                    errorMsg.contains("unauthorized", ignoreCase = true)) {
                    refreshTokenAndReconnect()
                } else {
                    // 其他错误也尝试重连
                    delay(2000)
                    checkAndReconnect()
                }
            }
        }
    }
    
    /**
     * 刷新 Token 并重连
     */
    private suspend fun refreshTokenAndReconnect() {
        val newToken = refreshTokenIfNeeded()
        if (!newToken.isNullOrEmpty()) {
            LogHelper.d(TAG, "Token 已刷新，使用新 Token 重连...")
            delay(1000) // 延迟1秒
            connect()
        } else {
            LogHelper.e(TAG, "Token 刷新失败，无法重连")
        }
    }
    
    /**
     * 刷新 Token（如果需要）
     * 
     * 使用 AuthManager 处理 Token 刷新
     */
    private suspend fun refreshTokenIfNeeded(): String? {
        // 防止短时间内多次刷新（5秒内最多刷新一次）
        val currentTime = System.currentTimeMillis()
        if (isRefreshingToken) {
            LogHelper.d(TAG, "正在刷新 Token，等待...")
            // 等待刷新完成，最多等待3秒
            var waitCount = 0
            while (isRefreshingToken && waitCount < 30) {
                delay(100) // 0.1秒
                waitCount++
            }
            // 刷新完成后，从存储中获取最新的 Token
            return StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
        }
        
        if (currentTime - lastTokenRefreshTime < 5000) {
            LogHelper.d(TAG, "刚刷新过 Token（${(currentTime - lastTokenRefreshTime) / 1000}秒前），使用存储中的 Token")
            return StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
        }
        
        return try {
            isRefreshingToken = true
            lastTokenRefreshTime = currentTime
            
            LogHelper.d(TAG, "开始刷新 Token...")
            
            // 使用 base-common 的 AuthManager
            val newToken = AuthManager.refreshTokenIfNeeded()
            
            if (!newToken.isNullOrEmpty()) {
                LogHelper.d(TAG, "Token 刷新成功")
                newToken
            } else {
                LogHelper.w(TAG, "Token 刷新失败（返回 null）")
                null
            }
        } catch (e: Exception) {
            LogHelper.e(TAG, "Token 刷新异常", e)
            null
        } finally {
            isRefreshingToken = false
        }
    }
}

