package com.narutohuo.xindazhou.socketio.manager

import android.app.Application
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import com.narutohuo.xindazhou.core.log.ILog
import com.narutohuo.xindazhou.core.storage.StorageImpl
import com.narutohuo.xindazhou.socketio.factory.SocketIORepositoryFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/**
 * SocketIO 连接管理器
 * 
 * 负责自动管理 SocketIO 连接的生命周期：
 * - App 进入前台时自动重连
 * - Token 过期时自动刷新并重连
 * - 监听应用生命周期
 * 
 * 使用方式：
 * ```kotlin
 * // 在 Application.onCreate() 中初始化
 * SocketIOManager.init(application)
 * ```
 */
object SocketIOManager : DefaultLifecycleObserver {
    
    private const val TAG = "SocketIOManager"
    
    private var application: Application? = null
    private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private var initialized = false
    
    /**
     * 登录状态检查回调（由业务层设置）
     * 避免能力层直接依赖业务层的 AuthManager
     */
    var isLoggedInProvider: (() -> Boolean)? = null
    
    /**
     * Token 刷新回调（由业务层设置）
     * 避免能力层直接依赖业务层的 AuthManager
     * 注意：这是一个 suspend 函数，因为 Token 刷新是异步操作
     */
    var refreshTokenProvider: (suspend () -> String?)? = null
    
    /**
     * 初始化 SocketIO 管理器
     * 
     * 需要在 Application.onCreate() 中调用
     * 会自动注册生命周期监听
     * 
     * @param app Application 实例
     */
    fun init(app: Application) {
        if (initialized) {
            ILog.w(TAG, "SocketIOManager 已初始化，跳过重复初始化")
            return
        }
        
        application = app
        initialized = true
        
        // 注册 App 生命周期监听（直接依赖，不使用反射）
        try {
            ProcessLifecycleOwner.get().lifecycle.addObserver(this)
            ILog.d(TAG, "SocketIOManager 初始化完成（生命周期监听已注册）")
        } catch (e: Exception) {
            ILog.e(TAG, "注册生命周期监听失败", e)
        }
    }
    
    /**
     * 设置回调函数
     * 
     * 由业务层调用，设置登录状态检查和 Token 刷新回调
     * 可以在 init 之后调用，也可以随时更新
     * 
     * 关键：同时设置到 SocketIORepository，这样 SocketIORepository 内部可以自动刷新 Token
     * 
     * @param isLoggedInProvider 登录状态检查回调（可选）
     * @param refreshTokenProvider Token 刷新回调（可选）
     */
    fun setCallbacks(
        isLoggedInProvider: (() -> Boolean)? = null,
        refreshTokenProvider: (suspend () -> String?)? = null
    ) {
        if (isLoggedInProvider != null) {
            this.isLoggedInProvider = isLoggedInProvider
            ILog.d(TAG, "登录状态检查回调已设置")
        }
        if (refreshTokenProvider != null) {
            this.refreshTokenProvider = refreshTokenProvider
            // 关键：同时设置到 SocketIORepository，让 SocketIORepository 内部自动处理 Token 刷新
            com.narutohuo.xindazhou.socketio.repository.SocketIORepository.refreshTokenProvider = refreshTokenProvider
            ILog.d(TAG, "Token 刷新回调已设置（已同步到 SocketIORepository）")
        }
    }
    
    /**
     * App 进入前台时调用
     * 
     * 检查 SocketIO 连接状态，如果断开则自动重连
     * 如果连接失败（可能是 Token 过期），会自动刷新 Token 后重连
     */
    override fun onStart(owner: LifecycleOwner) {
        ILog.d(TAG, "App 进入前台，检查 SocketIO 连接状态")
        
        // 检查是否已登录（通过回调函数，避免直接依赖 AuthManager）
        if (isLoggedInProvider?.invoke() != true) {
            ILog.d(TAG, "用户未登录，跳过 SocketIO 重连")
            return
        }
        
        // 检查连接状态，如果断开则重连
        appScope.launch {
            try {
                // 直接获取 SocketIORepository（不使用反射）
                val socketIORepository = SocketIORepositoryFactory.getInstance()
                
                // 检查是否已连接
                if (socketIORepository.isConnected()) {
                    ILog.d(TAG, "SocketIO 已连接，无需重连")
                    return@launch
                }
                
                // 检查 token 是否存在
                val token = StorageImpl.getString("access_token")
                if (token.isEmpty()) {
                    ILog.w(TAG, "Token 为空，无法重连 SocketIO")
                    return@launch
                }
                
                // 先尝试连接（不传参数，让它从存储中获取）
                ILog.d(TAG, "SocketIO 未连接，开始重连...")
                socketIORepository.checkAndReconnect(null, null)
                
                // 等待 2 秒，检查连接是否成功
                delay(2000)
                
                // 如果连接仍然失败，可能是 Token 过期，尝试刷新 Token 后重连
                if (!socketIORepository.isConnected()) {
                    ILog.d(TAG, "连接失败，可能是 Token 过期，尝试刷新 Token 后重连")
                    val refreshedToken = refreshTokenProvider?.invoke() // 在协程作用域中调用 suspend 函数
                    if (!refreshedToken.isNullOrEmpty() && refreshedToken != token) {
                        ILog.d(TAG, "Token 已刷新，使用新 Token 重连")
                        socketIORepository.checkAndReconnect(null, null)
                    } else {
                        ILog.w(TAG, "Token 刷新失败或未刷新，无法重连")
                    }
                } else {
                    ILog.d(TAG, "SocketIO 重连成功")
                }
            } catch (e: Exception) {
                ILog.e(TAG, "SocketIO 重连失败", e)
            }
        }
    }
    
    /**
     * App 进入后台时调用
     * 
     * 注意：通常不断开 SocketIO 连接，因为：
     * 1. SocketIO 连接是轻量级的
     * 2. 用户可能需要在后台接收消息
     * 3. 系统会在内存不足时自动清理
     */
    override fun onStop(owner: LifecycleOwner) {
        ILog.d(TAG, "App 进入后台")
        // 不断开连接，保持连接以便接收消息
    }
}

