// Story Summary - Prompt Injection
// 注入格式：世界状态 → 亲身经历(含闪回) → 相关背景(含闪回) → 记忆碎片 → 人物弧光

import { getContext } from "../../../../../../extensions.js";
import { extension_prompts, extension_prompt_types, extension_prompt_roles } from "../../../../../../../script.js";
import { xbLog } from "../../../core/debug-core.js";
import { getSummaryStore } from "../data/store.js";
import { getVectorConfig, getSummaryPanelConfig, getSettings } from "../data/config.js";
import { recallMemory, buildQueryText } from "../vector/recall.js";

const MODULE_ID = "summaryPrompt";
const SUMMARY_PROMPT_KEY = "LittleWhiteBox_StorySummary";

const BUDGET = { total: 10000, l3Max: 2000, l2Max: 5000 };
const MAX_CHUNKS_PER_EVENT = 2;
const MAX_ORPHAN_CHUNKS = 6;

// ═══════════════════════════════════════════════════════════════════════════
// 工具函数
// ═══════════════════════════════════════════════════════════════════════════

function estimateTokens(text) {
    if (!text) return 0;
    const s = String(text);
    const zh = (s.match(/[\u4e00-\u9fff]/g) || []).length;
    return Math.ceil(zh + (s.length - zh) / 4);
}

function pushWithBudget(lines, text, state) {
    const t = estimateTokens(text);
    if (state.used + t > state.max) return false;
    lines.push(text);
    state.used += t;
    return true;
}

// 从 summary 解析楼层范围：(#321-322) 或 (#321)
function parseFloorRange(summary) {
    if (!summary) return null;
    
    // 匹配 (#123-456) 或 (#123)
    const match = String(summary).match(/\(#(\d+)(?:-(\d+))?\)/);
    if (!match) return null;
    
    const start = parseInt(match[1], 10);
    const end = match[2] ? parseInt(match[2], 10) : start;
    
    return { start, end };
}

// 去掉 summary 末尾的楼层标记
function cleanSummary(summary) {
    return String(summary || '').replace(/\s*\(#\d+(?:-\d+)?\)\s*$/, '').trim();
}

// ═══════════════════════════════════════════════════════════════════════════
// L1 → L2 归属
// ═══════════════════════════════════════════════════════════════════════════

function attachChunksToEvents(events, chunks) {
    const usedChunkIds = new Set();
    
    // 给每个 event 挂载 chunks
    for (const e of events) {
        e._chunks = [];
        const range = parseFloorRange(e.event?.summary);
        if (!range) continue;
        
        for (const c of chunks) {
            if (c.floor >= range.start && c.floor <= range.end) {
                if (!usedChunkIds.has(c.chunkId)) {
                    e._chunks.push(c);
                    usedChunkIds.add(c.chunkId);
                }
            }
        }
        
        // 每个事件最多保留 N 条，按相似度排序
        e._chunks.sort((a, b) => (b.similarity || 0) - (a.similarity || 0));
        e._chunks = e._chunks.slice(0, MAX_CHUNKS_PER_EVENT);
    }
    
    // 找出无归属的 chunks（记忆碎片）
    const orphans = chunks
        .filter(c => !usedChunkIds.has(c.chunkId))
        .sort((a, b) => (b.similarity || 0) - (a.similarity || 0))
        .slice(0, MAX_ORPHAN_CHUNKS);
    
    return { events, orphans };
}

// ═══════════════════════════════════════════════════════════════════════════
// 格式化函数
// ═══════════════════════════════════════════════════════════════════════════

function formatWorldLines(world) {
    return [...(world || [])]
        .sort((a, b) => (b.floor || 0) - (a.floor || 0))
        .map(w => `- ${w.topic}：${w.content}`);
}

function formatChunkLine(c) {
    const text = String(c.text || '');
    const preview = text.length > 80 ? text.slice(0, 80) + '...' : text;
    return `› #${c.floor} ${preview}`;
}

function formatEventBlock(e, idx) {
    const ev = e.event || {};
    const time = ev.timeLabel || '';
    const people = (ev.participants || []).join(' / ');
    const summary = cleanSummary(ev.summary);
    
    const lines = [];
    
    // 标题行
    const header = time ? `${idx}.【${time}】${people}` : `${idx}. ${people}`;
    lines.push(header);
    
    // 摘要
    lines.push(`  ${summary}`);
    
    // 挂载的闪回
    for (const c of (e._chunks || [])) {
        lines.push(`  ${formatChunkLine(c)}`);
    }
    
    return lines.join('\n');
}

function formatArcLine(a) {
    const moments = (a.moments || [])
        .map(m => typeof m === 'string' ? m : m.text)
        .filter(Boolean);
    
    if (moments.length) {
        return `- ${a.name}：${moments.join(' → ')}（当前：${a.trajectory}）`;
    }
    return `- ${a.name}：${a.trajectory}`;
}

// ═══════════════════════════════════════════════════════════════════════════
// 主构建函数
// ═══════════════════════════════════════════════════════════════════════════

function buildMemoryPromptVectorEnabled(store, recallResult) {
    const data = store.json || {};
    const total = { used: 0, max: BUDGET.total };
    const sections = [];
    
    // ─────────────────────────────────────────────────────────────────────
    // [世界状态]
    // ─────────────────────────────────────────────────────────────────────
    const worldLines = formatWorldLines(data.world);
    if (worldLines.length) {
        const l3 = { used: 0, max: Math.min(BUDGET.l3Max, total.max) };
        const l3Lines = [];
        
        for (const line of worldLines) {
            if (!pushWithBudget(l3Lines, line, l3)) break;
        }
        
        if (l3Lines.length) {
            sections.push(`[世界状态] 请严格遵守\n${l3Lines.join('\n')}`);
            total.used += l3.used;
        }
    }
    
    // ─────────────────────────────────────────────────────────────────────
    // L1 → L2 归属处理
    // ─────────────────────────────────────────────────────────────────────
    const events = recallResult?.events || [];
    const chunks = recallResult?.chunks || [];
    const { events: eventsWithChunks, orphans } = attachChunksToEvents(events, chunks);
    
    // 分离 DIRECT 和 SIMILAR
    const directEvents = eventsWithChunks.filter(e => e._recallType === 'DIRECT');
    const similarEvents = eventsWithChunks.filter(e => e._recallType !== 'DIRECT');
    
    // ─────────────────────────────────────────────────────────────────────
    // [亲身经历] - DIRECT
    // ─────────────────────────────────────────────────────────────────────
    if (directEvents.length) {
        const l2 = { used: 0, max: Math.min(BUDGET.l2Max * 0.7, total.max - total.used) };
        const lines = [];
        
        let idx = 1;
        for (const e of directEvents) {
            const block = formatEventBlock(e, idx);
            if (!pushWithBudget(lines, block, l2)) break;
            idx++;
        }
        
        if (lines.length) {
            sections.push(`[亲身经历]\n\n${lines.join('\n\n')}`);
            total.used += l2.used;
        }
    }
    
    // ─────────────────────────────────────────────────────────────────────
    // [相关背景] - SIMILAR
    // ─────────────────────────────────────────────────────────────────────
    if (similarEvents.length) {
        const l2s = { used: 0, max: Math.min(BUDGET.l2Max * 0.3, total.max - total.used) };
        const lines = [];
        
        let idx = directEvents.length + 1;
        for (const e of similarEvents) {
            const block = formatEventBlock(e, idx);
            if (!pushWithBudget(lines, block, l2s)) break;
            idx++;
        }
        
        if (lines.length) {
            sections.push(`[相关背景]\n\n${lines.join('\n\n')}`);
            total.used += l2s.used;
        }
    }
    
    // ─────────────────────────────────────────────────────────────────────
    // [记忆碎片] - 无归属的 chunks
    // ─────────────────────────────────────────────────────────────────────
    if (orphans.length && total.used < total.max) {
        const l1 = { used: 0, max: total.max - total.used };
        const lines = [];
        
        for (const c of orphans) {
            const line = formatChunkLine(c);
            if (!pushWithBudget(lines, line, l1)) break;
        }
        
        if (lines.length) {
            sections.push(`[记忆碎片]\n${lines.join('\n')}`);
            total.used += l1.used;
        }
    }
    
    // ─────────────────────────────────────────────────────────────────────
    // [人物弧光]
    // ─────────────────────────────────────────────────────────────────────
    if (data.arcs?.length && total.used < total.max) {
        const arcLines = data.arcs.map(formatArcLine);
        const arcText = `[人物弧光]\n${arcLines.join('\n')}`;
        
        if (total.used + estimateTokens(arcText) <= total.max) {
            sections.push(arcText);
        }
    }
    
    // ─────────────────────────────────────────────────────────────────────
    // 组装
    // ─────────────────────────────────────────────────────────────────────
    if (!sections.length) return '';
    
    return `<剧情记忆>\n\n${sections.join('\n\n')}\n\n</剧情记忆>`;
}

function buildMemoryPromptVectorDisabled(store) {
    const data = store.json || {};
    const sections = [];
    
    // 世界状态
    if (data.world?.length) {
        const lines = formatWorldLines(data.world);
        sections.push(`[世界状态] 请严格遵守\n${lines.join('\n')}`);
    }
    
    // 全部事件（无召回，按时间）
    if (data.events?.length) {
        const lines = data.events.map((ev, i) => {
            const time = ev.timeLabel || '';
            const people = (ev.participants || []).join(' / ');
            const summary = cleanSummary(ev.summary);
            const header = time ? `${i + 1}.【${time}】${people}` : `${i + 1}. ${people}`;
            return `${header}\n  ${summary}`;
        });
        sections.push(`[剧情记忆]\n\n${lines.join('\n\n')}`);
    }
    
    // 弧光
    if (data.arcs?.length) {
        const lines = data.arcs.map(formatArcLine);
        sections.push(`[人物弧光]\n${lines.join('\n')}`);
    }
    
    if (!sections.length) return '';
    
    return `<剧情记忆>\n\n${sections.join('\n\n')}\n\n</剧情记忆>`;
}

// ═══════════════════════════════════════════════════════════════════════════
// 导出
// ═══════════════════════════════════════════════════════════════════════════

export function formatPromptWithMemory(store, recallResult) {
    const vectorCfg = getVectorConfig();
    return vectorCfg?.enabled
        ? buildMemoryPromptVectorEnabled(store, recallResult)
        : buildMemoryPromptVectorDisabled(store);
}

export async function recallAndInjectPrompt(excludeLastAi = false, postToFrame = null) {
    if (!getSettings().storySummary?.enabled) {
        delete extension_prompts[SUMMARY_PROMPT_KEY];
        return;
    }

    const { chat } = getContext();
    const store = getSummaryStore();

    if (!store?.json) {
        delete extension_prompts[SUMMARY_PROMPT_KEY];
        return;
    }

    const allEvents = store.json.events || [];
    const lastIdx = store.lastSummarizedMesId ?? 0;
    const length = chat?.length || 0;

    if (lastIdx >= length) {
        delete extension_prompts[SUMMARY_PROMPT_KEY];
        return;
    }

    const vectorCfg = getVectorConfig();
    let recallResult = { events: [], chunks: [] };

    if (vectorCfg?.enabled) {
        try {
            const queryText = buildQueryText(chat, 2, excludeLastAi);
            recallResult = await recallMemory(queryText, allEvents, vectorCfg, { excludeLastAi });
            postToFrame?.({ type: "RECALL_LOG", text: recallResult.logText || "" });
        } catch (e) {
            xbLog.error(MODULE_ID, "召回失败", e);
        }
    }

    injectPrompt(store, recallResult, chat);
}

export function updateSummaryExtensionPrompt() {
    if (!getSettings().storySummary?.enabled) {
        delete extension_prompts[SUMMARY_PROMPT_KEY];
        return;
    }

    const { chat } = getContext();
    const store = getSummaryStore();

    if (!store?.json || (store.lastSummarizedMesId ?? 0) >= (chat?.length || 0)) {
        delete extension_prompts[SUMMARY_PROMPT_KEY];
        return;
    }

    injectPrompt(store, { events: [], chunks: [] }, chat);
}

function injectPrompt(store, recallResult, chat) {
    const length = chat?.length || 0;

    let text = formatPromptWithMemory(store, recallResult);

    const cfg = getSummaryPanelConfig();
    if (cfg.trigger?.wrapperHead) {
        text = cfg.trigger.wrapperHead + "\n" + text;
    }
    if (cfg.trigger?.wrapperTail) {
        text = text + "\n" + cfg.trigger.wrapperTail;
    }

    if (!text.trim()) {
        delete extension_prompts[SUMMARY_PROMPT_KEY];
        return;
    }

    const lastIdx = store.lastSummarizedMesId ?? 0;
    let depth = length - lastIdx - 1;
    if (depth < 0) depth = 0;

    if (cfg.trigger?.forceInsertAtEnd) {
        depth = 10000;
    }

    extension_prompts[SUMMARY_PROMPT_KEY] = {
        value: text,
        position: extension_prompt_types.IN_CHAT,
        depth,
        role: extension_prompt_roles.ASSISTANT,
    };
}

export function clearSummaryExtensionPrompt() {
    delete extension_prompts[SUMMARY_PROMPT_KEY];
}
