import { getContext } from '/scripts/extensions.js';
import { state, SCRIPT_ID_PREFIX } from './cwb_state.js';
import { logDebug, logError, showToastr, escapeHtml, cleanChatName, parseCustomFormat, buildCustomFormat, isCwbEnabled } from './cwb_utils.js';
import { callCustomOpenAI } from './cwb_apiService.js';
import { saveDescriptionToLorebook, updateCharacterRosterLorebookEntry, manageAutoCardUpdateLorebookEntry, getTargetWorldBook } from './cwb_lorebookManager.js';
import { extractBlocksByTags, applyExclusionRules } from '../../core/utils/rag-tag-extractor.js';
import { getExtensionSettings } from '../../utils/settings.js';
import { getPresetPrompts, getMixedOrder } from '../../PresetSettings/index.js';
import { generateRandomSeed } from '../../core/api.js';
import { getChatIdentifier } from '../../core/lore.js';
import { safeLorebookEntries } from '../../core/tavernhelper-compatibility.js';
import { amilyHelper } from '../../core/tavern-helper/main.js';
import { resolveHistoriographyRuleConfig } from '../../utils/config/RuleProfileManager.js';

const { SillyTavern, jQuery, characters } = window;

let isUpdatingCard = false;
let isBatchUpdating = false;
let manualBatchStopRequested = false;
let currentBatchNum = 0;
let totalBatchesNum = 0;
const MAX_BATCH_RETRIES = 2;

export async function updateCardUpdateStatusDisplay($panel) {
    if (!$panel || !$panel.length) return;
    const $statusDisplay = $panel.find(`#${SCRIPT_ID_PREFIX}-card-update-status-display`);
    const $totalMessagesDisplay = $panel.find(`#${SCRIPT_ID_PREFIX}-total-messages-display`);

    $totalMessagesDisplay.text(`上下文总层数: ${state.allChatMessages.length}`);

    if (!state.currentChatFileIdentifier || state.currentChatFileIdentifier.startsWith('unknown_chat')) {
        $statusDisplay.text('当前聊天未知，无法获取更新状态。');
        return;
    }

    try {
        const context = SillyTavern.getContext();
        if (!context || !context.characterId) {
            $statusDisplay.text('没有选择角色。');
            return;
        }
        const bookName = await getTargetWorldBook();
        if (!bookName) {
            $statusDisplay.text('当前角色未设置主世界书或自定义世界书。');
            return;
        }
        const entries = await safeLorebookEntries(bookName);
        const entryPrefixForCurrentChat = `角色卡更新-${state.currentChatFileIdentifier}-`;

        let latestEntryToShow = null;
        let maxEndFloorOverall = -1;

        for (const entry of entries) {
            if (entry.comment && entry.comment.startsWith(entryPrefixForCurrentChat)) {
                const match = entry.comment.match(/-(\d+)-(\d+)$/);
                if (match && match[2]) {
                    const endFloor = parseInt(match[2], 10);
                    if (endFloor > maxEndFloorOverall) {
                        maxEndFloorOverall = endFloor;
                        latestEntryToShow = entry;
                    }
                }
            }
        }

        if (latestEntryToShow) {
            const commentParts = latestEntryToShow.comment.split('-');
            const charNameInComment = commentParts.slice(2, -2).join('-');
            const startFloorStr = commentParts[commentParts.length - 2];
            const endFloorStr = commentParts[commentParts.length - 1];
            $statusDisplay.html(
                `最新更新: 角色 <b>${escapeHtml(charNameInComment)}</b> (基于楼层 <b>${startFloorStr}-${endFloorStr}</b>)`
            );
        } else {
            $statusDisplay.text('当前聊天信息尚未在世界书中更新。');
        }
    } catch (e) {
        logError('加载/解析世界书条目以更新UI状态时失败:', e);
        $statusDisplay.text('获取世界书更新状态时出错。');
    }
}

async function loadAllChatMessages($panel) {
    logDebug('尝试使用 getContext() 加载所有聊天消息...');
    if (!SillyTavern) {
        logError('SillyTavern API 不可用。');
        state.allChatMessages = [];
        return;
    }

    try {
        const context = SillyTavern.getContext();
        const chat = context?.chat || [];

        if (chat.length === 0) {
            logDebug('聊天为空，无需加载消息。');
            state.allChatMessages = [];
        } else {
            state.allChatMessages = chat.map((msg, idx) => ({
                ...msg,
                message: msg.mes,
                id: idx
            }));
        }
        
        logDebug(`成功为 ${state.currentChatFileIdentifier} 加载了 ${state.allChatMessages.length} 条消息。`);
        await updateCardUpdateStatusDisplay($panel);

    } catch (error) {
        logError('使用 getContext() 获取聊天消息时发生严重错误:', error);
        showToastr('error', '获取聊天记录时发生内部错误。');
        state.allChatMessages = [];
    }
}

function processChatMessages(messages) {
    if (!messages || !Array.isArray(messages) || messages.length === 0) {
        logDebug('[CWB] processChatMessages: 没有可处理的消息。');
        return '';
    }

    logDebug(`[CWB] processChatMessages: 开始处理 ${messages.length} 条消息。`);
    
    try {
        const mainSettings = getExtensionSettings();
        if (!mainSettings) {
            logError('[CWB] 无法访问主扩展设置。将使用原始消息。');
            return messages.map(msg => `${msg.is_user ? SillyTavern?.name1 || '用户' : msg.name || '角色'}: ${msg.message}`).join('\n\n');
        }
        
        const historiographyRuleConfig = resolveHistoriographyRuleConfig(mainSettings);
        const useTagExtraction = historiographyRuleConfig.tagExtractionEnabled ?? false;
        const tagsToExtract = useTagExtraction ? (historiographyRuleConfig.tags || '').split(',').map(t => t.trim()).filter(Boolean) : [];
        const exclusionRules = historiographyRuleConfig.exclusionRules || [];

        logDebug(`[CWB] 标签提取: ${useTagExtraction}, 标签: ${tagsToExtract.join(', ')}, 排除规则: ${exclusionRules.length}`);

        if (!useTagExtraction && exclusionRules.length === 0) {
            logDebug('[CWB] 未激活任何处理规则。返回合并后的原始消息。');
            return messages.map(msg => `${msg.is_user ? SillyTavern?.name1 || '用户' : msg.name || '角色'}: ${msg.message}`).join('\n\n');
        }

        const processedMessages = messages.map((msg) => {
            let content = msg.message;

            if (useTagExtraction && tagsToExtract.length > 0) {
                const blocks = extractBlocksByTags(content, tagsToExtract);
                if (blocks.length > 0) {
                    content = blocks.join('\n\n');
                }
            }
            
            content = applyExclusionRules(content, exclusionRules);
            
            if (!content.trim()) return null;

                    return `【${msg.is_user ? SillyTavern?.name1 || '用户' : msg.name || '角色'}】:\n${content.trim()}`;
        }).filter(Boolean);

        logDebug(`[CWB] processChatMessages: 处理完成。${messages.length} -> ${processedMessages.length} 条有效消息。`);
        return processedMessages.join('\n\n');
        
    } catch (error) {
        logError('[CWB] processChatMessages 中发生错误:', error);
        return messages.map(msg => `${msg.is_user ? SillyTavern?.name1 || '用户' : msg.name || '角色'}: ${msg.message}`).join('\n\n');
    }
}


async function proceedWithCardUpdate($panel, messagesToUse) {
    const statusUpdater = text => {
        if ($panel && $panel.length) {
            $panel.find(`#${SCRIPT_ID_PREFIX}-status-message`).text(text);
        }
    };
    statusUpdater('正在生成角色卡描述...');

    try {
        const mode = state.isIncrementalUpdateEnabled ? 'cwb_summarizer_incremental' : 'cwb_summarizer';
        const presetPrompts = await getPresetPrompts(mode);
        const order = getMixedOrder(mode) || [];
        
        const messages = [
            { role: 'system', content: generateRandomSeed() }
        ];
        let promptCounter = 0;
        let existingData = {};

        if (state.isIncrementalUpdateEnabled) {
            statusUpdater('增量更新模式：正在获取现有角色数据...');
            try {
                const bookName = await getTargetWorldBook();
                if (bookName) {
                    const entries = (await safeLorebookEntries(bookName)) || [];
                    let chatIdentifier = state.currentChatFileIdentifier.replace(/ imported/g, '');
                    const messagesText = messagesToUse.map(m => {
                        const name = m.name || '';
                        const content = m.message || '';
                        return `${name}\n${content}`;
                    }).join('\n').toLowerCase();

                    const characterEntries = entries.filter(e => 
                        e.enabled &&
                        Array.isArray(e.keys) &&
                        e.keys.includes(chatIdentifier) &&
                        !e.keys.includes('Amily2角色总集')
                    );

                    for (const entry of characterEntries) {
                        try {
                            const keysToCheck = entry.keys.filter(k => k !== chatIdentifier);
                            if (entry.secondary_keys && Array.isArray(entry.secondary_keys)) {
                                keysToCheck.push(...entry.secondary_keys);
                            }
                            
                            let isTriggered = false;
                            if (keysToCheck.length > 0) {
                                isTriggered = keysToCheck.some(key => messagesText.includes(key.toLowerCase()));
                            }

                            if (isTriggered) {
                                const parsedData = parseCustomFormat(entry.content);
                                const entryCharName = parsedData?.name?.trim() || parsedData?.CI?.name?.trim() || parsedData?.core_identity?.name?.trim();
                                if (entryCharName) {
                                    existingData[entryCharName] = entry.content;
                                }
                            }
                        } catch (parseError) {
                            logError(`解析现有角色条目时出错 (UID: ${entry.uid}):`, parseError);
                        }
                    }
                    logDebug(`为 '${chatIdentifier}' 找到了 ${Object.keys(existingData).length} 个被触发的现有角色条目。`);
                }
            } catch (e) {
                logError('在增量更新中获取现有角色数据时出错:', e);
                showToastr('error', '获取旧档案失败，请检查控制台。');
            }
        }

        for (const item of order) {
            if (item.type === 'prompt') {
                if (presetPrompts && presetPrompts[promptCounter]) {
                    messages.push(presetPrompts[promptCounter]);
                    promptCounter++;
                }
            } else if (item.type === 'conditional') {
                switch (item.id) {
                    case 'cwb_break_armor_prompt':
                        if (state.currentBreakArmorPrompt) {
                            messages.push({ role: "system", content: state.currentBreakArmorPrompt });
                        }
                        break;
                    case 'cwb_char_card_prompt':
                        if (state.currentCharCardPrompt) {
                            messages.push({ role: "system", content: state.currentCharCardPrompt });
                        }
                        break;
                    case 'oldFiles':
                        if (state.isIncrementalUpdateEnabled) {
                            let oldFilesContent = "【旧档案】\n";
                            if (Object.keys(existingData).length > 0) {
                                for (const charName in existingData) {
                                    oldFilesContent += `${existingData[charName]}\n`;
                                }
                            } else {
                                oldFilesContent += "无\n";
                            }
                            messages.push({ role: 'user', content: oldFilesContent });
                        }
                        break;
                    case 'newContext':
                        const processedText = processChatMessages(messagesToUse);
                        let newContextContent = "";
                        if (state.isIncrementalUpdateEnabled) {
                            newContextContent = "【新对话】\n";
                        } else {
                            newContextContent = "最近的聊天记录摘要:\n";
                        }

                        if (processedText) {
                            newContextContent += processedText;
                        } else {
                            newContextContent += "(无有效对话内容)";
                        }

                        if (!state.isIncrementalUpdateEnabled) {
                            newContextContent += "\n\n请根据以上聊天记录更新角色描述：";
                        }
                        messages.push({ role: 'user', content: newContextContent });
                        break;
                }
            }
        }

        statusUpdater('正在调用AI生成角色卡...');
        const aiResponse = await callCustomOpenAI(messages);
        if (!aiResponse) throw new Error('AI未能生成有效描述。');

        const endFloor_0idx = state.allChatMessages.length - 1;
        const startFloor_0idx = Math.max(0, state.allChatMessages.length - messagesToUse.length);

        const characterBlocks = aiResponse.split(/(?=\[--Amily2::CHAR_START--\])/).filter(block => block.trim());
        if (characterBlocks.length === 0) throw new Error('AI未能生成任何角色描述块。');

        let allSucceeded = true;
        let processedNames = [];

        for (const block of characterBlocks) {
            const trimmedBlock = block.trim();
            if (!trimmedBlock) continue;

            const parsedData = parseCustomFormat(trimmedBlock);
            const charName = (parsedData?.name?.trim() || parsedData?.CI?.name?.trim() || parsedData?.core_identity?.name?.trim()) || 'UnknownCharacter';

            if (charName === 'UnknownCharacter') {
                logError('无法在块中找到角色名:', trimmedBlock);
                continue;
            }

            const success = await saveDescriptionToLorebook(charName, trimmedBlock, startFloor_0idx, endFloor_0idx);
            if (success) {
                processedNames.push(charName);
            } else {
                allSucceeded = false;
            }
        }

        if (processedNames.length > 0) {
            await updateCharacterRosterLorebookEntry([...new Set(processedNames)], startFloor_0idx, endFloor_0idx);
            statusUpdater(`已为 ${processedNames.length} 个角色更新描述！`);
        } else {
            throw new Error('AI生成了内容，但未能成功提取任何有效的角色卡。');
        }

        updateCardUpdateStatusDisplay($panel);
        return allSucceeded;
    } catch (error) {
        logError('角色卡更新过程出错:', error);
        showToastr('error', `更新失败: ${error.message}`);
        statusUpdater('错误：更新失败。');
        return false;
    }
}

async function triggerAutomaticUpdate($panel) {
    logDebug(`检查是否需要更新。总消息数: ${state.allChatMessages.length}, 自动更新启用: ${state.autoUpdateEnabled}`);
    if (!isCwbEnabled()) {
        logDebug('更新检查已跳过 - CharacterWorldBook总开关已关闭。');
        return;
    }
    if (!state.autoUpdateEnabled || isUpdatingCard || !state.customApiConfig.url || !state.customApiConfig.model || state.allChatMessages.length === 0) {
        logDebug('更新检查已跳过（未启用、正在更新、未配置或无消息）。');
        return;
    }

    let maxEndFloorInLorebook = 0;
    try {
        const context = SillyTavern.getContext();
        if (!context || !context.characterId) {
            logDebug('角色上下文未准备好，跳过自动更新的世界书检查。');
            return;
        }
        const bookName = await getTargetWorldBook();
        if (bookName) {
            const entries = (await safeLorebookEntries(bookName)) || [];
            const cleanChatId = state.currentChatFileIdentifier.replace(/ imported/g, '');
            const rosterEntry = entries.find(e => 
                Array.isArray(e.keys) &&
                e.keys.includes('Amily2角色总集') &&
                e.keys.includes(cleanChatId)
            );

            if (rosterEntry && rosterEntry.content) {
                const floorMatch = rosterEntry.content.match(/【前(\d+)楼角色世界书已更新完成】/);
                if (floorMatch && floorMatch[1]) {
                    maxEndFloorInLorebook = parseInt(floorMatch[1], 10);
                } else {
                    // Fallback for older entries
                    const floorRangeKey = rosterEntry.keys.find(k => /^\d+-\d+$/.test(k));
                    if (floorRangeKey) {
                        maxEndFloorInLorebook = parseInt(floorRangeKey.split('-')[1], 10);
                    }
                }
            }
        }
    } catch (e) {
        logError('从世界书获取最大结束楼层时出错:', e);
    }

    const unupdatedCount = state.allChatMessages.length - maxEndFloorInLorebook;
    logDebug(`未更新消息数: ${unupdatedCount} (阈值: ${state.autoUpdateThreshold}). 上次更新楼层: ${maxEndFloorInLorebook}.`);

    if (unupdatedCount >= state.autoUpdateThreshold) {
        showToastr('info', `检测到 ${unupdatedCount} 条新消息，将自动更新角色卡。`);
        const messagesToUse = state.allChatMessages.slice(maxEndFloorInLorebook);
        isUpdatingCard = true;
        await proceedWithCardUpdate($panel, messagesToUse);
        isUpdatingCard = false;
    }
}

export async function getLatestChatName() {
    let attempts = 0;
    const maxAttempts = 50;
    const interval = 100;

    while (attempts < maxAttempts) {
        const context = getContext();
        if (context && context.chatId) {
            return context.chatId;
        }
        await new Promise((resolve) => setTimeout(resolve, interval));
        attempts++;
    }

    logError("[CWB] 长时间等待后，仍无法确定聊天ID。");
    return "unknown_chat_timeout";
}

export async function handleMessageReceived($panel) {
    if (!isCwbEnabled('消息接收处理')) {
        return;
    }
    
    const context = SillyTavern.getContext();
    if (!context || !context.chat || !context.chat.length === 0) return;
    const latestMessage = context.chat[context.chat.length - 1];
    if (!latestMessage || latestMessage.is_user) {
        return;
    }

    await loadAllChatMessages($panel);
    await triggerAutomaticUpdate($panel);
}

export async function resetScriptStateForNewChat($panel, newChatName) {
    logDebug(`为新聊天重置脚本状态: "${newChatName}"`);
    state.allChatMessages = [];
    state.currentChatFileIdentifier = newChatName || 'unknown_chat_fallback';

    await loadAllChatMessages($panel);

    logDebug('状态重置完成。');
}

function updateBatchButtonState($panel, state, batchNum = 0, attemptNum = 0) {
    if (!$panel || !$panel.length) return;
    
    const $button = $panel.find('#cwb-batch-update-card');
    const $progress = $panel.find('#cwb-batch-progress');
    
    if (!$button.length) return;

    switch (state) {
        case 'processing':
            let attemptText = attemptNum > 0 ? ` (尝试 ${attemptNum + 1})` : '';
            $button.text(`点击停止 (${batchNum}/${totalBatchesNum})${attemptText}`);
            $button.prop('disabled', false);
            $progress.show().text(`正在处理批次 ${batchNum}/${totalBatchesNum}...`);
            isBatchUpdating = true;
            break;
        case 'stopping':
            $button.text('正在停止...');
            $button.prop('disabled', true);
            $progress.text('正在停止批量更新...');
            break;
        case 'paused':
            $button.text('继续批量更新');
            $button.prop('disabled', false);
            $progress.text('批量更新已暂停，点击继续...');
            isBatchUpdating = true;
            break;
        case 'error':
            $button.text('继续批量更新 (出错)');
            $button.prop('disabled', false);
            $progress.text('批量更新出错，请检查后继续...');
            isBatchUpdating = true;
            break;
        case 'idle':
        default:
            $button.text('立即批量更新');
            $button.prop('disabled', false);
            $progress.hide();
            isBatchUpdating = false;
            currentBatchNum = 0;
            manualBatchStopRequested = false;
            break;
    }
}


function getMessagesForFloorRange(startFloor, endFloor) {
    if (!state.allChatMessages || state.allChatMessages.length === 0) {
        return [];
    }
    
    // 转换为0-based索引
    const startIndex = Math.max(0, startFloor - 1);
    const endIndex = Math.min(state.allChatMessages.length, endFloor);
    
    if (startIndex >= endIndex) {
        return [];
    }
    
    return state.allChatMessages.slice(startIndex, endIndex);
}


async function runBatchUpdateAttempt($panel, batchNum, attemptNum) {
    try {
        if (manualBatchStopRequested) {
            logDebug(`批次 ${batchNum} 在开始前被手动停止。`);
            updateBatchButtonState($panel, 'paused');
            return;
        }

        updateBatchButtonState($panel, 'processing', batchNum, attemptNum);
        
        const startFloor = (batchNum - 1) * state.autoUpdateThreshold + 1;
        const endFloor = Math.min(startFloor + state.autoUpdateThreshold - 1, state.allChatMessages.length);

        logDebug(`正在处理批次 ${batchNum}/${totalBatchesNum} (楼层 ${startFloor}-${endFloor}, 尝试 ${attemptNum + 1}/${MAX_BATCH_RETRIES + 1})`);

        const messagesToProcess = getMessagesForFloorRange(startFloor, endFloor);
        if (!messagesToProcess || messagesToProcess.length === 0) {
            throw new Error('指定范围内无有效消息可处理。');
        }

        const success = await proceedWithCardUpdate($panel, messagesToProcess);
        if (!success) {
            throw new Error('角色卡更新失败。');
        }

        logDebug(`批次 ${batchNum} 处理成功。`);
        currentBatchNum = batchNum;

        setTimeout(() => processNextBatch($panel), 1000);

    } catch (error) {
        logError(`批次 ${batchNum} 尝试 ${attemptNum + 1} 失败: ${error.message}`);
        if (attemptNum >= MAX_BATCH_RETRIES) {
            logError(`批次 ${batchNum} 已达到最大重试次数，任务暂停。`);
            showToastr('error', `批次 ${batchNum} 多次失败，请检查网络或API设置后手动继续。`);
            currentBatchNum = batchNum - 1;
            updateBatchButtonState($panel, 'error');
        } else {
            logDebug(`将在3秒后自动重试批次 ${batchNum}...`);
            setTimeout(() => runBatchUpdateAttempt($panel, batchNum, attemptNum + 1), 3000);
        }
    }
}

async function processNextBatch($panel) {
    if (manualBatchStopRequested) {
        logDebug(`批次 ${currentBatchNum + 1} 在开始前被手动停止。`);
        updateBatchButtonState($panel, 'paused');
        return;
    }

    if (currentBatchNum >= totalBatchesNum) {
        logDebug('所有批次处理完毕！');
        showToastr('success', '批量更新完成！');
        updateBatchButtonState($panel, 'idle');
        return;
    }

    await runBatchUpdateAttempt($panel, currentBatchNum + 1, 0);
}

export async function startBatchUpdate($panel) {
    if (!isCwbEnabled()) {
        showToastr('warning', 'CharacterWorldBook总开关已关闭，无法执行批量更新。');
        return;
    }
    await loadAllChatMessages($panel);
    if (!state.customApiConfig.url || !state.customApiConfig.model) {
        showToastr('warning', '请先配置API信息。');
        return;
    }

    if (isBatchUpdating) {
        const $button = $panel.find('#cwb-batch-update-card');
        if ($button.text().startsWith('点击停止')) {
            manualBatchStopRequested = true;
            updateBatchButtonState($panel, 'stopping');
            logDebug('批量更新停止请求已发出！将在当前批次完成后暂停。');
        } else if ($button.text().startsWith('继续批量更新')) {
            manualBatchStopRequested = false;
            logDebug('从上次暂停处继续批量更新...');
            await processNextBatch($panel);
        }
        return;
    }

    manualBatchStopRequested = false;
    
    if (state.allChatMessages.length === 0) {
        showToastr('info', '当前没有聊天记录，无需更新。');
        return;
    }

    totalBatchesNum = Math.ceil(state.allChatMessages.length / state.autoUpdateThreshold);
    currentBatchNum = 0;

    logDebug(`准备开始批量更新任务，共 ${totalBatchesNum} 个批次。`);
    showToastr('info', `开始批量更新，共 ${totalBatchesNum} 个批次...`);
    
    await processNextBatch($panel);
}

export async function handleFloorRangeUpdate($panel) {
    if (!isCwbEnabled()) {
        showToastr('warning', 'CharacterWorldBook总开关已关闭，无法执行楼层范围更新。');
        return;
    }
    await loadAllChatMessages($panel);
    if (isUpdatingCard || isBatchUpdating) {
        showToastr('info', '已有更新任务在进行中。');
        return;
    }
    
    if (!state.customApiConfig.url || !state.customApiConfig.model) {
        showToastr('warning', '请先配置API信息。');
        return;
    }

    const startFloor = parseInt($panel.find('#cwb-start-floor').val(), 10);
    const endFloor = parseInt($panel.find('#cwb-end-floor').val(), 10);

    if (!startFloor || !endFloor || startFloor <= 0 || endFloor <= 0) {
        showToastr('warning', '请输入有效的楼层范围。');
        return;
    }

    if (startFloor > endFloor) {
        showToastr('warning', '起始楼层不能大于结束楼层。');
        return;
    }
    
    if (state.allChatMessages.length === 0) {
        showToastr('info', '当前没有聊天记录，无需更新。');
        return;
    }
    
    if (endFloor > state.allChatMessages.length) {
        showToastr('warning', `结束楼层 ${endFloor} 超出了当前聊天记录长度 ${state.allChatMessages.length}。`);
        return;
    }

    const messagesToProcess = getMessagesForFloorRange(startFloor, endFloor);
    if (!messagesToProcess || messagesToProcess.length === 0) {
        showToastr('warning', '指定楼层范围内没有有效内容可处理。');
        return;
    }

    isUpdatingCard = true;
    const $button = $panel.find('#cwb-floor-range-update');
    $button.prop('disabled', true).text('更新中...');

    try {
        logDebug(`开始处理楼层 ${startFloor}-${endFloor} 的内容...`);
        const success = await proceedWithCardUpdate($panel, messagesToProcess);
        
        if (success) {
            showToastr('success', `楼层 ${startFloor}-${endFloor} 更新完成！`);
        }
    } finally {
        isUpdatingCard = false;
        $button.prop('disabled', false).text('楼层范围更新');
    }
}

export async function manualUpdateLogic($panel = null) {
    if (!isCwbEnabled()) {
        logDebug('手动更新已跳过 - CharacterWorldBook总开关已关闭。');
        return;
    }
    if (isUpdatingCard) {
        showToastr('info', '已有更新任务在进行中。');
        return;
    }
    if (!state.customApiConfig.url || !state.customApiConfig.model) {
        showToastr('warning', '请先配置API信息。');
        return;
    }

    isUpdatingCard = true;
    await loadAllChatMessages($panel);
    const depth = state.scanDepth || state.autoUpdateThreshold || 6;
    const messagesToProcess = state.allChatMessages.slice(-depth);
    await proceedWithCardUpdate($panel, messagesToProcess);
    isUpdatingCard = false;

    logDebug('手动更新完成。');
}

export async function handleManualUpdateCard($panel) {
    const $button = $panel.find(`#${SCRIPT_ID_PREFIX}-manual-update-card`);
    $button.prop('disabled', true).text('更新中...');
    await manualUpdateLogic($panel);
    $button.prop('disabled', false).text('立即更新角色描述');
}

export async function handleLegacyFormatConversion($panel) {
    if (!isCwbEnabled()) {
        showToastr('warning', 'CharacterWorldBook总开关已关闭。');
        return;
    }

    const $button = $panel.find('#cwb-legacy-auto-update');
    $button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> 转换中...');

    try {
        const bookName = await getTargetWorldBook();
        if (!bookName) {
            showToastr('warning', '未找到目标世界书。');
            return;
        }

        const entries = await safeLorebookEntries(bookName);
        let updatedCount = 0;
        const entriesToUpdate = [];

        for (const entry of entries) {
            if (!entry.content || !entry.content.includes('[--Amily2::CHAR_START--]')) continue;

            try {
                const parsed = parseCustomFormat(entry.content);
                if (!parsed || Object.keys(parsed).length === 0) continue;

                let hasChanges = false;
                const newData = {};

                // Helper to rename keys
                const renameKey = (obj, oldKey, newKey) => {
                    if (obj[oldKey] !== undefined) {
                        obj[newKey] = obj[oldKey];
                        delete obj[oldKey];
                        return true;
                    }
                    return false;
                };

                // Helper to rename sub-keys
                const renameSubKeys = (parentObj, parentKey, mapping) => {
                    if (parentObj[parentKey]) {
                        let subChanged = false;
                        for (const [oldSub, newSub] of Object.entries(mapping)) {
                            if (renameKey(parentObj[parentKey], oldSub, newSub)) {
                                subChanged = true;
                            }
                        }
                        return subChanged;
                    }
                    return false;
                };

                // Copy parsed data to newData to avoid mutating original if needed (though parseCustomFormat returns new obj)
                Object.assign(newData, JSON.parse(JSON.stringify(parsed)));

                // 1. Rename Top Level Modules
                if (renameKey(newData, 'core_identity', 'CI')) hasChanges = true;
                if (renameKey(newData, 'physical_imprint', 'PI')) hasChanges = true;
                if (renameKey(newData, 'psyche_profile', 'PP')) hasChanges = true;
                if (renameKey(newData, 'social_matrix', 'SM')) hasChanges = true;
                if (renameKey(newData, 'narrative_essence', 'NE')) hasChanges = true;

                // 2. Rename Sub-keys
                // CI
                if (renameSubKeys(newData, 'CI', {
                    'archetype': 'arch',
                    'gender': 'gen',
                    'current_status': 'status'
                })) hasChanges = true;

                // PI
                if (renameSubKeys(newData, 'PI', {
                    'first_impression': 'first',
                    'key_features': 'feat',
                    'mannerisms': 'manner'
                })) hasChanges = true;

                // PP
                if (renameSubKeys(newData, 'PP', {
                    'description': 'desc',
                    'motivation': 'mot',
                    'values': 'val',
                    'inner_conflict': 'conf'
                })) hasChanges = true;

                // SM
                if (renameSubKeys(newData, 'SM', {
                    'interaction_style': 'style',
                    'skills': 'skill',
                    'reputation': 'rep'
                })) hasChanges = true;

                // NE
                if (newData.NE) {
                    // core_traits -> trait
                    if (newData.NE.core_traits) {
                        newData.NE.trait = newData.NE.core_traits.map(t => {
                            const newT = { ...t };
                            renameKey(newT, 'definition', 'def');
                            renameKey(newT, 'evidence', 'evid');
                            return newT;
                        });
                        delete newData.NE.core_traits;
                        hasChanges = true;
                    }

                    // verbal_patterns -> verb
                    if (newData.NE.verbal_patterns) {
                        newData.NE.verb = { ...newData.NE.verbal_patterns };
                        delete newData.NE.verbal_patterns;
                        renameKey(newData.NE.verb, 'style_summary', 'style');
                        renameKey(newData.NE.verb, 'quotes', 'quote');
                        hasChanges = true;
                    }

                    // key_relationships -> rel
                    if (newData.NE.key_relationships) {
                        newData.NE.rel = newData.NE.key_relationships.map(r => {
                            const newR = { ...r };
                            renameKey(newR, 'summary', 'sum');
                            return newR;
                        });
                        delete newData.NE.key_relationships;
                        hasChanges = true;
                    }
                }

                if (hasChanges) {
                    const newContent = buildCustomFormat(newData);
                    entriesToUpdate.push({
                        uid: entry.uid,
                        content: newContent
                    });
                    updatedCount++;
                }

            } catch (e) {
                logError(`转换条目失败 (UID: ${entry.uid}):`, e);
            }
        }

        if (updatedCount > 0) {
            await amilyHelper.setLorebookEntries(bookName, entriesToUpdate);
            showToastr('success', `成功转换了 ${updatedCount} 个旧版格式条目！`);
        } else {
            showToastr('info', '没有发现需要转换的旧版格式条目。');
        }

    } catch (error) {
        logError('旧版格式转换失败:', error);
        showToastr('error', `转换失败: ${error.message}`);
    } finally {
        $button.prop('disabled', false).html('<i class="fa-solid fa-history"></i> 旧版格式转换');
    }
}

export async function initializeCore($panel) {
    const initialChatName = await getLatestChatName();
    await resetScriptStateForNewChat($panel, initialChatName);
    logDebug('CWB 核心已初始化。基于事件的检查已激活。');
}
