package com.narutohuo.xindazhou.socketio.repository

import com.narutohuo.xindazhou.core.log.ILog
import com.narutohuo.xindazhou.core.storage.StorageImpl
import com.narutohuo.xindazhou.socketio.api.SocketIOService
import com.narutohuo.xindazhou.socketio.factory.SocketIOServiceFactory
import com.narutohuo.xindazhou.socketio.model.SocketIOEvent
import com.narutohuo.xindazhou.socketio.model.SocketIOResponse
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay

/**
 * SocketIO 数据仓库
 * 
 * 负责统一管理 SocketIO 连接和数据获取
 * 完全封装在 capability-socketio 模块内部，app 模块通过 Factory 获取实例
 * 
 * 自动处理 Token 刷新：
 * - 连接前自动刷新 Token（如果需要）
 * - 连接失败时自动刷新 Token 并重连
 * - 断开连接时，如果是 Token 过期导致，自动刷新并重连
 */
class SocketIORepository(
    private val socketService: SocketIOService = SocketIOServiceFactory.getInstance()
) {
    
    companion object {
        private const val TAG = "SocketIORepository"
        
        // AuthManager 的类名（通过反射调用，避免直接依赖 base-common）
        private const val AUTH_MANAGER_CLASS = "com.narutohuo.xindazhou.common.auth.AuthManager"
        
        // refreshTokenIfNeeded 方法名
        private const val REFRESH_TOKEN_METHOD = "refreshTokenIfNeeded"
        
        // Token 刷新回调（由 SocketIOManager 设置）
        @Volatile
        var refreshTokenProvider: (suspend () -> String?)? = null
    }
    
    // 使用 extraBufferCapacity 确保事件不丢失
    private val _connectionState = MutableSharedFlow<Boolean>(extraBufferCapacity = 1)
    val connectionState: SharedFlow<Boolean> = _connectionState.asSharedFlow()
    
    private val _logMessage = MutableSharedFlow<String>(extraBufferCapacity = 64)
    val logMessage: SharedFlow<String> = _logMessage.asSharedFlow()
    
    private val _vehicleControlResponse = MutableSharedFlow<SocketIOResponse>(extraBufferCapacity = 1)
    val vehicleControlResponse: SharedFlow<SocketIOResponse> = _vehicleControlResponse.asSharedFlow()
    
    private val _vehicleAlarm = MutableSharedFlow<SocketIOResponse>(extraBufferCapacity = 1)
    val vehicleAlarm: SharedFlow<SocketIOResponse> = _vehicleAlarm.asSharedFlow()
    
    // 用于在回调中发送事件的协程作用域
    private val eventScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    
    // 用于防止重复刷新 Token 的标记（避免短时间内多次刷新）
    @Volatile
    private var isRefreshingToken = false
    
    // 记录最近一次 Token 刷新的时间（毫秒）
    @Volatile
    private var lastTokenRefreshTime = 0L
    
    // 用于防止重复重连的标记（避免断开后多次触发重连）
    @Volatile
    private var isReconnecting = false
    
    // 记录最近一次重连的时间（毫秒）
    @Volatile
    private var lastReconnectTime = 0L
    
    init {
        subscribeEvents()
    }
    
    /**
     * 连接 SocketIO
     * 
     * 自动处理 Token 刷新：
     * 1. 连接前先尝试刷新 Token（如果提供了刷新回调）
     * 2. 使用最新的 Token 连接
     * 
     * 关键：即使传入了 token，如果提供了刷新回调，也会先刷新一下
     * 这样可以确保使用最新的、未过期的 Token
     * 
     * @param serverUrl SocketIO 服务器地址（如果为 null，则从存储中获取）
     * @param token JWT Token（如果为 null，则从存储中获取或刷新）
     */
    suspend fun connect(serverUrl: String? = null, token: String? = null): Result<Unit> {
        return try {
            val finalServerUrl = serverUrl ?: StorageImpl.getString("socket_url")
            var finalToken = token
            
            if (finalServerUrl.isEmpty()) {
                return Result.failure(Exception("服务器地址未配置，无法连接"))
            }
            
            // 关键：如果提供了 Token 刷新回调，连接前总是先刷新 Token（确保使用最新的、未过期的 Token）
            // AuthManager.refreshTokenIfNeeded 会判断是否需要刷新，如果 Token 未过期则返回当前 Token
            if (refreshTokenProvider != null) {
                ILog.d(TAG, "connect: 连接前自动刷新 Token（如果需要）...")
                val refreshedToken = tryRefreshToken()
                if (!refreshedToken.isNullOrEmpty()) {
                    finalToken = refreshedToken
                    ILog.d(TAG, "connect: Token 已刷新/确认，新 Token 长度: ${refreshedToken.length}")
                } else {
                    // 如果刷新失败，使用传入的 token 或存储中的 token
                    finalToken = token ?: StorageImpl.getString("access_token")
                    ILog.w(TAG, "connect: Token 刷新失败，使用原有 Token")
                }
            } else {
                // 如果没有提供刷新回调，使用传入的 token 或存储中的 token
                finalToken = token ?: StorageImpl.getString("access_token")
            }
            
            if (finalToken.isNullOrEmpty()) {
                return Result.failure(Exception("未登录（Token 为空），无法连接"))
            }
            
            ILog.d(TAG, "connect: 正在连接 $finalServerUrl, Token 长度: ${finalToken.length}")
            socketService.connect(finalServerUrl, finalToken)
            Result.success(Unit)
        } catch (e: Exception) {
            ILog.e(TAG, "connect: 连接失败", e)
            Result.failure(e)
        }
    }
    
    /**
     * 断开 SocketIO 连接
     */
    suspend fun disconnect(): Result<Unit> {
        return try {
            socketService.disconnect()
            ILog.d(TAG, "disconnect: 已断开连接")
            Result.success(Unit)
        } catch (e: Exception) {
            ILog.e(TAG, "disconnect: 断开连接失败", e)
            Result.failure(e)
        }
    }
    
    /**
     * 检查连接状态
     */
    fun isConnected(): Boolean {
        return socketService.isConnected()
    }
    
    /**
     * 检查连接状态，如果断开则自动重连
     * 
     * 用于 App 进入前台时自动重连的场景
     * 自动处理 Token 刷新：如果连接失败，会自动尝试刷新 Token 后重连
     * 
     * @param serverUrl SocketIO 服务器地址（如果为 null，则从存储中获取）
     * @param token JWT Token（如果为 null，则从存储中获取或刷新）
     * @return 如果已连接返回 true，如果正在重连返回 false（重连是异步的）
     */
    suspend fun checkAndReconnect(serverUrl: String? = null, token: String? = null): Boolean {
        return if (socketService.isConnected()) {
            // 已连接，无需重连
            ILog.d(TAG, "checkAndReconnect: 已连接，无需重连")
            true
        } else {
            // 未连接，尝试重连
            ILog.d(TAG, "checkAndReconnect: 未连接，开始重连...")
            
            // 第一次尝试连接（connect 方法内部会自动刷新 Token 如果需要）
            val result = connect(serverUrl, token)
            
            if (result.isSuccess) {
                // 等待 2 秒，检查连接是否真正成功
                delay(2000)
                
                if (socketService.isConnected()) {
                    ILog.d(TAG, "checkAndReconnect: 重连成功")
                    return true
                } else {
                    // 连接失败，可能是 Token 过期，尝试刷新 Token 后重连
                    ILog.w(TAG, "checkAndReconnect: 连接后仍未成功，可能是 Token 过期，尝试刷新 Token...")
                }
            } else {
                // 连接失败，可能是 Token 过期
                ILog.w(TAG, "checkAndReconnect: 首次连接失败: ${result.exceptionOrNull()?.message}")
            }
            
            // 第二次尝试：刷新 Token 后重连（如果提供了刷新回调）
            if (refreshTokenProvider != null) {
                val refreshedToken = tryRefreshToken()
                if (!refreshedToken.isNullOrEmpty()) {
                    ILog.d(TAG, "checkAndReconnect: Token 已刷新，使用新 Token 重连...")
                    val retryResult = connect(serverUrl, refreshedToken)
                    if (retryResult.isSuccess) {
                        delay(2000)
                        if (socketService.isConnected()) {
                            ILog.d(TAG, "checkAndReconnect: 刷新 Token 后重连成功")
                            return true
                        }
                    }
                }
            }
            
            ILog.e(TAG, "checkAndReconnect: 重连失败（已尝试刷新 Token）")
            false
        }
    }
    
    /**
     * 处理自动重连逻辑（统一处理 disconnect 和 connect_error 事件）
     * 
     * 关键：这是自动重连机制，完全在 capability-socketio 内部处理
     * 如果连接断开，自动刷新 Token 并重连（如果提供了刷新回调）
     * 
     * @param source 触发来源（用于日志记录）
     */
    private suspend fun handleAutoReconnect(source: String) {
        // 防重复重连：如果正在重连或刚重连过（5秒内），跳过
        val currentTime = System.currentTimeMillis()
        if (isReconnecting || (currentTime - lastReconnectTime < 5000)) {
            ILog.d(TAG, "$source: 正在重连或刚重连过（${(currentTime - lastReconnectTime) / 1000}秒前），跳过自动重连")
            return
        }
        
        // 如果没有提供刷新回调，无法自动重连
        if (refreshTokenProvider == null) {
            ILog.d(TAG, "$source: 未提供 Token 刷新回调，无法自动重连")
            return
        }
        
        // 如果已经连接，无需重连
        if (socketService.isConnected()) {
            ILog.d(TAG, "$source: 已连接，无需重连")
            return
        }
        
        // 延迟 2 秒后尝试重连（给服务端一些时间，避免立即重连导致问题）
        delay(2000)
        
        // 再次检查连接状态（可能在此期间已经连接了）
        if (socketService.isConnected()) {
            ILog.d(TAG, "$source: 延迟期间已连接，跳过重连")
            return
        }
        
        isReconnecting = true
        lastReconnectTime = System.currentTimeMillis()
        
        try {
            ILog.d(TAG, "$source: 检测到断开连接，尝试刷新 Token 并重连...")
            val refreshedToken = tryRefreshToken()
            if (!refreshedToken.isNullOrEmpty()) {
                // 使用新 Token 重连
                val result = connect(null, refreshedToken)
                if (result.isSuccess) {
                    // 等待 2 秒，确认连接是否真正成功
                    delay(2000)
                    if (socketService.isConnected()) {
                        ILog.d(TAG, "$source: 刷新 Token 后重连成功 ✅")
                        _logMessage.tryEmit("🔄 自动刷新 Token 并重连成功")
                    } else {
                        ILog.w(TAG, "$source: 刷新 Token 后重连失败（连接未建立）")
                    }
                } else {
                    ILog.w(TAG, "$source: 刷新 Token 后重连失败: ${result.exceptionOrNull()?.message}")
                }
            } else {
                ILog.w(TAG, "$source: Token 刷新失败，无法重连")
            }
        } catch (e: Exception) {
            ILog.e(TAG, "$source: 自动重连异常", e)
        } finally {
            // 延迟重置标记，避免短时间内重复重连
            delay(3000)
            isReconnecting = false
        }
    }
    
    /**
     * 尝试刷新 Token（带防重复刷新机制）
     * 
     * 关键：即使 Token 未过期，也会调用刷新回调（由 AuthManager 判断是否需要刷新）
     * AuthManager.refreshTokenIfNeeded 会判断 Token 是否过期，如果未过期则返回当前 Token
     * 
     * @return 新的 Access Token，如果刷新失败返回 null
     */
    private suspend fun tryRefreshToken(): String? {
        // 防止短时间内多次刷新（避免频繁请求，5秒内最多刷新一次）
        val currentTime = System.currentTimeMillis()
        if (isRefreshingToken) {
            ILog.d(TAG, "tryRefreshToken: 正在刷新中，等待...")
            // 等待刷新完成，最多等待 3 秒
            var waitCount = 0
            while (isRefreshingToken && waitCount < 30) {
                delay(100)
                waitCount++
            }
            // 刷新完成后，从存储中获取最新的 Token
            return StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
        }
        
        if (currentTime - lastTokenRefreshTime < 5000) {
            ILog.d(TAG, "tryRefreshToken: 刚刷新过（${(currentTime - lastTokenRefreshTime) / 1000}秒前），使用存储中的 Token")
            return StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
        }
        
        return try {
            isRefreshingToken = true
            lastTokenRefreshTime = currentTime
            
            ILog.d(TAG, "tryRefreshToken: 开始刷新 Token（如果需要）...")
            val newToken = refreshTokenProvider?.invoke()
            
            if (!newToken.isNullOrEmpty()) {
                ILog.d(TAG, "tryRefreshToken: Token 刷新成功，新 Token 长度: ${newToken.length}")
                // 注意：Token 已经由 AuthManager.refreshTokenIfNeeded 保存到 StorageImpl，这里不需要再次保存
                // 但为了确保一致性，我们可以从 StorageImpl 重新读取
                val storedToken = StorageImpl.getString("access_token")
                if (storedToken.isNotEmpty() && storedToken == newToken) {
                    ILog.d(TAG, "tryRefreshToken: Token 已保存到存储，验证一致")
                } else {
                    ILog.w(TAG, "tryRefreshToken: 警告：存储中的 Token 与刷新返回的 Token 不一致")
                }
                newToken
            } else {
                ILog.w(TAG, "tryRefreshToken: Token 刷新失败（返回 null），尝试使用存储中的 Token")
                // 刷新失败，尝试使用存储中的 Token
                StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
            }
        } catch (e: Exception) {
            ILog.e(TAG, "tryRefreshToken: Token 刷新异常", e)
            // 刷新异常，尝试使用存储中的 Token
            StorageImpl.getString("access_token").takeIf { it.isNotEmpty() }
        } finally {
            isRefreshingToken = false
        }
    }
    
    /**
     * 发送车辆控制指令
     */
    suspend fun sendVehicleControl(command: String, vehicleId: Long): Result<Unit> {
        return try {
            if (!socketService.isConnected()) {
                return Result.failure(Exception("未连接，无法发送消息"))
            }
            
            val data = mapOf(
                "command" to command,
                "vehicleId" to vehicleId,
                "timestamp" to System.currentTimeMillis()
            )
            socketService.emit(SocketIOEvent.VEHICLE_CONTROL, data)
            ILog.d(TAG, "sendVehicleControl: 发送指令 $command")
            Result.success(Unit)
        } catch (e: Exception) {
            ILog.e(TAG, "sendVehicleControl: 发送失败", e)
            Result.failure(e)
        }
    }
    
    /**
     * 订阅 SocketIO 事件
     */
    private fun subscribeEvents() {
        ILog.d(TAG, "subscribeEvents: 开始注册事件监听器...")
        
        // 订阅连接成功事件
        socketService.on(SocketIOEvent.CONNECT) { response ->
            ILog.d(TAG, "onConnect - 触发连接状态更新")
            // 在协程作用域中使用 emit 确保事件被发送
            eventScope.launch {
                _connectionState.emit(true)
                ILog.d(TAG, "onConnect - 连接状态已更新为 true")
            }
            _logMessage.tryEmit("✅ 连接成功")
        }
        
        // 订阅断开连接事件
        // 关键：断开连接时，自动刷新 Token 并重连（如果是 Token 过期导致）
        // 完全在 capability-socketio 内部自动处理，不需要外部干预
        socketService.on(SocketIOEvent.DISCONNECT) { response ->
            val disconnectReason = response.data
            ILog.d(TAG, "onDisconnect: reason=$disconnectReason")
            
            eventScope.launch {
                _connectionState.emit(false)
            }
            _logMessage.tryEmit("❌ 断开连接: $disconnectReason")
            
            // 如果提供了 Token 刷新回调，且未连接，尝试刷新 Token 并重连
            // 关键：这里是自动重连机制，完全在 capability-socketio 内部处理
            eventScope.launch(Dispatchers.IO) {
                handleAutoReconnect("onDisconnect")
            }
        }
        
        // 订阅连接错误事件
        // 关键：连接错误时，自动刷新 Token 并重连（可能是 Token 过期）
        // 完全在 capability-socketio 内部自动处理，不需要外部干预
        socketService.on(SocketIOEvent.CONNECT_ERROR) { response ->
            val errorMsg = response.data
            ILog.e(TAG, "onConnectError: $errorMsg")
            
            eventScope.launch {
                _connectionState.emit(false)
            }
            _logMessage.tryEmit("❌ 连接错误：$errorMsg")
            
            // 如果提供了 Token 刷新回调，且未连接，尝试刷新 Token 并重连
            // 关键：这里是自动重连机制，完全在 capability-socketio 内部处理
            eventScope.launch(Dispatchers.IO) {
                handleAutoReconnect("onConnectError")
            }
        }
        
        // 订阅车辆控制响应事件
        // 注意：使用常量而不是硬编码字符串，但 capability-socketio 模块的 SocketIOEvent 没有定义此常量
        // 所以暂时使用硬编码字符串，应该与 base-common 模块的 SocketIOEvent.VEHICLE_CONTROL_RESPONSE 保持一致
        socketService.on("vehicle_control_response") { response ->
            ILog.d(TAG, "onVehicleControlResponse: ✅ 收到车辆控制响应事件！")
            ILog.d(TAG, "onVehicleControlResponse: event=${response.event}, data=${response.data}, timestamp=${response.timestamp}")
            _logMessage.tryEmit("📩 收到车辆控制响应：${response.data}")
            _vehicleControlResponse.tryEmit(response)
        }
        
        ILog.d(TAG, "subscribeEvents: 事件监听器注册完成，已注册 vehicle_control_response 监听器")
        
        // 订阅车辆报警事件
        socketService.on(SocketIOEvent.VEHICLE_ALARM) { response ->
            ILog.d(TAG, "onVehicleAlarm: ${response.data}")
            _logMessage.tryEmit("🚨 收到车辆报警推送")
            _logMessage.tryEmit("   事件: ${response.event}")
            _logMessage.tryEmit("   数据: ${response.data}")
            _vehicleAlarm.tryEmit(response)
        }
    }
}

