import {defineStore} from 'pinia'
import {ref, computed} from 'vue'
import {bonProtocol, GameMessages, g_utils} from '../utils/bonProtocol.js'
import {XyzwWebSocketClient} from '../utils/xyzwWebSocket.js'
import {findAnswer} from '../utils/studyQuestionsFromJSON.js'
import {tokenLogger, wsLogger, gameLogger} from '../utils/logger.js'
import indexedDBStorage from '../utils/indexedDBStorage.js'

/** 日志配置检查（保留用于批量任务日志配置） */
const shouldLog = (type) => {
  try {
    const config = JSON.parse(localStorage.getItem('batchTaskLogConfig') || '{}')
    return config[type] === true
  } catch {
    return false
  }
}

// 本地logger包装器 - 结合shouldLog和logger系统
// 对于websocket日志，如果批量任务日志配置打开，则显示debug级别
// 否则使用logger的默认级别控制
const log = {
  debug: (msg, ...args) => {
    // DEBUG级别的日志受shouldLog控制（用于批量任务兼容性）
    if (shouldLog('websocket')) {
      tokenLogger.debug(msg, ...args)
    }
  },
  info: (msg, ...args) => tokenLogger.info(msg, ...args),
  warn: (msg, ...args) => {
    // WARN级别的日志也受控制（用于游戏消息处理跳过）
    if (msg?.includes('消息处理跳过') || msg?.includes('无法找到cmd')) {
      if (shouldLog('gameMessage')) {
        tokenLogger.warn(msg, ...args)
      }
    } else {
      tokenLogger.warn(msg, ...args)
    }
  },
  error: (msg, ...args) => tokenLogger.error(msg, ...args)
}

/**
 * 重构后的Token管理存储
 * 以名称-token列表形式管理多个游戏角色
 */
export const useTokenStore = defineStore('tokens', () => {
    // 状态
    // 🔧 修复：确保从 localStorage 加载的数据是有效数组
    let initialTokens = []
    try {
        const stored = localStorage.getItem('gameTokens')
        if (stored) {
            const parsed = JSON.parse(stored)
            initialTokens = Array.isArray(parsed) ? parsed : []
        }
    } catch (error) {
        tokenLogger.error('❌ 解析 gameTokens 失败，使用空数组', error)
        initialTokens = []
    }
    const gameTokens = ref(initialTokens)
    const selectedTokenId = ref(localStorage.getItem('selectedTokenId') || null)
    const wsConnections = ref({}) // WebSocket连接状态
    const wsTimers = ref({}) // 🆕 v3.14.0: 跟踪每个token的定时器，用于清理
    
    // 🆕 v3.14.0: 创建可追踪的定时器（用于批量任务时防止内存泄漏）
    const createTrackedTimeout = (tokenId, callback, delay) => {
        const timerId = setTimeout(() => {
            // 执行回调
            callback()
            // 执行后从追踪列表中移除
            if (wsTimers.value[tokenId]) {
                const index = wsTimers.value[tokenId].indexOf(timerId)
                if (index > -1) {
                    wsTimers.value[tokenId].splice(index, 1)
                }
            }
        }, delay)
        
        // 记录定时器ID
        if (!wsTimers.value[tokenId]) {
            wsTimers.value[tokenId] = []
        }
        wsTimers.value[tokenId].push(timerId)
        
        return timerId
    }

    // 游戏数据存储
    const gameData = ref({
        roleInfo: null,
        legionInfo: null,
        presetTeam: null,
        studyStatus: {
            isAnswering: false,
            questionCount: 0,
            answeredCount: 0,
            status: '', // '', 'starting', 'answering', 'claiming_rewards', 'completed'
            timestamp: null
        },
        lastUpdated: null
    })

    // 计算属性
    const hasTokens = computed(() => gameTokens.value.length > 0)
    const selectedToken = computed(() =>
        gameTokens.value.find(token => token.id === selectedTokenId.value)
    )

    // 获取当前选中token的角色信息
    const selectedTokenRoleInfo = computed(() => {
        return gameData.value.roleInfo
    })

    // Token管理
    const addToken = async (tokenData) => {
        // 🔧 修复：确保 gameTokens.value 是数组
        if (!Array.isArray(gameTokens.value)) {
            tokenLogger.warn('⚠️ gameTokens 不是数组，正在重置为空数组')
            gameTokens.value = []
        }

        const newToken = {
            id: 'token_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9),
            name: tokenData.name,
            token: tokenData.token, // 保存原始Base64 token
            wsUrl: tokenData.wsUrl || null, // 可选的自定义WebSocket URL
            server: tokenData.server || '',
            level: tokenData.level || 1,
            profession: tokenData.profession || '',
            createdAt: new Date().toISOString(),
            lastUsed: new Date().toISOString(),
            isActive: true,
            // URL获取相关信息
            sourceUrl: tokenData.sourceUrl || null, // Token来源URL（用于刷新）
            importMethod: tokenData.importMethod || 'manual', // 导入方式：manual 或 url 或 bin
            // bin文件相关信息
            binFileId: tokenData.binFileId || null, // 本地存储的bin文件ID
            binFileContent: tokenData.binFileContent || null, // bin文件内容（ArrayBuffer或Base64字符串）
            rawData: tokenData.rawData || null, // 原始token数据
            lastRefreshed: tokenData.lastRefreshed || null // 最后刷新时间
        }

        gameTokens.value.push(newToken)
        
        // ⚡ v3.15.1: 优化 - 单个添加使用putToken，不需要保存全部
        const tokenToSave = {
            id: newToken.id,
            name: newToken.name,
            token: newToken.token,
            wsUrl: newToken.wsUrl,
            server: newToken.server,
            level: newToken.level,
            profession: newToken.profession,
            createdAt: newToken.createdAt,
            lastUsed: newToken.lastUsed,
            isActive: newToken.isActive,
            sourceUrl: newToken.sourceUrl,
            importMethod: newToken.importMethod,
            binFileId: newToken.binFileId,
            lastRefreshed: newToken.lastRefreshed
        }
        await indexedDBStorage.putToken(tokenToSave)
        
        // 更新localStorage元数据
        try {
            localStorage.setItem('gameTokens_count', gameTokens.value.length.toString())
        } catch (e) {
            log.warn('⚠️ 无法更新token数量:', e.message)
        }

        return newToken
    }

    // 更新token
    const updateToken = async (tokenId, tokenData) => {
        try {
            tokenLogger.debug(`开始更新token，角色ID: ${tokenId}`)
            
            // 查找token在数组中的索引
            const tokenIndex = gameTokens.value.findIndex(t => t.id === tokenId)
            if (tokenIndex === -1) {
                tokenLogger.error(`错误: 未找到ID为${tokenId}的token`)
                return false
            }
            
            // 获取现有的token数据
            const existingData = gameTokens.value[tokenIndex]
            
            // 记录更新前的token数据
            tokenLogger.debug(`更新前的token数据:`, {
                tokenId,
                token: existingData.token ? existingData.token.substring(0, 20) + '...' : 'N/A',
                roleToken: existingData.roleToken ? existingData.roleToken.substring(0, 20) + '...' : 'N/A',
                lastRefreshed: existingData.lastRefreshed
            })
            
            // 记录要更新的token数据
            tokenLogger.debug(`要更新的token数据:`, {
                tokenId,
                token: tokenData.token ? tokenData.token.substring(0, 20) + '...' : 'N/A',
                roleToken: tokenData.roleToken ? tokenData.roleToken.substring(0, 20) + '...' : 'N/A',
                binFileId: tokenData.binFileId ? '已设置' : '未设置',
                binFileContent: tokenData.binFileContent ? (typeof tokenData.binFileContent === 'string' ? 'Base64字符串' : 'ArrayBuffer') : '未设置',
                lastRefreshed: tokenData.lastRefreshed
            })
            
            // 合并现有数据和新数据
            const updatedData = {
                ...existingData,
                ...tokenData,
                id: tokenId, // 确保ID正确
                updatedAt: Date.now() // 更新时间戳
            }
            
            tokenLogger.debug(`合并后的token数据:`, {
                tokenId,
                token: updatedData.token ? updatedData.token.substring(0, 20) + '...' : 'N/A',
                roleToken: updatedData.roleToken ? updatedData.roleToken.substring(0, 20) + '...' : 'N/A',
                binFileId: updatedData.binFileId ? '已设置' : '未设置',
                binFileContent: updatedData.binFileContent ? (typeof updatedData.binFileContent === 'string' ? 'Base64字符串' : 'ArrayBuffer') : '未设置',
                lastRefreshed: updatedData.lastRefreshed,
                updatedAt: updatedData.updatedAt
            })
            
            // 更新状态 - 使用数组索引直接更新
            gameTokens.value[tokenIndex] = updatedData
            
            // ⚡ v3.15.1: 优化 - 单个更新使用putToken，不需要保存全部
            const tokenToSave = {
                id: updatedData.id,
                name: updatedData.name,
                token: updatedData.token,
                wsUrl: updatedData.wsUrl,
                server: updatedData.server,
                level: updatedData.level,
                profession: updatedData.profession,
                createdAt: updatedData.createdAt,
                lastUsed: updatedData.lastUsed,
                isActive: updatedData.isActive,
                sourceUrl: updatedData.sourceUrl,
                importMethod: updatedData.importMethod,
                binFileId: updatedData.binFileId,
                lastRefreshed: updatedData.lastRefreshed
            }
            await indexedDBStorage.putToken(tokenToSave)
            
            tokenLogger.info(`Token更新成功`)
            return true
        } catch (error) {
            tokenLogger.error(`更新token失败:`, error)
            return false
        }
    }

    const removeToken = async (tokenId) => {
        // 在删除token前，先获取token信息以便清理bin文件存储
        const token = gameTokens.value.find(t => t.id === tokenId)
        
        // ⚡ 优化：并行执行删除操作
        const deleteOperations = []
        
        // 1. 从数组移除
        gameTokens.value = gameTokens.value.filter(token => token.id !== tokenId)
        
        // ⚡ v3.15.1: 优化 - 删除单个token用deleteToken，不需要保存全部
        deleteOperations.push(indexedDBStorage.deleteToken(tokenId))
        
        // 更新localStorage元数据
        try {
            localStorage.setItem('gameTokens_count', gameTokens.value.length.toString())
        } catch (e) {
            // 忽略错误
        }

        // 2. 关闭WebSocket连接（同步操作，不需要await）
        if (wsConnections.value[tokenId]) {
            closeWebSocketConnection(tokenId)
        }

        // 3. 清除选中状态（同步操作）
        if (selectedTokenId.value === tokenId) {
            selectedTokenId.value = null
            localStorage.removeItem('selectedTokenId')
        }

        // 4. 🆕 v3.15.0: 删除关联的bin文件（并行执行）
        if (token && token.binFileId) {
            // ⚡ IndexedDB删除（并行）
            deleteOperations.push(
                indexedDBStorage.deleteBinFile(token.binFileId)
                    .then(() => log.debug(`✅ 已从IndexedDB清理bin文件: ${token.binFileId}`))
                    .catch(e => log.debug(`⚠️ IndexedDB清理失败: ${e.message}`))
            )
            
            // ⚡ localStorage清理（异步执行，不阻塞）
            Promise.resolve().then(() => {
                try {
                    const index = JSON.parse(localStorage.getItem('storedBinFiles_index') || '{}')
                    const segmentNum = index[token.binFileId]
                    
                    if (segmentNum !== undefined) {
                        const segmentKey = `storedBinFiles_${segmentNum}`
                        const segment = JSON.parse(localStorage.getItem(segmentKey) || '{}')
                        delete segment[token.binFileId]
                        localStorage.setItem(segmentKey, JSON.stringify(segment))
                        
                        delete index[token.binFileId]
                        localStorage.setItem('storedBinFiles_index', JSON.stringify(index))
                        log.debug(`✅ 已从localStorage分段存储清理bin文件: ${token.binFileId}`)
                    } else {
                        // 尝试从旧方式存储中删除
                        const storedBinFiles = JSON.parse(localStorage.getItem('storedBinFiles') || '{}')
                        if (storedBinFiles[token.binFileId]) {
                            delete storedBinFiles[token.binFileId]
                            localStorage.setItem('storedBinFiles', JSON.stringify(storedBinFiles))
                            log.debug(`✅ 已从localStorage旧方式清理bin文件: ${token.binFileId}`)
                        }
                    }
                } catch (error) {
                    log.debug(`⚠️ 清理localStorage bin文件失败（不影响功能）:`, error)
                }
            })
        }

        // ⚡ 等待关键操作完成（token保存和bin文件IndexedDB删除）
        await Promise.all(deleteOperations)

        return true
    }

    const selectToken = async (tokenId) => {
        const token = gameTokens.value.find(t => t.id === tokenId)
        if (token) {
            // 🔧 保存旧的tokenId
            const oldTokenId = selectedTokenId.value

            // 🔧 如果旧Token存在且不同于新Token，先关闭旧Token的WebSocket连接
            if (oldTokenId && oldTokenId !== tokenId && wsConnections.value[oldTokenId]) {
                wsLogger.info(`🔌 切换Token: 断开旧连接 [${oldTokenId}]`)
                closeWebSocketConnection(oldTokenId)
            }

            // 🔧 清空游戏数据，避免显示旧Token的数据
            if (oldTokenId !== tokenId) {
                wsLogger.info(`🔄 切换Token: 清空旧数据`)
                gameData.value = {
                    roleInfo: null,
                    legionInfo: null,
                    presetTeam: null,
                    studyStatus: {
                        isAnswering: false,
                        questionCount: 0,
                        answeredCount: 0,
                        status: '',
                        timestamp: null
                    },
                    lastUpdated: null
                }
            }

            // 更新选中的tokenId
            selectedTokenId.value = tokenId
            localStorage.setItem('selectedTokenId', tokenId)

            // 更新最后使用时间
            updateToken(tokenId, {lastUsed: new Date().toISOString()})

            // 使用新的reconnectWebSocket函数，确保每次都从bin文件重新获取token
            wsLogger.info(`🔌 切换Token: 连接新Token [${tokenId}]`)
            const wsClient = await reconnectWebSocket(tokenId)

            if (!wsClient) {
                wsLogger.error(`创建WebSocket连接失败 [${tokenId}]`)
                return null
            }

            wsLogger.info(`✅ Token切换完成: [${oldTokenId || '无'}] → [${tokenId}]`)
            return token
        }
        return null
    }

    // 辅助函数：分析数据结构
    const analyzeDataStructure = (obj, depth = 0, maxDepth = 3) => {
        if (depth > maxDepth || !obj || typeof obj !== 'object') {
            return typeof obj
        }

        const structure = {}
        for (const [key, value] of Object.entries(obj)) {
            if (Array.isArray(value)) {
                structure[key] = `Array[${value.length}]${value.length > 0 ? `: ${analyzeDataStructure(value[0], depth + 1, maxDepth)}` : ''}`
            } else if (typeof value === 'object' && value !== null) {
                structure[key] = analyzeDataStructure(value, depth + 1, maxDepth)
            } else {
                structure[key] = typeof value
            }
        }
        return structure
    }

    // 辅助函数：尝试解析队伍数据
    const tryParseTeamData = (data, cmd) => {
        // 静默解析，不打印详细日志

        // 查找队伍相关字段
        const teamFields = []
        const scanForTeamData = (obj, path = '') => {
            if (!obj || typeof obj !== 'object') return

            for (const [key, value] of Object.entries(obj)) {
                const currentPath = path ? `${path}.${key}` : key

                if (key.toLowerCase().includes('team') ||
                    key.toLowerCase().includes('preset') ||
                    key.toLowerCase().includes('formation') ||
                    key.toLowerCase().includes('lineup')) {
                    teamFields.push({
                        path: currentPath,
                        key: key,
                        value: value,
                        type: typeof value,
                        isArray: Array.isArray(value)
                    })
                }

                if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
                    scanForTeamData(value, currentPath)
                }
            }
        }

        scanForTeamData(data)

        if (teamFields.length > 0) {
            log.debug(`👥 找到 ${teamFields.length} 个队伍相关字段:`, teamFields)

            // 尝试更新游戏数据
            teamFields.forEach(field => {
                if (field.key === 'presetTeamInfo' || field.path.includes('presetTeamInfo')) {
                    log.debug(`👥 发现预设队伍信息，准备更新:`, field.value)
                    if (!gameData.value.presetTeam) {
                        gameData.value.presetTeam = {}
                    }
                    gameData.value.presetTeam.presetTeamInfo = field.value
                    gameData.value.lastUpdated = new Date().toISOString()
                }
            })
        } else {
            // 未找到队伍数据
        }
    }

    // 处理学习答题响应的核心函数
    const handleStudyResponse = async (tokenId, body) => {
        try {
            log.debug('📚 开始处理学习答题响应:', body)

            const connection = wsConnections.value[tokenId]
            if (!connection || connection.status !== 'connected' || !connection.client) {
                log.error('❌ WebSocket连接不可用，无法进行答题')
                return
            }

            // 获取题目列表和学习ID
            const questionList = body.questionList
            const studyId = body.role?.study?.id

            if (!questionList || !Array.isArray(questionList)) {
                log.error('❌ 未找到题目列表')
                return
            }

            if (!studyId) {
                log.error('❌ 未找到学习ID')
                return
            }

            log.debug(`📝 找到 ${questionList.length} 道题目，学习ID: ${studyId}`)

            // 更新答题状态
            gameData.value.studyStatus = {
                isAnswering: true,
                questionCount: questionList.length,
                answeredCount: 0,
                status: 'answering',
                timestamp: Date.now()
            }

            // 遍历题目并回答
            for (let i = 0; i < questionList.length; i++) {
                const question = questionList[i]
                const questionText = question.question
                const questionId = question.id

                log.debug(`📖 题目 ${i + 1}: ${questionText}`)

                // 查找答案（异步）
                let answer = await findAnswer(questionText)

                if (answer === null) {
                    // 如果没有找到答案，默认选择选项1
                    answer = 1
                    log.debug(`⚠️ 未找到匹配答案，使用默认答案: ${answer}`)
                } else {
                    log.debug(`✅ 找到答案: ${answer}`)
                }

                // 发送答案
                try {
                    connection.client.send('study_answer', {
                        id: studyId,
                        option: [answer],
                        questionId: [questionId]
                    })
                    log.debug(`📤 已提交题目 ${i + 1} 的答案: ${answer}`)
                } catch (error) {
                    log.error(`❌ 提交答案失败 (题目 ${i + 1}):`, error)
                }

                // 更新已回答题目数量
                gameData.value.studyStatus.answeredCount = i + 1

                // 添加短暂延迟，避免请求过快
                if (i < questionList.length - 1) {
                    await new Promise(resolve => setTimeout(resolve, 200))
                }
            }

            // 等待一下让所有答案提交完成，然后领取奖励
            setTimeout(() => {
                log.debug('🎁 开始领取答题奖励...')

                // 更新状态为正在领取奖励
                gameData.value.studyStatus.status = 'claiming_rewards'

                // 领取所有等级的奖励 (1-10)
                const rewardPromises = []
                for (let rewardId = 1; rewardId <= 10; rewardId++) {
                    try {
                        const promise = connection.client.send('study_claimreward', {
                            rewardId: rewardId
                        })
                        rewardPromises.push(promise)
                        log.debug(`🎯 已发送奖励领取请求: rewardId=${rewardId}`)
                    } catch (error) {
                        log.error(`❌ 发送奖励领取请求失败 (rewardId=${rewardId}):`, error)
                    }
                }

                log.debug('🎊 一键答题完成！已尝试领取所有奖励')

                // 更新状态为完成
                gameData.value.studyStatus.status = 'completed'

                // 3秒后重置状态
                setTimeout(() => {
                    gameData.value.studyStatus = {
                        isAnswering: false,
                        questionCount: 0,
                        answeredCount: 0,
                        status: '',
                        timestamp: null
                    }
                }, 3000)

                // 更新游戏数据
                setTimeout(() => {
                    try {
                        connection.client.send('role_getroleinfo', {})
                        log.debug('📊 已请求更新角色信息')
                    } catch (error) {
                        log.error('❌ 请求角色信息更新失败:', error)
                    }
                }, 1000)

            }, 500) // 延迟500ms后领取奖励

        } catch (error) {
            log.error('❌ 处理学习答题响应失败:', error)
        }
    }

    // 判断当前时间是否在本周内
    function isInCurrentWeek(timestamp, weekStart = 1) {
        // timestamp 单位：毫秒。如果是秒，先 *1000
        const now = new Date();
        const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

        // 当前星期几 (0=周日,1=周一,...6=周六)
        const currentWeekday = today.getDay();
        // 算出本周起始
        let diff = currentWeekday - weekStart;
        if (diff < 0) diff += 7;

        const startOfWeek = new Date(today);
        startOfWeek.setDate(today.getDate() - diff);

        const endOfWeek = new Date(startOfWeek);
        endOfWeek.setDate(startOfWeek.getDate() + 7);

        const target = new Date(timestamp);
        return target >= startOfWeek && target < endOfWeek;
    }

    // 游戏消息处理
    const handleGameMessage = (tokenId, message) => {
        try {
            if (!message || message.error) {
                log.warn(`⚠️ 消息处理跳过 [${tokenId}]:`, message?.error || '无效消息')
                return
            }

            // 尝试从多个位置获取 cmd
            let cmd = message.cmd || message._raw?.cmd || message.rawData?.cmd
            
            // 如果没有cmd，跳过处理
            if (!cmd) {
                log.warn(`⚠️ 无法找到cmd [${tokenId}]`)
                return
            }
            
            cmd = cmd.toLowerCase()
            
            // 优先使用rawData（ProtoMsg自动解码），然后decodedBody（手动解码），最后body（原始数据）
            const body = message.rawData !== undefined ? message.rawData :
                message.decodedBody !== undefined ? message.decodedBody :
                    message.body

            // 简化消息处理日志（移除详细结构信息）
            if (cmd !== '_sys/ack') { // 过滤心跳消息
                log.debug(`📋 处理 [${tokenId}] ${cmd}`, body ? '✓' : '✗')
            }

            // 过滤塔相关消息的详细打印

            // 处理角色信息 - 支持多种可能的响应命令
            if (cmd === 'role_getroleinfo' || cmd === 'role_getroleinforesp' || cmd.includes('role') && cmd.includes('info')) {
                log.debug(`📊 角色信息 [${tokenId}]`)

                if (body) {
                    gameData.value.roleInfo = body
                    gameData.value.lastUpdated = new Date().toISOString()
                    log.debug('📊 角色信息已更新')

                    // 检查答题完成状态
                    if (body.role?.study?.maxCorrectNum !== undefined) {
                        const maxCorrectNum = body.role.study.maxCorrectNum
                        const beginTime = body.role.study.beginTime
                        const isStudyCompleted = maxCorrectNum >= 10 && isInCurrentWeek(beginTime*1000)

                        // 更新答题完成状态
                        if (!gameData.value.studyStatus) {
                            gameData.value.studyStatus = {}
                        }
                        gameData.value.studyStatus.isCompleted = isStudyCompleted
                        gameData.value.studyStatus.maxCorrectNum = maxCorrectNum

                        log.debug(`📚 答题状态更新: maxCorrectNum=${maxCorrectNum}, 完成状态=${isStudyCompleted}`)
                    }

                    // 检查塔信息
                    if (body.role?.tower) {
                        // 塔信息已更新
                    }
                } else {
                    log.debug('📊 角色信息响应为空')
                }
            }

            // 处理军团信息 - 支持 legion_getinfo 和 legion_getinforesp
            else if (cmd === 'legion_getinfo' || cmd === 'legion_getinforesp') {
                if (body) {
                    gameData.value.legionInfo = body
                    log.debug('🏛️ 军团信息已更新:', {
                        hasInfo: !!body.info,
                        clubName: body.info?.name,
                        memberCount: body.info?.members ? Object.keys(body.info.members).length : 0
                    })
                    console.log('🏛️ [俱乐部] 军团信息已更新:', body)
                }
            }

            // 处理队伍信息 - 支持多种队伍相关响应
            else if (cmd === 'presetteam_getinfo' || cmd === 'presetteam_getinforesp' ||
                cmd === 'presetteam_setteam' || cmd === 'presetteam_setteamresp' ||
                cmd === 'presetteam_saveteam' || cmd === 'presetteam_saveteamresp' ||
                cmd === 'role_gettargetteam' || cmd === 'role_gettargetteamresp' ||
                (cmd && cmd.includes('presetteam')) || (cmd && cmd.includes('team'))) {
                log.debug(`👥 队伍信息 [${tokenId}] ${cmd}`)

                if (body) {
                    // 更新队伍数据
                    if (!gameData.value.presetTeam) {
                        gameData.value.presetTeam = {}
                    }

                    // 根据不同的响应类型处理数据
                    if (cmd.includes('getteam')) {
                        // 获取队伍信息响应
                        gameData.value.presetTeam = {...gameData.value.presetTeam, ...body}
                    } else if (cmd.includes('setteam') || cmd.includes('saveteam')) {
                        // 设置/保存队伍响应 - 可能只返回确认信息
                        if (body.presetTeamInfo) {
                            gameData.value.presetTeam.presetTeamInfo = body.presetTeamInfo
                        }
                        // 合并其他队伍相关数据
                        Object.keys(body).forEach(key => {
                            if (key.includes('team') || key.includes('Team')) {
                                gameData.value.presetTeam[key] = body[key]
                            }
                        })
                    } else {
                        // 其他队伍相关响应
                        gameData.value.presetTeam = {...gameData.value.presetTeam, ...body}
                    }

                    gameData.value.lastUpdated = new Date().toISOString()
                    log.debug('👥 队伍信息已更新')

                    // 简化队伍数据结构日志
                    if (gameData.value.presetTeam.presetTeamInfo) {
                        const teamCount = Object.keys(gameData.value.presetTeam.presetTeamInfo).length
                        log.debug(`👥 队伍数量: ${teamCount}`)
                    }
                } else {
                    log.debug('👥 队伍信息响应为空')
                }
            }

            // 处理爬塔响应（静默处理，保持功能）
            else if (cmd === 'fight_starttower' || cmd === 'fight_starttowerresp') {
                if (body) {
                    // 判断爬塔结果
                    const battleData = body.battleData
                    if (battleData) {
                        const curHP = battleData.result?.sponsor?.ext?.curHP
                        const isSuccess = curHP > 0

                        // 保存爬塔结果到gameData中，供组件使用
                        if (!gameData.value.towerResult) {
                            gameData.value.towerResult = {}
                        }
                        gameData.value.towerResult = {
                            success: isSuccess,
                            curHP: curHP,
                            towerId: battleData.options?.towerId,
                            timestamp: Date.now()
                        }
                        gameData.value.lastUpdated = new Date().toISOString()

                        if (isSuccess) {
                            // 检查是否需要自动领取奖励
                            const towerId = battleData.options?.towerId
                            if (towerId !== undefined) {
                                const layer = towerId % 10
                                const floor = Math.floor(towerId / 10)

                                // 如果是新层数的第一层(layer=0)，检查是否有奖励可领取
                                if (layer === 0) {
                                    setTimeout(() => {
                                        const connection = wsConnections.value[tokenId]
                                        if (connection && connection.status === 'connected' && connection.client) {
                                            // 检查角色信息中的奖励状态
                                            const roleInfo = gameData.value.roleInfo
                                            const towerRewards = roleInfo?.role?.tower?.reward

                                            if (towerRewards && !towerRewards[floor]) {
                                                // 保存奖励信息
                                                gameData.value.towerResult.autoReward = true
                                                gameData.value.towerResult.rewardFloor = floor
                                                connection.client.send('tower_claimreward', {rewardId: floor})
                                            }
                                        }
                                    }, 1500)
                                }
                            }
                        }
                    }

                    // 爬塔后立即更新角色信息和塔信息
                    setTimeout(() => {
                        try {
                            const connection = wsConnections.value[tokenId]
                            if (connection && connection.status === 'connected' && connection.client) {
                                connection.client.send('role_getroleinfo', {})
                            }
                        } catch (error) {
                            // 忽略更新数据错误
                        }
                    }, 0)
                }
            }

            // 处理奖励领取响应（静默处理）
            else if (cmd === 'tower_claimreward' || cmd === 'tower_claimrewardresp') {
                if (body) {
                    // 奖励领取成功后更新角色信息
                    setTimeout(() => {
                        const connection = wsConnections.value[tokenId]
                        if (connection && connection.status === 'connected' && connection.client) {
                            connection.client.send('role_getroleinfo', {})
                        }
                    }, 500)
                }
            }

            // 处理学习答题响应 - 一键答题功能
            else if (cmd === 'studyresp' || cmd === 'study_startgame' || cmd === 'study_startgameresp') {
                if (body) {
                    log.debug(`📚 学习答题响应 [${tokenId}]`, body)
                    handleStudyResponse(tokenId, body)
                }
            }

            // 处理加钟相关响应
            else if (cmd === 'system_mysharecallback' || cmd === 'syncresp' || cmd === 'system_claimhangupreward' || cmd === 'system_claimhanguprewardresp') {
                log.debug(`🕐 加钟/挂机 [${tokenId}] ${cmd}`)

                // 加钟操作完成后，延迟更新角色信息
                if (cmd === 'syncresp' || cmd === 'system_mysharecallback') {
                    setTimeout(() => {
                        const connection = wsConnections.value[tokenId]
                        if (connection && connection.status === 'connected' && connection.client) {
                            connection.client.send('role_getroleinfo', {})
                        }
                    }, 800)
                }

                // 挂机奖励领取完成后更新角色信息
                if (cmd === 'system_claimhanguprewardresp') {
                    setTimeout(() => {
                        const connection = wsConnections.value[tokenId]
                        if (connection && connection.status === 'connected' && connection.client) {
                            connection.client.send('role_getroleinfo', {})
                        }
                    }, 500)
                }
            }

            // 处理心跳响应（静默处理，不打印日志）
            else if (cmd === '_sys/ack') {
                // 心跳响应 - 静默处理
                return
            }

            // 处理其他消息
            else {
                log.debug(`📋 游戏消息 [${tokenId}] ${cmd}`)

                // 特别关注队伍相关的未处理消息
                if (cmd && (cmd.includes('team') || cmd.includes('preset') || cmd.includes('formation'))) {
                    log.debug(`👥 未处理队伍消息 [${tokenId}] ${cmd}`)

                    // 尝试自动解析队伍数据
                    if (body && typeof body === 'object') {
                        tryParseTeamData(body, cmd)
                    }
                }

                // 特别关注塔相关的未处理消息（静默处理）
                if (cmd && cmd.includes('tower')) {
                    // 未处理塔消息
                }
            }

        } catch (error) {
            log.error(`处理消息失败 [${tokenId}]:`, error.message)
        }
    }

    // 验证token有效性
    const validateToken = (token) => {
        if (!token) return false
        if (typeof token !== 'string') return false
        if (token.trim().length === 0) return false
        // 简单检查：token应该至少有一定长度
        if (token.trim().length < 10) return false
        return true
    }

    // Base64解析功能（增强版）
    const parseBase64Token = (base64String) => {
        try {
            // 输入验证
            if (!base64String || typeof base64String !== 'string') {
                throw new Error('Token字符串无效')
            }

            // 移除可能的前缀和空格
            const cleanBase64 = base64String.replace(/^data:.*base64,/, '').trim()

            if (cleanBase64.length === 0) {
                throw new Error('Token字符串为空')
            }

            // 解码base64
            let decoded
            try {
                decoded = atob(cleanBase64)
            } catch (decodeError) {
                // 如果不是有效的Base64，作为纯文本token处理
                decoded = base64String.trim()
            }

            // 尝试解析为JSON
            let tokenData
            try {
                tokenData = JSON.parse(decoded)
            } catch {
                // 不是JSON格式，作为纯token处理
                tokenData = {token: decoded}
            }

            // 提取实际token
            const actualToken = tokenData.token || tokenData.gameToken || decoded

            // 验证token有效性
            if (!validateToken(actualToken)) {
                throw new Error(`提取的token无效: "${actualToken}"`)
            }

            return {
                success: true,
                data: {
                    ...tokenData,
                    actualToken // 添加提取出的实际token
                }
            }
        } catch (error) {
            return {
                success: false,
                error: '解析失败：' + error.message
            }
        }
    }

    const importBase64Token = (name, base64String, additionalInfo = {}) => {
    const parseResult = parseBase64Token(base64String)

    if (!parseResult.success) {
        return {
            success: false,
            error: parseResult.error,
            message: `Token "${name}" 导入失败: ${parseResult.error}`
        }
    }

    const tokenData = {
        name,
        token: parseResult.data.actualToken, // 使用验证过的实际token
        ...additionalInfo,
        ...parseResult.data, // 解析出的数据覆盖手动输入
        importMethod: additionalInfo.importMethod || 'manual', // 导入方式：manual 或 bin
        binFileContent: additionalInfo.binFileContent || null, // 保存bin文件内容
        binFileId: additionalInfo.binFileId || null, // 保存bin文件ID
        rawData: additionalInfo.rawData || null, // 保存原始token数据
        lastRefreshed: additionalInfo.lastRefreshed || null // 保存最后刷新时间
    }

    try {
        const newToken = addToken(tokenData)

        // 添加更多验证信息到成功消息
        const tokenInfo = parseResult.data.actualToken
        const displayToken = tokenInfo.length > 20 ?
            `${tokenInfo.substring(0, 10)}...${tokenInfo.substring(tokenInfo.length - 6)}` :
            tokenInfo

        return {
            success: true,
            data: newToken,
            message: `Token "${name}" 导入成功`,
            details: `实际Token: ${displayToken}`
        }
    } catch (error) {
        return {
            success: false,
            error: error.message,
            message: `Token "${name}" 添加失败: ${error.message}`
        }
    }
}

// 保存bin文件内容到token
const saveBinFileToToken = async (tokenId, binFileContent) => {
    const token = gameTokens.value.find(t => t.id === tokenId)
    if (token) {
        token.binFileContent = binFileContent
        token.updatedAt = new Date().toISOString()
        await saveTokensToStorage()
        return true
    }
    return false
}

    // 从本地存储的bin文件获取新的roleToken
    const getRoleTokenFromStoredBin = async (tokenId) => {
        try {
            log.debug(`[TokenStore] 开始从本地存储的bin文件获取新的roleToken，Token ID: ${tokenId}`);
            
            // 查找对应的token对象
            const tokenObj = gameTokens.value.find(t => t.id === tokenId);
            if (!tokenObj || !tokenObj.binFileId) {
                log.error(`[TokenStore] 错误: 找不到Token或binFileId，Token ID: ${tokenId}`);
                return null;
            }
            
            // 🆕 v3.15.0: 优先从IndexedDB读取bin文件
            let binFileData = null
            
            try {
                binFileData = await indexedDBStorage.getBinFile(tokenObj.binFileId)
                if (binFileData) {
                    log.debug(`[TokenStore] 从IndexedDB读取到bin文件: ${tokenObj.binFileId}`)
                }
            } catch (e) {
                log.debug(`[TokenStore] IndexedDB读取失败，尝试localStorage: ${e.message}`)
            }
            
            // 🔥 v3.14.1: 如果IndexedDB没有，尝试从localStorage分段存储读取（向后兼容）
            if (!binFileData) {
                try {
                    const index = JSON.parse(localStorage.getItem('storedBinFiles_index') || '{}')
                    if (index[tokenObj.binFileId] !== undefined) {
                        const segmentNum = index[tokenObj.binFileId]
                        const segmentKey = `storedBinFiles_${segmentNum}`
                        const segment = JSON.parse(localStorage.getItem(segmentKey) || '{}')
                        binFileData = segment[tokenObj.binFileId]
                        log.debug(`[TokenStore] 从localStorage分段存储读取到bin文件: ${tokenObj.binFileId}`)
                    }
                } catch (e) {
                    log.debug(`[TokenStore] localStorage分段存储读取失败: ${e.message}`)
                }
            }
            
            // 如果分段存储也没有，尝试旧方式（向后兼容）
            if (!binFileData) {
                try {
                    const storedBinFiles = JSON.parse(localStorage.getItem('storedBinFiles') || '{}')
                    binFileData = storedBinFiles[tokenObj.binFileId]
                    if (binFileData) {
                        log.debug(`[TokenStore] 从localStorage旧方式读取到bin文件: ${tokenObj.binFileId}`)
                    }
                } catch (e) {
                    log.debug(`[TokenStore] localStorage旧方式读取失败: ${e.message}`)
                }
            }
            
            if (!binFileData || !binFileData.content) {
                // 🔥 v3.14.1: 降低日志级别，避免大量错误输出消耗CPU
                // log.error 改为 log.debug，只在需要调试时输出
                log.debug(`[TokenStore] 未找到bin文件数据（将使用原有token），binFileId: ${tokenObj.binFileId}`)
                return null
            }
            
            log.debug(`[TokenStore] 找到存储的bin文件:`, {
                id: binFileData.id,
                name: binFileData.name,
                roleName: binFileData.roleName,
                createdAt: binFileData.createdAt
            });
            
            // 将Base64内容转换回ArrayBuffer
            const binaryString = atob(binFileData.content);
            const bytes = new Uint8Array(binaryString.length);
            for (let i = 0; i < binaryString.length; i++) {
                bytes[i] = binaryString.charCodeAt(i);
            }
            const arrayBuffer = bytes.buffer;
            
            log.debug(`[TokenStore] 成功将Base64转换为ArrayBuffer，字节长度: ${arrayBuffer.byteLength}`);
            
            // 向咸鱼之王登录接口发送POST请求
            const loginUrl = 'https://xxz-xyzw.hortorgames.com/login/authuser?_seq=1';
            log.debug(`[TokenStore] 发送登录请求到: ${loginUrl}`);
            
            const response = await fetch(loginUrl, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/octet-stream'
              },
              body: arrayBuffer,
            });
            
            log.debug(`[TokenStore] 响应状态:`, response.status);
            
            if (!response.ok) {
              const errorText = await response.text();
              log.error(`[TokenStore] 登录请求失败，状态: ${response.status}, 响应:`, errorText);
              throw new Error(`登录请求失败: ${response.status} ${response.statusText}`);
            }
            
            // 获取服务器响应的二进制数据
            const responseArrayBuffer = await response.arrayBuffer();
            log.debug(`[TokenStore] 服务器响应字节长度: ${responseArrayBuffer.byteLength}`);
            
            // 从服务器响应中提取Token
            const roleToken = extractRoleTokenFromBin(responseArrayBuffer);
            
            if (!roleToken) {
                log.error(`[TokenStore] 错误: 无法从服务器响应中提取Token`);
                throw new Error('无法从服务器响应中提取Token');
            }
            
            // 🆕 v3.15.0: 更新bin文件的最后使用时间到IndexedDB
            try {
                binFileData.lastUsed = new Date().toISOString();
                
                // 尝试更新IndexedDB中的数据
                try {
                    await indexedDBStorage.saveBinFile(binFileData);
                    log.debug(`[TokenStore] 更新bin文件最后使用时间到IndexedDB成功`);
                } catch (e) {
                    log.debug(`[TokenStore] IndexedDB更新失败，尝试更新localStorage: ${e.message}`);
                    
                    // 回退：尝试更新localStorage分段存储中的数据
                    const index = JSON.parse(localStorage.getItem('storedBinFiles_index') || '{}');
                    if (index[tokenObj.binFileId] !== undefined) {
                        const segmentNum = index[tokenObj.binFileId];
                        const segmentKey = `storedBinFiles_${segmentNum}`;
                        const segment = JSON.parse(localStorage.getItem(segmentKey) || '{}');
                        if (segment[tokenObj.binFileId]) {
                            segment[tokenObj.binFileId].lastUsed = binFileData.lastUsed;
                            localStorage.setItem(segmentKey, JSON.stringify(segment));
                        }
                    } else {
                        // 如果不在分段存储中，尝试更新旧方式存储（向后兼容）
                        const storedBinFiles = JSON.parse(localStorage.getItem('storedBinFiles') || '{}');
                        if (storedBinFiles[tokenObj.binFileId]) {
                            storedBinFiles[tokenObj.binFileId].lastUsed = binFileData.lastUsed;
                            localStorage.setItem('storedBinFiles', JSON.stringify(storedBinFiles));
                        }
                    }
                }
            } catch (updateError) {
                log.debug(`[TokenStore] 更新bin文件最后使用时间失败（不影响功能）:`, updateError);
            }
            
            log.debug(`[TokenStore] ✅ 成功从本地存储的bin文件获取新的roleToken: ${roleToken.substring(0, 20)}...`);
            log.debug(`[TokenStore] 🔍 调试 - 完整roleToken数据:`, roleToken);
            log.debug(`[TokenStore] 🔍 调试 - roleToken长度: ${roleToken.length}`);
            return roleToken;
        } catch (error) {
            log.error(`[TokenStore] ❌ 从本地存储的bin文件获取roleToken失败:`, error);
            return null;
        }
    }
    
    // 从二进制数据中提取Token（与TokenImport.vue中的extractRoleToken函数相同）
    const extractRoleTokenFromBin = (arrayBuffer) => {
        try {
            // 将ArrayBuffer转换为Uint8Array以便处理
            const bytes = new Uint8Array(arrayBuffer);

            // 转换为ASCII字符串以便搜索
            let asciiString = '';
            for (let i = 0; i < bytes.length; i++) {
                // 只转换可打印的ASCII字符（32-126）
                if (bytes[i] >= 32 && bytes[i] <= 126) {
                    asciiString += String.fromCharCode(bytes[i]);
                } else {
                    asciiString += '.'; // 用点号表示不可打印字符
                }
            }

            // 添加调试信息
            log.debug('[TokenStore] 转换后的ASCII字符串前200个字符:', asciiString.substring(0, 200));
            
            // 搜索Token的位置 - 只查找 "Token" 字符串（与原始工具保持一致）
            const tokenIndex = asciiString.indexOf('Token');

            if (tokenIndex !== -1) {
                log.debug(`[TokenStore] 找到Token标记在位置 ${tokenIndex}`);
                
                // 找到Token标记，提取Token值
                let tokenStart = tokenIndex + 5; // "Token"长度为5

                // 跳过可能的非Base64字符，直到找到Base64字符
                while (tokenStart < asciiString.length) {
                    const char = asciiString[tokenStart];
                    if (isBase64Char(char)) {
                        break;
                    }
                    tokenStart++;
                }

                // 提取Base64 Token
                let tokenEnd = tokenStart;
                while (tokenEnd < asciiString.length && isBase64Char(asciiString[tokenEnd])) {
                    tokenEnd++;
                }

                const tokenValue = asciiString.substring(tokenStart, tokenEnd);
                log.debug(`[TokenStore] 提取的Token值: ${tokenValue.substring(0, 50)}...`);
                log.debug(`[TokenStore] Token长度: ${tokenValue.length}`);

                if (tokenValue.length > 0) {
                    return tokenValue;
                } else {
                    log.error('找到Token标记但未找到Token值');
                    return null;
                }
            } else {
                log.error('在响应中未找到Token标记');
                return null;
            }
        } catch (error) {
            log.error('提取Token时发生错误:', error);
            return null;
        }
    }
    
    // 检查字符是否为Base64字符
    const isBase64Char = (char) => {
        // Base64字符集: A-Z, a-z, 0-9, +, /, =
        return /[A-Za-z0-9+/=]/.test(char);
    }

    // 从bin文件中获取新的roleToken
    const getRoleTokenFromBin = async (tokenId, binFileContent) => {
        try {
            log.debug(`[TokenStore] 开始从bin文件获取新的roleToken，Token ID: ${tokenId}`)
            
            if (!binFileContent) {
                log.error(`[TokenStore] 错误: bin文件内容为空，无法获取新的roleToken`)
                return null
            }
            
            let binArrayBuffer
            
            // 检查binFileContent是否为ArrayBuffer
            if (binFileContent instanceof ArrayBuffer) {
                // 如果是ArrayBuffer，直接使用
                binArrayBuffer = binFileContent
                log.debug(`[TokenStore] 使用ArrayBuffer格式的bin文件内容，大小: ${binArrayBuffer.byteLength} 字节`)
            } else {
                // 如果是base64字符串，转换为ArrayBuffer
                try {
                    const binaryString = atob(binFileContent)
                    const bytes = new Uint8Array(binaryString.length)
                    for (let i = 0; i < binaryString.length; i++) {
                        bytes[i] = binaryString.charCodeAt(i)
                    }
                    binArrayBuffer = bytes.buffer
                    log.debug(`[TokenStore] 将base64字符串转换为ArrayBuffer，大小: ${binArrayBuffer.byteLength} 字节`)
                } catch (error) {
                    log.error('转换binFileContent失败:', error)
                    throw new Error('bin文件内容格式错误')
                }
            }
            
            // 向咸鱼之王登录接口发送POST请求
            const loginUrl = 'https://xxz-xyzw.hortorgames.com/login/authuser?_seq=1';
            log.debug(`[TokenStore] 发送登录请求到: ${loginUrl}`);
            
            const response = await fetch(loginUrl, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/octet-stream',
              },
              body: binArrayBuffer,
            });
            
            log.debug(`[TokenStore] 响应状态:`, response.status)
            
            if (!response.ok) {
              const errorText = await response.text();
              log.error(`[TokenStore] 登录请求失败，状态: ${response.status}, 响应:`, errorText);
              throw new Error(`登录请求失败: ${response.status} ${response.statusText}`);
            }
            
            // 获取服务器响应的二进制数据
            const responseArrayBuffer = await response.arrayBuffer();
            log.debug(`[TokenStore] 服务器响应字节长度: ${responseArrayBuffer.byteLength}`);
            
            // 从服务器响应中提取Token
            const roleToken = extractRoleTokenFromBin(responseArrayBuffer);
            
            if (!roleToken) {
              throw new Error('无法从服务器响应中提取Token');
            }
            
            log.debug(`[TokenStore] ✅ 成功从bin文件获取新的roleToken: ${roleToken.substring(0, 20)}...`);
            log.debug(`[TokenStore] 🔍 调试 - 完整roleToken数据:`, roleToken);
            log.debug(`[TokenStore] 🔍 调试 - roleToken长度: ${roleToken.length}`);
            return roleToken;
        } catch (error) {
            log.error(`[TokenStore] ❌ 从bin文件获取roleToken失败:`, error)
            return null
        }
    }

    // WebSocket连接管理
    const createWebSocketConnection = async (tokenId, base64Token, customWsUrl = null) => {
        if (wsConnections.value[tokenId]) {
            closeWebSocketConnection(tokenId)
        }

        try {
            // 查找对应的token对象
            const tokenObj = gameTokens.value.find(t => t.id === tokenId)
            let actualToken = base64Token
            let finalWsUrl = customWsUrl
            let updatedTokenData = null

            // 检查是否有本地存储的bin文件，如果有则优先使用它获取新的roleToken
            log.debug(`[TokenStore] 🔍 调试 - 检查bin文件情况:`, {
                hasTokenObj: !!tokenObj,
                hasBinFileId: !!(tokenObj && tokenObj.binFileId),
                hasBinFileContent: !!(tokenObj && tokenObj.binFileContent),
                binFileId: tokenObj?.binFileId,
                hasBinFileContentLength: tokenObj?.binFileContent ? (tokenObj.binFileContent instanceof ArrayBuffer ? tokenObj.binFileContent.byteLength : tokenObj.binFileContent.length) : 0
            });
            
            if (tokenObj && tokenObj.binFileId && !updatedTokenData) {
                log.debug(`🔄 检测到本地存储的bin文件，尝试获取新的roleToken`);
                // 异步获取新的roleToken
                const roleToken = await getRoleTokenFromStoredBin(tokenId);
                log.debug(`[TokenStore] 🔍 调试 - 从本地存储bin文件获取roleToken结果:`, {
                    success: !!roleToken,
                    roleTokenLength: roleToken ? roleToken.length : 0
                });
                if (roleToken) {
                    // 生成完整的Token数据和WSS连接参数
                    const sessId = Date.now() * 1000 + Math.floor(Math.random() * 1000);
                    const connId = Date.now();
                    updatedTokenData = {
                      roleToken,
                      sessId,
                      connId,
                      isRestore: 0,
                    };
                    
                    log.debug(`[Bin连接] 生成的Token数据:`, updatedTokenData);
                    
                    // 创建完整的Token格式
                    const tokenStr = JSON.stringify(updatedTokenData);
                    actualToken = btoa(unescape(encodeURIComponent(tokenStr)));
                    log.debug(`[Bin连接] Base64编码的Token: ${actualToken.substring(0, 20)}...`);
                    
                    // 生成最终的WSS链接
                    finalWsUrl = finalWsUrl || `wss://xxz-xyzw.hortorgames.com/agent?p=${encodeURIComponent(tokenStr)}&e=x&lang=chinese`;
                    
                    // 更新token对象中的实际token
                    updateToken(tokenId, {
                      token: actualToken,
                      rawData: updatedTokenData,
                      wsUrl: finalWsUrl,
                      binFileId: tokenObj.binFileId,
                      lastRefreshed: Date.now()
                    });
                }
            }
            // 如果没有本地存储的bin文件，检查是否有内存中的bin文件内容
            else if (tokenObj && tokenObj.binFileContent && !updatedTokenData) {
                log.debug(`🔄 检测到内存中的bin文件，尝试获取新的roleToken`);
                // 异步获取新的roleToken
                const roleToken = await getRoleTokenFromBin(tokenId, tokenObj.binFileContent);
                log.debug(`[TokenStore] 🔍 调试 - 从内存中bin文件获取roleToken结果:`, {
                    success: !!roleToken,
                    roleTokenLength: roleToken ? roleToken.length : 0
                });
                if (roleToken) {
                    // 生成完整的Token数据和WSS连接参数
                    const sessId = Date.now() * 1000 + Math.floor(Math.random() * 1000);
                    const connId = Date.now();
                    updatedTokenData = {
                      roleToken,
                      sessId,
                      connId,
                      isRestore: 0,
                    };
                    
                    log.debug(`[Bin连接] 生成的Token数据:`, updatedTokenData);
                    
                    // 创建完整的Token格式
                    const tokenStr = JSON.stringify(updatedTokenData);
                    actualToken = btoa(unescape(encodeURIComponent(tokenStr)));
                    log.debug(`[Bin连接] Base64编码的Token: ${actualToken.substring(0, 20)}...`);
                    
                    // 生成最终的WSS链接
                    finalWsUrl = finalWsUrl || `wss://xxz-xyzw.hortorgames.com/agent?p=${encodeURIComponent(tokenStr)}&e=x&lang=chinese`;
                    
                    // 更新token对象中的实际token
                    updateToken(tokenId, {
                      token: actualToken,
                      rawData: updatedTokenData,
                      wsUrl: finalWsUrl,
                      binFileId: tokenObj.binFileId,
                      lastRefreshed: Date.now()
                    });
                }
            }

            // 如果没有从bin获取到新token，使用统一的token解析逻辑
            log.debug(`[TokenStore] 🔍 调试 - 检查updatedTokenData状态:`, {
                hasUpdatedTokenData: !!updatedTokenData,
                updatedTokenData: updatedTokenData
            });
            
            if (!updatedTokenData) {
                log.debug(`[TokenStore] 🔍 调试 - 未从bin文件获取到新token，使用统一的token解析逻辑`);
                const parseResult = parseBase64Token(actualToken)
                if (parseResult.success) {
                    actualToken = parseResult.data.actualToken
                }
            } else {
                log.debug(`[TokenStore] 🔍 调试 - 已从bin文件获取到新token，将使用新token生成WSS链接`);
            }

            // 使用固定的WebSocket基础地址，将token带入占位符
            const baseWsUrl = 'wss://xxz-xyzw.hortorgames.com/agent?p=%s&e=x&lang=chinese'
            
            // 生成最终的WSS链接
            let tokenForWs = actualToken
            if (updatedTokenData) {
                // 如果有更新的token数据，直接使用它生成链接参数
                tokenForWs = JSON.stringify(updatedTokenData)
                // 确保使用更新后的token数据生成WSS链接
                finalWsUrl = `wss://xxz-xyzw.hortorgames.com/agent?p=${encodeURIComponent(tokenForWs)}&e=x&lang=chinese`;
                log.debug(`[Bin连接] 使用新roleToken生成的WSS链接: ${finalWsUrl.substring(0, 100)}...`);
            }
            
            // 如果没有从bin文件生成finalWsUrl，则使用baseWsUrl
            if (!finalWsUrl) {
                finalWsUrl = baseWsUrl.replace('%s', encodeURIComponent(tokenForWs))
            }

            log.debug(`🔗 创建WebSocket连接:`, finalWsUrl)
            log.debug(`🎯 Token ID: ${tokenId}`)
            log.debug(`🔑 使用Token: ${actualToken.substring(0, 20)}...`)
            log.debug(`[TokenStore] 🔍 调试 - 最终使用的WSS链接: ${finalWsUrl}`);
            log.debug(`[TokenStore] 🔍 调试 - WSS链接长度: ${finalWsUrl.length}`);
            log.debug(`[TokenStore] 🔍 调试 - 是否使用bin文件生成的WSS链接: ${!!updatedTokenData}`);
            if (updatedTokenData) {
                log.debug(`[Bin连接] 完整的Token数据:`, updatedTokenData);
                log.debug(`[TokenStore] 🔍 调试 - bin文件中的roleToken: ${updatedTokenData.roleToken.substring(0, 20)}...`);
                log.debug(`[TokenStore] 🔍 调试 - bin文件中的roleToken长度: ${updatedTokenData.roleToken.length}`);
            }

            // 检查g_utils结构
            log.debug('🔍 g_utils结构检查:', {
                hasGetEnc: !!g_utils.getEnc,
                hasEncode: !!g_utils.encode,
                hasParse: !!g_utils.parse,
                hasBon: !!g_utils.bon,
                bonHasDecode: !!(g_utils.bon && g_utils.bon.decode)
            })

            // 创建新的WebSocket客户端
            const wsClient = new XyzwWebSocketClient({
                url: finalWsUrl,
                utils: g_utils,
                heartbeatMs: 3000,  // 3秒心跳间隔（优化长任务连接稳定性）
                idleTimeout: 5 * 60 * 1000  // 🔧 v3.13.5: 空闲超时5分钟（批量任务可能需要更长时间）
            })

            // 设置连接状态
            wsConnections.value[tokenId] = {
                client: wsClient,
                status: 'connecting',
                tokenId,
                wsUrl: finalWsUrl,
                actualToken,
                connectedAt: null,
                lastMessage: null,
                lastError: null,
                useBinFile: !!(tokenObj?.binFileId || tokenObj?.binFileContent), // 标记是否使用bin文件
                useStoredBinFile: !!tokenObj?.binFileId // 标记是否使用本地存储的bin文件
            }

            // 设置事件监听
            wsClient.onConnect = () => {
                if (shouldLog('websocket')) {
                    log.debug(`✅ WebSocket连接已建立: ${tokenId}`)
                }
                if (wsConnections.value[tokenId]) {
                    wsConnections.value[tokenId].status = 'connected'
                    wsConnections.value[tokenId].connectedAt = new Date().toISOString()
                }
            }

            wsClient.onDisconnect = (event) => {
                if (shouldLog('websocket')) {
                    log.debug(`🔌 WebSocket连接已断开: ${tokenId}`, event)
                }
                if (wsConnections.value[tokenId]) {
                    wsConnections.value[tokenId].status = 'disconnected'
                }
            }

            wsClient.onError = (error) => {
                if (shouldLog('websocket')) {
                    log.error(`❌ WebSocket错误 [${tokenId}]:`, error)
                }
                if (wsConnections.value[tokenId]) {
                    wsConnections.value[tokenId].status = 'error'
                    wsConnections.value[tokenId].lastError = {
                        timestamp: new Date().toISOString(),
                        error: error.toString(),
                        url: finalWsUrl
                    }
                }
            }

            // 设置消息监听
            wsClient.setMessageListener((message) => {
                // 只打印消息命令，不打印完整结构
                const cmd = message?.cmd || 'unknown'
                if (cmd !== '_sys/ack') { // 过滤心跳消息
                    log.debug(`📨 [${tokenId}] ${cmd}`)
                    // 如果是 unknown，打印完整消息结构帮助调试
                    if (cmd === 'unknown') {
                        log.debug(`🔍 Unknown消息完整结构:`, message)
                        log.debug(`🔍 _raw内容:`, message._raw)
                        log.debug(`🔍 _raw.cmd:`, message._raw?.cmd)
                        log.debug(`🔍 _raw.resp:`, message._raw?.resp)
                        log.debug(`🔍 _raw.body:`, message._raw?.body)
                        log.debug(`🔍 message.rawData:`, message.rawData)
                    }
                }

                // 更新连接状态中的最后接收消息（🆕 v3.14.0: 只保存必要信息，不保存完整数据）
                if (wsConnections.value[tokenId]) {
                    wsConnections.value[tokenId].lastMessage = {
                        timestamp: new Date().toISOString(),
                        cmd: message?.cmd
                        // ❌ 不再保存 data: message - 会导致内存泄漏（916个token × 大量消息）
                    }
                }

                // 处理游戏消息
                handleGameMessage(tokenId, message)
            })

            // 开启调试模式
            wsClient.setShowMsg(true)

            // 初始化连接
            wsClient.init()

            return wsClient
        } catch (error) {
            log.error(`创建WebSocket连接失败 [${tokenId}]:`, error)
            return null
        }
    }

    // 重新连接WebSocket，每次都从bin文件重新获取token
    const reconnectWebSocket = async (tokenId) => {
        log.debug(`🔄 重新连接WebSocket，Token ID: ${tokenId}`);
        
        // 查找对应的token对象
        const tokenObj = gameTokens.value.find(t => t.id === tokenId);
        if (!tokenObj) {
            log.error(`❌ 找不到Token对象，Token ID: ${tokenId}`);
            return null;
        }
        
        // 关闭现有连接
        if (wsConnections.value[tokenId]) {
            closeWebSocketConnection(tokenId);
        }
        
        // 强制从bin文件获取新的roleToken
        if (tokenObj.binFileId || tokenObj.binFileContent) {
            log.debug(`🔄 重新连接时检测到bin文件，强制获取新的roleToken`);
            
            // 清除现有token数据，确保重新获取
            let newRoleToken = null;
            if (tokenObj.binFileId) {
                // 从本地存储的bin文件获取新的roleToken
                newRoleToken = await getRoleTokenFromStoredBin(tokenId);
            } else if (tokenObj.binFileContent) {
                // 从内存中的bin文件获取新的roleToken
                newRoleToken = await getRoleTokenFromBin(tokenId, tokenObj.binFileContent);
            }
            
            if (newRoleToken) {
                log.debug(`🔄 成功从bin文件获取新的roleToken: ${newRoleToken.substring(0, 20)}...`);
                log.debug(`[TokenStore] 🔍 调试 - 重新连接时获取的完整roleToken数据:`, newRoleToken);
                log.debug(`[TokenStore] 🔍 调试 - 重新连接时获取的roleToken长度: ${newRoleToken.length}`);
                
                // 生成完整的Token数据和WSS连接参数
                const sessId = Date.now() * 1000 + Math.floor(Math.random() * 1000);
                const connId = Date.now();
                const updatedTokenData = {
                    roleToken: newRoleToken,
                    sessId,
                    connId,
                    isRestore: 0,
                };
                
                log.debug(`[Bin连接] 重新连接时生成的Token数据:`, updatedTokenData);
                
                // 创建完整的Token格式
                const tokenStr = JSON.stringify(updatedTokenData);
                const newToken = btoa(unescape(encodeURIComponent(tokenStr)));
                log.debug(`[Bin连接] 重新连接时Base64编码的Token: ${newToken.substring(0, 20)}...`);
                
                // 生成最终的WSS链接
                const finalWsUrl = `wss://xxz-xyzw.hortorgames.com/agent?p=${encodeURIComponent(tokenStr)}&e=x&lang=chinese`;
                log.debug(`[Bin连接] 重新连接时生成的WSS链接: ${finalWsUrl.substring(0, 100)}...`);
                log.debug(`[TokenStore] 🔍 调试 - 重新连接时生成的完整WSS链接: ${finalWsUrl}`);
                log.debug(`[TokenStore] 🔍 调试 - 重新连接时WSS链接长度: ${finalWsUrl.length}`);
                
                // 更新token对象中的实际token
                updateToken(tokenId, {
                    token: newToken,
                    rawData: updatedTokenData,
                    wsUrl: finalWsUrl,
                    binFileId: tokenObj.binFileId,
                    lastRefreshed: Date.now()
                });
                
                // 使用新的token创建连接
                return await createWebSocketConnection(tokenId, newToken, finalWsUrl);
            } else {
                // 🔥 v3.14.1: 降低日志级别，避免大量错误输出消耗CPU
                log.debug(`从bin文件获取roleToken失败（使用原有token），Token ID: ${tokenId}`)
                // 如果获取失败，使用原有token创建连接
                return await createWebSocketConnection(tokenId, tokenObj.token, tokenObj.wsUrl);
            }
        } else {
            // 如果没有bin文件，使用原有token创建连接
            return await createWebSocketConnection(tokenId, tokenObj.token, tokenObj.wsUrl);
        }
    }

    const closeWebSocketConnection = (tokenId) => {
        const connection = wsConnections.value[tokenId]
        if (connection && connection.client) {
            // 🆕 v3.14.0: 清理该token的所有定时器，防止内存泄漏
            if (wsTimers.value[tokenId]) {
                wsTimers.value[tokenId].forEach(timerId => {
                    clearTimeout(timerId)
                })
                delete wsTimers.value[tokenId]
            }
            
            // 🆕 v3.14.0: 清理事件监听器，防止内存泄漏
            const client = connection.client
            if (client.onConnect) client.onConnect = null
            if (client.onDisconnect) client.onDisconnect = null
            if (client.onError) client.onError = null
            if (client.onMessage) client.onMessage = null
            
            // 🔥 安全断开连接：捕获可能的错误
            try {
                client.disconnect()
            } catch (error) {
                // 忽略断开连接时的错误（连接可能已经断开或正在建立中）
                log.debug(`断开连接时出现错误（已忽略）: ${tokenId}`, error.message)
            }
            
            // 删除连接记录
            delete wsConnections.value[tokenId]
        }
    }

    const getWebSocketStatus = (tokenId) => {
        return wsConnections.value[tokenId]?.status || 'disconnected'
    }

    // 获取WebSocket客户端
    const getWebSocketClient = (tokenId) => {
        return wsConnections.value[tokenId]?.client || null
    }

    // 设置消息监听器
    const setMessageListener = (listener) => {
        if (selectedToken.value) {
            const connection = wsConnections.value[selectedToken.value.id]
            if (connection && connection.client) {
                connection.client.setMessageListener(listener)
            }
        }
    }

    // 设置是否显示消息
    const setShowMsg = (show) => {
        if (selectedToken.value) {
            const connection = wsConnections.value[selectedToken.value.id]
            if (connection && connection.client) {
                connection.client.setShowMsg(show)
            }
        }
    }


    // 发送消息到WebSocket
    const sendMessage = (tokenId, cmd, params = {}, options = {}) => {
        const connection = wsConnections.value[tokenId]
        if (!connection || connection.status !== 'connected') {
            log.error(`❌ WebSocket未连接，无法发送消息 [${tokenId}]`)
            return false
        }

        try {
            const client = connection.client
            if (!client) {
                log.error(`❌ WebSocket客户端不存在 [${tokenId}]`)
                return false
            }

            client.send(cmd, params, options)
            log.debug(`📤 [${tokenId}] ${cmd}`)

            return true
        } catch (error) {
            log.error(`❌ 发送失败 [${tokenId}] ${cmd}:`, error.message)
            return false
        }
    }

    // Promise版发送消息
    const sendMessageWithPromise = async (tokenId, cmd, params = {}, timeout = 1000) => {
        const connection = wsConnections.value[tokenId]
        if (!connection || connection.status !== 'connected') {
            return Promise.reject(new Error(`WebSocket未连接 [${tokenId}]`))
        }

        const client = connection.client
        if (!client) {
            return Promise.reject(new Error(`WebSocket客户端不存在 [${tokenId}]`))
        }

        try {
            return await client.sendWithPromise(cmd, params, timeout)
        } catch (error) {
            return Promise.reject(error)
        }
    }

    // 发送心跳消息
    const sendHeartbeat = (tokenId) => {
        return sendMessage(tokenId, 'heart_beat')
    }

    // 发送获取角色信息请求（异步处理）
    const sendGetRoleInfo = async (tokenId, params = {}) => {
        try {
            const roleInfo = await sendMessageWithPromise(tokenId, 'role_getroleinfo', params, 1000)

            // 手动更新游戏数据（因为响应可能不会自动触发消息处理）
            if (roleInfo) {
                gameData.value.roleInfo = roleInfo
                gameData.value.lastUpdated = new Date().toISOString()
                log.debug('📊 角色信息已通过 Promise 更新')
            }

            return roleInfo
        } catch (error) {
            log.error(`❌ 获取角色信息失败 [${tokenId}]:`, error.message)
            throw error
        }
    }

    // 发送获取数据版本请求
    const sendGetDataBundleVersion = (tokenId, params = {}) => {
        return sendMessageWithPromise(tokenId, 'system_getdatabundlever', params)
    }

    // 发送签到请求
    const sendSignIn = (tokenId) => {
        return sendMessageWithPromise(tokenId, 'system_signinreward')
    }

    // 发送领取日常任务奖励
    const sendClaimDailyReward = (tokenId, rewardId = 0) => {
        return sendMessageWithPromise(tokenId, 'task_claimdailyreward', {rewardId})
    }

    // 发送获取队伍信息
    const sendGetTeamInfo = (tokenId, params = {}) => {
        return sendMessageWithPromise(tokenId, 'presetteam_getinfo', params)
    }

    // 发送自定义游戏消息
    const sendGameMessage = (tokenId, cmd, params = {}, options = {}) => {
        if (options.usePromise) {
            return sendMessageWithPromise(tokenId, cmd, params, options.timeout)
        } else {
            return sendMessage(tokenId, cmd, params, options)
        }
    }

    // 获取当前塔层数
    const getCurrentTowerLevel = () => {
        try {
            // 从游戏数据中获取塔信息
            const roleInfo = gameData.value.roleInfo
            if (!roleInfo || !roleInfo.role) {
                log.warn('⚠️ 角色信息不存在')
                return null
            }

            const tower = roleInfo.role.tower
            if (!tower) {
                log.warn('⚠️ 塔信息不存在')
                return null
            }

            // 可能的塔层数字段（根据实际数据结构调整）
            const level = tower.level || tower.currentLevel || tower.floor || tower.stage

            // 当前塔层数
            return level
        } catch (error) {
            log.error('❌ 获取塔层数失败:', error)
            return null
        }
    }

    // 获取详细塔信息
    const getTowerInfo = () => {
        try {
            const roleInfo = gameData.value.roleInfo
            if (!roleInfo || !roleInfo.role) {
                return null
            }

            return roleInfo.role.tower || null
        } catch (error) {
            log.error('❌ 获取塔信息失败:', error)
            return null
        }
    }

    // 工具方法
    const exportTokens = () => {
        // 🆕 兼容性修复：导出时排除大字段，避免文件过大
        const tokensToExport = gameTokens.value.map(token => ({
            id: token.id,
            name: token.name,
            token: token.token,
            wsUrl: token.wsUrl,
            server: token.server,
            level: token.level,
            profession: token.profession,
            createdAt: token.createdAt,
            lastUsed: token.lastUsed,
            isActive: token.isActive,
            sourceUrl: token.sourceUrl,
            importMethod: token.importMethod,
            // 注意：不导出 binFileId 和 binFileContent
            // binFileId 在新环境中无效，binFileContent 可能很大
            // 用户需要重新导入bin文件
            lastRefreshed: token.lastRefreshed
        }))
        
        return {
            tokens: tokensToExport,
            exportedAt: new Date().toISOString(),
            version: '2.1', // 版本升级，标记不包含bin文件数据
            note: 'bin文件数据未包含在导出中，需要重新导入bin文件以恢复完整功能'
        }
    }

    const importTokens = async (data) => {
        try {
            if (data.tokens && Array.isArray(data.tokens)) {
                gameTokens.value = data.tokens
                await saveTokensToStorage()
                return {success: true, message: `成功导入 ${data.tokens.length} 个Token`}
            } else {
                return {success: false, message: '导入数据格式错误'}
            }
        } catch (error) {
            return {success: false, message: '导入失败：' + error.message}
        }
    }

    const clearAllTokens = async () => {
        // 关闭所有WebSocket连接
        Object.keys(wsConnections.value).forEach(tokenId => {
            closeWebSocketConnection(tokenId)
        })

        // 🆕 v3.15.0: 清理所有bin文件存储数据（IndexedDB + localStorage）
        // ⚡ 性能优化：直接清空而非逐个删除
        try {
            const binFileIds = gameTokens.value
                .filter(token => token.binFileId)
                .map(token => token.binFileId)
            
            if (binFileIds.length > 0) {
                log.debug(`🧹 准备清理 ${binFileIds.length} 个关联的bin文件存储数据`)
                
                // ⚡ 优化：直接清空IndexedDB，比逐个删除快数百倍
                try {
                    await indexedDBStorage.clearBinFiles()
                    log.debug(`✅ 已从IndexedDB清空所有bin文件（快速模式）`)
                } catch (e) {
                    log.debug(`⚠️ IndexedDB清空失败: ${e.message}`)
                }
                
                // ⚡ 优化：直接删除localStorage key，比逐个操作快
                // 清理分段存储的所有key（storedBinFiles_0, storedBinFiles_1, ...）
                const segmentCount = Math.ceil(binFileIds.length / 30)  // 每段30个
                for (let i = 0; i < segmentCount; i++) {
                    localStorage.removeItem(`storedBinFiles_${i}`)
                }
                
                // 清理索引
                localStorage.removeItem('storedBinFiles_index')
                
                // 清理旧方式存储
                localStorage.removeItem('storedBinFiles')
                
                log.debug(`✅ 已清理所有localStorage中的bin文件存储数据（快速模式）`)
            }
        } catch (error) {
            log.debug(`⚠️ 清理bin文件存储数据失败（不影响token清空）:`, error)
        }

        gameTokens.value = []
        selectedTokenId.value = null
        
        // 🆕 v3.15.0: 清理IndexedDB存储
        await indexedDBStorage.clearTokens()
        
        localStorage.removeItem('gameTokens')
        localStorage.removeItem('selectedTokenId')
        localStorage.removeItem('gameTokens_count')
        localStorage.removeItem('gameTokens_useIndexedDB')
    }

    const cleanExpiredTokens = async () => {
        // 🔧 修复：确保 gameTokens.value 是数组
        if (!Array.isArray(gameTokens.value)) {
            log.warn('⚠️ gameTokens 不是数组，正在重置为空数组', gameTokens.value)
            gameTokens.value = []
            await saveTokensToStorage()
            return 0
        }

        const now = new Date()
        const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000)

        const cleanedTokens = gameTokens.value.filter(token => {
            const lastUsed = new Date(token.lastUsed || token.createdAt)
            return lastUsed > oneDayAgo
        })

        const cleanedCount = gameTokens.value.length - cleanedTokens.length
        gameTokens.value = cleanedTokens
        await saveTokensToStorage()

        return cleanedCount
    }

    // 🆕 v3.15.1: 防抖保存机制，避免频繁保存
    let saveTokensTimer = null
    const saveTokensDebounced = async (immediate = false) => {
        // 清除现有定时器
        if (saveTokensTimer) {
            clearTimeout(saveTokensTimer)
            saveTokensTimer = null
        }
        
        if (immediate) {
            // 立即保存（用于关键操作）
            await saveTokensToStorageInternal()
        } else {
            // 延迟保存（用于频繁操作，如更新lastUsed）
            return new Promise((resolve) => {
                saveTokensTimer = setTimeout(async () => {
                    await saveTokensToStorageInternal()
                    saveTokensTimer = null
                    resolve()
                }, 1000) // 1秒内的多次更新只保存一次
            })
        }
    }

    const saveTokensToStorage = async (immediate = true) => {
        return saveTokensDebounced(immediate)
    }

    const saveTokensToStorageInternal = async () => {
        // 🔧 修复：确保 gameTokens.value 是数组
        if (!Array.isArray(gameTokens.value)) {
            log.warn('⚠️ saveTokensToStorage: gameTokens 不是数组，正在重置为空数组')
            gameTokens.value = []
        }

        // 优化存储：只保存必要的字段，移除大字段以节省空间
        const tokensToSave = gameTokens.value.map(token => ({
            id: token.id,
            name: token.name,
            token: token.token,
            wsUrl: token.wsUrl,
            server: token.server,
            level: token.level,
            profession: token.profession,
            createdAt: token.createdAt,
            lastUsed: token.lastUsed,
            isActive: token.isActive,
            sourceUrl: token.sourceUrl,
            importMethod: token.importMethod,
            binFileId: token.binFileId,
            // 不保存大字段：binFileContent, rawData
            lastRefreshed: token.lastRefreshed
        }))
        
        try {
            // 🆕 v3.15.1: 使用updateTokens增量更新，比saveTokens快
            await indexedDBStorage.updateTokens(tokensToSave)
            log.debug(`✅ 成功更新 ${tokensToSave.length} 个token到IndexedDB`)
            
            // 保存token数量到localStorage（用于快速检查）
            try {
                localStorage.setItem('gameTokens_count', tokensToSave.length.toString())
                localStorage.setItem('gameTokens_useIndexedDB', 'true')
            } catch (e) {
                // localStorage失败不影响主要功能
                log.warn('⚠️ 无法保存token数量到localStorage:', e.message)
            }
        } catch (error) {
            log.error('❌ 保存token到IndexedDB失败:', error.message)
            
            // 如果IndexedDB失败，尝试回退到localStorage（只用于少量token）
            if (tokensToSave.length <= 50) {
                log.warn('⚠️ IndexedDB失败，尝试使用localStorage作为备选...')
                try {
                    localStorage.setItem('gameTokens', JSON.stringify(tokensToSave))
                    localStorage.setItem('gameTokens_useIndexedDB', 'false')
                    log.debug(`✅ 已使用localStorage保存 ${tokensToSave.length} 个token`)
                } catch (e) {
                    log.error('❌ localStorage也失败:', e.message)
                    throw new Error(`无法保存token：存储失败。建议检查浏览器设置。`)
                }
            } else {
                throw new Error(`无法保存大量token (${tokensToSave.length}个)：IndexedDB失败。`)
            }
        }
    }

    // 清理token中的大字段，释放内存
    const cleanupTokenData = async () => {
        let cleaned = 0
        gameTokens.value.forEach(token => {
            if (token.binFileContent || token.rawData) {
                delete token.binFileContent
                delete token.rawData
                cleaned++
            }
        })
        
        if (cleaned > 0) {
            log.debug(`🧹 清理了 ${cleaned} 个token的冗余数据`)
            await saveTokensToStorage()
        }
        
        return cleaned
    }
    
    // 🆕 v3.15.0: 数据迁移 - 将localStorage的token迁移到IndexedDB
    const migrateToIndexedDB = async () => {
        try {
            const useIndexedDB = localStorage.getItem('gameTokens_useIndexedDB')
            
            // 如果已经迁移过，跳过
            if (useIndexedDB === 'true') {
                log.debug('✅ 已使用IndexedDB，无需迁移')
                return false
            }
            
            // 从localStorage读取旧数据
            const savedTokens = localStorage.getItem('gameTokens')
            if (!savedTokens) {
                log.debug('📦 localStorage中没有token数据')
                return false
            }
            
            const tokens = JSON.parse(savedTokens)
            if (!Array.isArray(tokens) || tokens.length === 0) {
                log.debug('📦 没有需要迁移的token')
                return false
            }
            
            log.info(`🔄 开始迁移 ${tokens.length} 个token到IndexedDB...`)
            
            // 保存到IndexedDB
            await indexedDBStorage.saveTokens(tokens)
            
            // 标记已迁移
            localStorage.setItem('gameTokens_useIndexedDB', 'true')
            localStorage.setItem('gameTokens_count', tokens.length.toString())
            
            // 清理localStorage中的旧数据（保留备份一段时间）
            // localStorage.removeItem('gameTokens') // 暂时保留，以防需要回滚
            
            log.info(`✅ 成功迁移 ${tokens.length} 个token到IndexedDB`)
            return true
        } catch (error) {
            log.error('❌ 迁移到IndexedDB失败:', error)
            return false
        }
    }

    // 🆕 v3.15.0: bin文件迁移 - 将localStorage分段存储的bin文件迁移到IndexedDB
    const migrateBinFilesToIndexedDB = async () => {
        try {
            const binMigrated = localStorage.getItem('binFiles_migratedToIndexedDB')
            
            // 如果已经迁移过，跳过
            if (binMigrated === 'true') {
                log.debug('✅ bin文件已迁移到IndexedDB，无需重复迁移')
                return false
            }
            
            log.info(`🔄 开始迁移bin文件到IndexedDB...`)
            
            // 从localStorage分段存储读取所有bin文件
            const binFiles = []
            
            // 读取索引
            try {
                const index = JSON.parse(localStorage.getItem('storedBinFiles_index') || '{}')
                const fileIds = Object.keys(index)
                
                if (fileIds.length === 0) {
                    log.debug('📦 localStorage中没有bin文件数据')
                    localStorage.setItem('binFiles_migratedToIndexedDB', 'true')
                    return false
                }
                
                log.info(`📦 找到 ${fileIds.length} 个bin文件，开始迁移...`)
                
                // 逐个读取bin文件
                for (const fileId of fileIds) {
                    const segmentNum = index[fileId]
                    if (segmentNum !== undefined) {
                        const segmentKey = `storedBinFiles_${segmentNum}`
                        try {
                            const segment = JSON.parse(localStorage.getItem(segmentKey) || '{}')
                            const binFile = segment[fileId]
                            if (binFile) {
                                binFiles.push(binFile)
                            }
                        } catch (e) {
                            log.debug(`⚠️ 读取分段 ${segmentKey} 失败: ${e.message}`)
                        }
                    }
                }
            } catch (e) {
                log.debug(`读取localStorage索引失败，尝试旧方式: ${e.message}`)
                
                // 尝试旧方式读取
                try {
                    const storedBinFiles = JSON.parse(localStorage.getItem('storedBinFiles') || '{}')
                    const fileIds = Object.keys(storedBinFiles)
                    for (const fileId of fileIds) {
                        binFiles.push(storedBinFiles[fileId])
                    }
                } catch (e2) {
                    log.debug(`旧方式读取也失败: ${e2.message}`)
                }
            }
            
            if (binFiles.length === 0) {
                log.debug('📦 没有需要迁移的bin文件')
                localStorage.setItem('binFiles_migratedToIndexedDB', 'true')
                return false
            }
            
            log.info(`🔄 开始将 ${binFiles.length} 个bin文件保存到IndexedDB...`)
            
            // 批量保存到IndexedDB
            await indexedDBStorage.saveBinFiles(binFiles)
            
            // 标记已迁移
            localStorage.setItem('binFiles_migratedToIndexedDB', 'true')
            
            // 清理localStorage中的旧数据（保留备份一段时间）
            // 不立即清理，以防需要回滚
            
            log.info(`✅ 成功迁移 ${binFiles.length} 个bin文件到IndexedDB`)
            return true
        } catch (error) {
            log.error('❌ 迁移bin文件到IndexedDB失败:', error)
            return false
        }
    }

    // 初始化
    const initTokenStore = async () => {
        try {
            // 🆕 v3.15.0: 首先尝试数据迁移
            await migrateToIndexedDB()
            
            // 🆕 v3.15.0: 迁移bin文件
            await migrateBinFilesToIndexedDB()
            
            // 从IndexedDB恢复数据
            const useIndexedDB = localStorage.getItem('gameTokens_useIndexedDB')
            
            if (useIndexedDB === 'true') {
                // 使用IndexedDB
                const tokens = await indexedDBStorage.getTokens()
                gameTokens.value = Array.isArray(tokens) ? tokens : []
                log.info(`📦 从IndexedDB加载了 ${gameTokens.value.length} 个token`)
            } else {
                // 回退到localStorage
                const savedTokens = localStorage.getItem('gameTokens')
                if (savedTokens) {
                    try {
                        gameTokens.value = JSON.parse(savedTokens)
                        log.info(`📦 从localStorage加载了 ${gameTokens.value.length} 个token`)
                    } catch (error) {
                        log.error('解析Token数据失败:', error.message)
                        gameTokens.value = []
                    }
                }
            }

            // 恢复选中的token ID
            const savedSelectedId = localStorage.getItem('selectedTokenId')
            if (savedSelectedId) {
                selectedTokenId.value = savedSelectedId
            }

            // 清理过期token
            await cleanExpiredTokens()
            
            // 自动清理冗余数据（延迟执行，避免阻塞初始化）
            setTimeout(() => {
                cleanupTokenData()
            }, 1000)
            
            // 显示存储使用情况
            const storageInfo = await indexedDBStorage.getStorageEstimate()
            const binCount = await indexedDBStorage.getBinFileCount()
            if (storageInfo) {
                log.info(`💾 存储使用情况: ${storageInfo.usageMB}MB / ${storageInfo.quotaMB}MB (${storageInfo.percentUsed}%)`)
                log.info(`📁 存储统计: ${gameTokens.value.length} 个token, ${binCount} 个bin文件`)
            }
        } catch (error) {
            log.error('❌ 初始化TokenStore失败:', error)
            gameTokens.value = []
        }
    }

    return {
        // 状态
        gameTokens,
        selectedTokenId,
        wsConnections,
        gameData,

        // 计算属性
        hasTokens,
        selectedToken,
        selectedTokenRoleInfo,

        // Token管理方法
        addToken,
        updateToken,
        removeToken,
        selectToken,

        // Base64解析方法
        parseBase64Token,
        importBase64Token,

        // WebSocket方法
        createWebSocketConnection,
        reconnectWebSocket,
        closeWebSocketConnection,
        getWebSocketStatus,
        getWebSocketClient,
        sendMessage,
        sendMessageWithPromise,
        sendMessageAsync: sendMessageWithPromise, // 别名，便于使用
        setMessageListener,
        setShowMsg,
        sendHeartbeat,
        sendGetRoleInfo,
        sendGetDataBundleVersion,
        sendSignIn,
        sendClaimDailyReward,
        sendGetTeamInfo,
        sendGameMessage,

        // 工具方法
        exportTokens,
        importTokens,
        clearAllTokens,
        cleanExpiredTokens,
        cleanupTokenData,
        initTokenStore,

        // 塔信息方法
        getCurrentTowerLevel,
        getTowerInfo,

        // 调试工具方法
        validateToken,
        debugToken: (tokenString) => {
            log.debug('🔍 Token调试信息:')
            log.debug('原始Token:', tokenString)
            const parseResult = parseBase64Token(tokenString)
            log.debug('解析结果:', parseResult)
            if (parseResult.success) {
                log.debug('实际Token:', parseResult.data.actualToken)
                log.debug('Token有效性:', validateToken(parseResult.data.actualToken))
            }
            return parseResult
        }
    }
})
