# -*- coding: utf-8 -*-
"""
Helper script to patch script.js in Optimus Developer
"""
import re

target_path = r"C:\OptimusProjects\Optimus Developer\script.js"

with open(target_path, "r", encoding="utf-8") as f:
    text = f.read()

# 1. Update updateWorkspaceUI to NOT call resetChatToClean
old_ws = """    if (!currentWorkspace) {
        renderEmptyWorkspaceView();
        resetEditorToEmpty();
        resetChatToClean();
    }"""
new_ws = """    if (!currentWorkspace) {
        renderEmptyWorkspaceView();
        resetEditorToEmpty();
        // Preservar historial y conversacion activa: la ausencia de workspace no autoriza borrar el chat
    }"""
assert old_ws in text, "old_ws block not found"
text = text.replace(old_ws, new_ws, 1)

# 2. Update init() to NOT call resetChatToClean and always loadActiveConversation
old_init = """async function init() {
    await updateSystemStatus();
    if (currentWorkspace) {
        await loadFileTree();
        await loadActiveConversation();
        await checkAndResumeActiveMission();
    } else {
        renderEmptyWorkspaceView();
        resetEditorToEmpty();
        resetChatToClean();
    }
    await loadLedgerEvents();"""

new_init = """async function init() {
    await updateSystemStatus();
    if (currentWorkspace) {
        await loadFileTree();
    } else {
        renderEmptyWorkspaceView();
        resetEditorToEmpty();
    }
    // Preservar y cargar siempre la conversacion activa e inspeccionar misiones independientemente de workspace
    await loadActiveConversation();
    await checkAndResumeActiveMission();
    await loadLedgerEvents();"""

assert old_init in text, "old_init block not found"
text = text.replace(old_init, new_init, 1)

# 3. Add persistence helpers for active conversation ID and update loadActiveConversation
old_load_conv = """async function loadActiveConversation() {
    try {
        const res = await fetch(`${API_BASE}/api/conversations/active`);
        const data = await res.json();
        if (data.status === 'ok' && data.conversation) {
            const conv = data.conversation;
            activeConversationId = conv.id || conv.conversation_id;
            renderChatMessages(conv.messages || []);
        }

        const mRes = await fetch(`${API_BASE}/api/mission/active`);
        const mData = await mRes.json();
        if (mData.status === 'ok' && mData.mission) {
            updateMissionCard(mData.mission);
        } else {
            clearMissionCard();
        }
    } catch (e) {
        console.warn('Error loading active conversation:', e);
    }
}"""

new_load_conv = """function saveActiveConversationId(id) {
    if (id) {
        try { localStorage.setItem('optimus_active_conversation_id', id); } catch(e) {}
    } else {
        try { localStorage.removeItem('optimus_active_conversation_id'); } catch(e) {}
    }
}

function loadSavedConversationId() {
    try { return localStorage.getItem('optimus_active_conversation_id'); } catch(e) { return null; }
}

async function loadActiveConversation() {
    try {
        const savedId = loadSavedConversationId();
        const res = await fetch(`${API_BASE}/api/conversations/active`);
        let data = await res.json();

        // If backend active is empty but client has a saved active conversation id, fetch it directly
        if ((!data.conversation || data.conversation === null) && savedId) {
            try {
                const directRes = await fetch(`${API_BASE}/api/conversations/${encodeURIComponent(savedId)}`);
                if (directRes.ok) {
                    const directData = await directRes.json();
                    if (directData.status === 'ok' && directData.conversation) {
                        data = directData;
                    }
                }
            } catch (errConv) {}
        }

        if (data.status === 'ok' && data.conversation) {
            const conv = data.conversation;
            activeConversationId = conv.id || conv.conversation_id;
            saveActiveConversationId(activeConversationId);
            renderChatMessages(conv.messages || []);
        }

        const mRes = await fetch(`${API_BASE}/api/mission/active`);
        const mData = await mRes.json();
        if (mData.status === 'ok' && mData.mission) {
            updateMissionCard(mData.mission);
        } else {
            clearMissionCard();
        }
    } catch (e) {
        console.warn('Error loading active conversation:', e);
    }
}"""

assert old_load_conv in text, "old_load_conv block not found"
text = text.replace(old_load_conv, new_load_conv, 1)

# 4. Update deduplication in appendMessageBubble: do NOT use text substring
# Replace deduplication key logic
old_dedup = """    // Deduplication key
    const cleanSender = String(sender || 'Optimus').trim();
    const cleanText = String(text || '').trim();
    const key = msgId || (metadata && (metadata.id || metadata.message_id)) || `${cleanSender}_${cleanText.substring(0, 80)}`;

    if (key && renderedMessageKeys.has(key)) {
        return; // Avoid duplicate bubble
    }
    if (key) {
        renderedMessageKeys.add(key);
    }"""

new_dedup = """    const cleanSender = String(sender || 'Optimus').trim();
    const cleanText = String(text || '').trim();

    // Message identity: use stable message_id, metadata ID, or unique dispatch identity
    // CRITICAL: Do NOT use textual content as identity (prevents hiding legitimate identical messages)
    const key = msgId || (metadata && (metadata.id || metadata.message_id)) || 
        `dispatch_${Date.now()}_${++localDispatchSeq}_${Math.random().toString(36).substring(2, 7)}`;

    if (renderedMessageKeys.has(key)) {
        return; // Avoid duplicate bubble from repeated polling of the same message_id
    }
    renderedMessageKeys.add(key);"""

assert old_dedup in text, "old_dedup block not found"
text = text.replace(old_dedup, new_dedup, 1)

# Also ensure localDispatchSeq is declared right before appendMessageBubble or at renderedMessageKeys
old_keys_decl = "const renderedMessageKeys = new Set();"
new_keys_decl = "const renderedMessageKeys = new Set();\nlet localDispatchSeq = 0;"
assert old_keys_decl in text, "old_keys_decl not found"
text = text.replace(old_keys_decl, new_keys_decl, 1)

# 5. In sendPromptToBrain: assign a dispatch ID to Creator prompt
old_send_bubble = "    chatPromptInput.value = '';\n    appendMessageBubble('Creator', text);"
new_send_bubble = "    chatPromptInput.value = '';\n    const clientMsgId = 'client_msg_' + Date.now() + '_' + Math.random().toString(36).substring(2, 8);\n    appendMessageBubble('Creator', text, null, null, clientMsgId);"
assert old_send_bubble in text, "old_send_bubble not found"
text = text.replace(old_send_bubble, new_send_bubble, 1)

# 6. In selectConversation and btnNewChat: save conversation id and cleanly reset bubbles on explicit switch
old_select = """        if (data.status === 'ok' && data.conversation) {
            activeConversationId = data.conversation.id;
            renderChatMessages(data.conversation.messages || []);
            showToast(`Conversaci\u00f3n activa: ${data.conversation.title || id}`);
        }"""
# Wait, check encoding for 'Conversación'
pattern_select = re.compile(r"""if \(data\.status === 'ok' && data\.conversation\) \{\s+activeConversationId = data\.conversation\.id;\s+renderChatMessages\(data\.conversation\.messages \|\| \[\]\);\s+showToast\(`Conversaci[^`]+activa: \$\{data\.conversation\.title \|\| id\}`\);\s+\}""")
match_sel = pattern_select.search(text)
if match_sel:
    new_sel_code = """if (data.status === 'ok' && data.conversation) {
            activeConversationId = data.conversation.id;
            saveActiveConversationId(activeConversationId);
            if (chatMessages) chatMessages.innerHTML = '';
            renderedMessageKeys.clear();
            renderChatMessages(data.conversation.messages || []);
            showToast(`Conversacion activa: ${data.conversation.title || id}`);
        }"""
    text = text[:match_sel.start()] + new_sel_code + text[match_sel.end():]
    print("selectConversation updated")
else:
    print("WARNING: selectConversation pattern not matched")

pattern_newchat = re.compile(r"""if \(data\.status === 'ok' && data\.conversation\) \{\s+activeConversationId = data\.conversation\.id;\s+renderChatMessages\(data\.conversation\.messages \|\| \[\]\);\s+showToast\(`Nueva conversaci[^`]+iniciada: \$\{title\}`\);\s+\}""")
match_new = pattern_newchat.search(text)
if match_new:
    new_newchat_code = """if (data.status === 'ok' && data.conversation) {
                activeConversationId = data.conversation.id;
                saveActiveConversationId(activeConversationId);
                if (chatMessages) chatMessages.innerHTML = '';
                renderedMessageKeys.clear();
                clearMissionCard();
                renderChatMessages(data.conversation.messages || []);
                showToast(`Nueva conversacion iniciada: ${title}`);
            }"""
    text = text[:match_new.start()] + new_newchat_code + text[match_new.end():]
    print("btnNewChat updated")
else:
    print("WARNING: btnNewChat pattern not matched")

# Also in resetChatToClean:
old_reset_clean = """function resetChatToClean() {
    activeConversationId = null;
    renderedMessageKeys.clear();"""
new_reset_clean = """function resetChatToClean() {
    activeConversationId = null;
    saveActiveConversationId(null);
    renderedMessageKeys.clear();"""
assert old_reset_clean in text, "old_reset_clean not found"
text = text.replace(old_reset_clean, new_reset_clean, 1)

with open(target_path, "w", encoding="utf-8") as f:
    f.write(text)

print("SUCCESS: script.js successfully patched!")
