let chatsCache = null;
const element = {};
let selectRequestId = 0;
let apiBaseUrl = '';
let userguidelink = '';
let userhelplink = '';
let messagesAbortController = null;
let chatSelectTimeout = null;
let currentPage = 1;
const pageSize = 15;
let isLoading = false;
let hasMore = true;
let messagePage = 1;
const messagePageSize = 15;
let isLoadingMessages = false;
let hasMoreMessages = true;
const pendingAIChats = new Set();
let currentMessageNavIndex = -1;
let inputHistory = [];
let inputHistoryIndex = null;
let inputHistoryDraft = '';
let stagedAttachmentFiles = [];
const Toast = Swal.mixin({
    toast: true,
    position: 'top-end',
    showConfirmButton: false,
    timer: 3000,
    didOpen: (toast) => {
        toast.addEventListener('mouseenter', Swal.stopTimer);
        toast.addEventListener('mouseleave', Swal.resumeTimer);
    }
});

function cacheDOM() {
    element.container = document.querySelector('.chatbot-container');
    element.chatsList = document.getElementById('chatsList');
    element.messagesContainer = document.getElementById('messagesContainer');
    element.messageInput = document.getElementById('messageInput');
    element.chatTitle = document.getElementById('chatTitle');
    element.editChatBtn = document.getElementById('editChatBtn');
    element.deleteChatBtn = document.getElementById('deleteChatBtn');
    element.newChatBtn = document.getElementById('newChatBtn');
    element.sendMessageBtn = document.getElementById('sendMessageBtn');
    element.searchChatsInput = document.getElementById('searchChatsInput');
    element.attachFileBtn = document.getElementById('attachFileBtn');
    element.attachFileInput = document.getElementById('attachFileInput');
    element.attachmentsBar = document.getElementById('attachmentsBar');
}

function autoResizeMessageInput() {
    if (!element.messageInput) return;

    const maxHeight = 200;
    element.messageInput.style.height = 'auto';
    const nextHeight = Math.min(element.messageInput.scrollHeight, maxHeight);
    element.messageInput.style.height = `${nextHeight}px`;
    element.messageInput.style.overflowY = element.messageInput.scrollHeight > maxHeight ? 'auto' : 'hidden';
}

function getMessageWrappers() {
    return element.messagesContainer
        ? Array.from(element.messagesContainer.querySelectorAll('.chatbot-message-wrapper'))
        : [];
}

function getUserMessageWrappers() {
    return getMessageWrappers().filter(w => w.querySelector('.chatbot-message.user'));
}

function resetMessageNav() {
    currentMessageNavIndex = -1;
    getMessageWrappers().forEach(w => w.classList.remove('chatbot-message-active-nav'));
}

function highlightNavMessage(wrappers, index) {
    getMessageWrappers().forEach(w => w.classList.remove('chatbot-message-active-nav'));
    wrappers[index].classList.add('chatbot-message-active-nav');
    wrappers[index].scrollIntoView({ block: 'center', behavior: 'smooth' });
}

function navigateMessages(direction) {
    const wrappers = getUserMessageWrappers();
    if (wrappers.length === 0) return false;

    let nextIndex;
    if (currentMessageNavIndex === -1) {
        nextIndex = direction < 0 ? wrappers.length - 1 : 0;
    } else {
        nextIndex = currentMessageNavIndex + direction;
    }

    if (nextIndex < 0 || nextIndex >= wrappers.length) return false;

    currentMessageNavIndex = nextIndex;
    highlightNavMessage(wrappers, nextIndex);
    return true;
}

function setInputHistory(messages) {
    inputHistory = (messages || [])
        .filter(m => m && m.role === 'user' && typeof m.content === 'string')
        .map(m => m.content);
    resetInputHistoryNav();
}

function resetInputHistoryNav() {
    inputHistoryIndex = null;
    inputHistoryDraft = '';
}

function setInputValue(value) {
    const el = element.messageInput;
    el.value = value;
    autoResizeMessageInput();
    const end = value.length;
    el.setSelectionRange(end, end);
    saveInputDraft();
}

function recallHistoryUp() {
    if (inputHistory.length === 0) return false;
    if (inputHistoryIndex === null) {
        inputHistoryDraft = element.messageInput.value;
        inputHistoryIndex = inputHistory.length - 1;
    } else if (inputHistoryIndex > 0) {
        inputHistoryIndex--;
    } else {
        return false;
    }
    setInputValue(inputHistory[inputHistoryIndex]);
    return true;
}

function recallHistoryDown() {
    if (inputHistoryIndex === null) return false;
    if (inputHistoryIndex < inputHistory.length - 1) {
        inputHistoryIndex++;
        setInputValue(inputHistory[inputHistoryIndex]);
    } else {
        const draft = inputHistoryDraft;
        resetInputHistoryNav();
        setInputValue(draft);
    }
    return true;
}

function draftKey(chatId) {
    return 'chatbot_draft_' + (chatId ?? getActiveChatId() ?? 'new');
}

function saveInputDraft() {
    try {
        localStorage.setItem(draftKey(), element.messageInput.value);
    } catch (e) {
        console.error('Failed to save input draft:', e);
    }
}

function restoreInputDraft() {
    let text = '';
    try {
        text = localStorage.getItem(draftKey()) || '';
    } catch (e) {
        console.error('Failed to restore input draft:', e);
    }
    element.messageInput.value = text;
    autoResizeMessageInput();
}

function clearInputDraft(chatId) {
    try {
        localStorage.removeItem(draftKey(chatId));
    } catch (e) {
        console.error('Failed to clear input draft:', e);
    }
}

function showLoader() {
    if (element.container) {
        element.container.classList.add('chatbot-loading');
    }

    let loader = document.getElementById('chatbot-loader');
    if (!loader) {
        loader = document.createElement('div');
        loader.id = 'chatbot-loader';
        loader.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 9999; text-align: center;';
        loader.innerHTML = '<div style="border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto;"></div><p style="margin-top: 10px; color: #666; font-family: Arial, sans-serif;">Loading chats...</p><style>@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); }}</style>';
        document.body.appendChild(loader);
    }
    loader.style.display = 'block';
}

function hideLoader() {
    if (element.container) {
        element.container.classList.remove('chatbot-loading');
    }

    const loader = document.getElementById('chatbot-loader');
    if (loader) {
        loader.style.display = 'none';
    }
}

function formatChatsFromApi(apiChats) {
    return (apiChats || []).map(chat => ({
        id: String(chat.id || chat.ID),
        title: chat.name || chat.Name || 'Untitled Chat',
        createdAt: chat.createdDate || chat.CreatedDate || new Date().toISOString(),
        updatedAt: chat.modifiedDate || chat.ModifiedDate || chat.createdDate || chat.CreatedDate || new Date().toISOString(),
        messages: []
    }));
}

async function getAllChats() {
    return chatsCache || [];
}

function getActiveChatId() {
    return localStorage.getItem('chatbot_active_chat') || null;
}


function setActiveChatId(chatId) {
    localStorage.setItem('chatbot_active_chat', chatId);
}

async function getOrganizationId() {
    return IndexLibrary.GetAccountId();
}

async function getProjectId() {
    return IndexLibrary.GetCurrentProjectId();
}

async function getUserEmail() {
    return IndexLibrary.GetUserEmail();
}

async function getAccessToken() {
    return IndexLibrary.GetAccessToken();
}

async function waitForIndexLibrary(maxAttempts = 50, intervalMs = 100) {
    for (let i = 0; i < maxAttempts; i++) {
        if (typeof IndexLibrary !== 'undefined') {
            return true;
        }
        await new Promise(resolve => setTimeout(resolve, intervalMs));
    }
    return false;
}

async function ensureSdkReady() {
    const isAvailable = await waitForIndexLibrary();
    if (isAvailable && typeof IndexLibrary.InitializeSDK === 'function') {
        await IndexLibrary.InitializeSDK();
    }
}

function generateChatId() {
    return 'chat_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}

function isApiChatId(chatId) {
    return /^\d+$/.test(String(chatId));
}

async function createChatOnApi(initialContext = '') {
    const organizationId = await getOrganizationId();
    const projectId = await getProjectId();

    const response = await IndexLibrary.PostRequest({
        endpoint: '/api/InChat/new',
        baseAPIUrl: apiBaseUrl,
        azureAuthenticationHeader: await getAccessToken(),
        organizationId: organizationId,
        headers: {
            'UserEmail': await getUserEmail()
        },
        body: {
            OrganizationId: organizationId,
            ProjectId: projectId,
            InitialContext: initialContext
        }
    });

    if (!response.ok) {
        const errorText = await response.text();
        console.error('API Error response:', errorText);
        throw new Error(`API Error: ${response.status} - ${response.statusText}`);
    }

    const apiChat = await response.json();
    console.log('API Chat response:', apiChat);

    const chatId = String(apiChat.chatId || apiChat.id || apiChat.chatID || apiChat.ID);
    const chatTitle = apiChat.chatName || apiChat.name || apiChat.title || 'New Chat';

    return {
        id: chatId,
        title: chatTitle,
        messages: [],
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
    };
}

async function replaceChatId(oldChatId, newChat) {
    const chats = await getAllChats();
    const chatIndex = chats.findIndex(chat => chat.id === oldChatId);

    if (chatIndex !== -1) {
        chats[chatIndex] = {
            ...chats[chatIndex],
            id: newChat.id,
            title: newChat.title || chats[chatIndex].title,
            createdAt: newChat.createdAt || chats[chatIndex].createdAt,
            updatedAt: newChat.updatedAt || chats[chatIndex].updatedAt
        };
        chatsCache = chats;
    }
}

async function deleteChatOnApi(chatId) {
    const response = await IndexLibrary.DeleteRequest({
        endpoint: `/api/InChat/${chatId}`,
        baseAPIUrl: apiBaseUrl,
        azureAuthenticationHeader: await getAccessToken(),
        organizationId: await getOrganizationId(),
        headers: {
            'UserEmail': await getUserEmail()
        }
    });

    if (!response.ok) {
        throw new Error(`API Error: ${response.status}`);
    }
}

async function getChatMessagesApi(chatId, pageNo = 1, pageSize = 10) {
    if (messagesAbortController) {
        messagesAbortController.abort();
    }
    messagesAbortController = new AbortController();
    const createdBy = await getUserEmail();
    try {
        const response = await IndexLibrary.GetRequest({
            endpoint: `/api/InChat/${chatId}/messages?pageno=${pageNo}&pagesize=${pageSize}&createdBy=${encodeURIComponent(createdBy)}`,
            baseAPIUrl: apiBaseUrl,
            azureAuthenticationHeader: await getAccessToken(),
            organizationId: await getOrganizationId(),
            headers: {},
            signal: messagesAbortController.signal
        });

        if (!response.ok) {
            throw new Error(`API Error: ${response.status}`);
        }

        const apiMessages = await response.json();
        return (apiMessages || []).map(msg => ({
            id: String(msg.id || msg.ID),
            role: msg.isAIResponse ?? msg.IsAIResponse ? 'assistant' : 'user',
            content: msg.messageContext || msg.MessageContext || '',
            attachments: msg.attachments || msg.Attachments || [],
            timestamp: msg.createdDate || msg.CreatedDate || new Date().toISOString()
        }));
    } catch (error) {
        if (error.name === 'AbortError') {
            console.log(`Aborted fetching messages for chat: ${chatId}`);
        }
        throw error;
    }
}

async function sendMessageApi(chatId, message, attachments = []) {
    const response = await IndexLibrary.PostRequest({
        endpoint: `/api/InChat/${chatId}/chat`,
        baseAPIUrl: apiBaseUrl,
        azureAuthenticationHeader: await getAccessToken(),
        organizationId: await getOrganizationId(),
        headers: {
            'UserEmail': await getUserEmail()
        },
        body: {
            MessageContext: message,
            azureDevOpsOrgName: await IndexLibrary.GetAccountNameAsync(),
            azureAccessToken: await getAccessToken(),
            attachments: attachments
        }
    });

    let responseData = null;
    try {
        responseData = await response.json();
    } catch (e) {
        responseData = null;
    }

    if (!response.ok) {
        const fallback = `API Error: ${response.status}`;
        const serverMessage = responseData?.errorMessage || responseData?.message || fallback;
        throw new Error(serverMessage);
    }

    if (responseData?.isError) {
        throw new Error(responseData.errorMessage || 'Unable to get AI response. Please try again.');
    }

    return responseData;
}

async function getAllChatsApi(page = 1, pageSize = 10) {
    const createdBy = await getUserEmail();
    const projectId = await getProjectId();
    const response = await IndexLibrary.GetRequest({
        endpoint: `/api/InChat?pageno=${page}&pagesize=${pageSize}&createdBy=${encodeURIComponent(createdBy)}&projectId=${encodeURIComponent(projectId)}`,
        baseAPIUrl: apiBaseUrl,
        azureAuthenticationHeader: await getAccessToken(),
        organizationId: await getOrganizationId()
    });

    if (!response.ok) {
        throw new Error(`API Error: ${response.status}`);
    }

    return response.json();
}

function showSkeletonMessageLoader() {
    const container = element.messagesContainer;

    if (document.getElementById('skeleton-msg-loader')) return;

    const loader = document.createElement('div');
    loader.id = 'skeleton-msg-loader';
    loader.className = 'chatbot-message assistant';

    loader.innerHTML = `
        <div class="chatbot-message-content">
            <span style="font-size:12px; color:#888;">Loading older messages</span>
            <span class="typing-dots">
                <span></span><span></span><span></span>
            </span>
        </div>
    `;

    container.insertBefore(loader, container.firstChild);
}

function hideSkeletonMessageLoader() {
    const loader = document.getElementById('skeleton-msg-loader');
    if (loader) loader.remove();
}

function showChatsSkeleton(count = 5) {
    const container = element.chatsList;

    let html = '';

    for (let i = 0; i < count; i++) {
        html += `
            <li class="chat-skeleton-item">
                <div class="chat-skeleton-line long"></div>
                <div class="chat-skeleton-line short"></div>
            </li>
        `;
    }

    container.insertAdjacentHTML('beforeend', html);
}
function hideChatsSkeleton() {
    document.querySelectorAll('.chat-skeleton-item')
        .forEach(el => el.remove());
}
async function searchChatsApi(name) {
    const createdBy = await getUserEmail();
    const projectId = await getProjectId();
    const response = await IndexLibrary.GetRequest({
        endpoint: `/api/InChat/search/${encodeURIComponent(name)}?createdBy=${encodeURIComponent(createdBy)}&projectId=${encodeURIComponent(projectId)}`,
        baseAPIUrl: apiBaseUrl,
        azureAuthenticationHeader: await getAccessToken(),
        organizationId: await getOrganizationId()
    });

    if (!response.ok) {
        throw new Error(`API Error: ${response.status}`);
    }

    return response.json();
}

async function updateChatApi(chatId, name, summaryContext) {
    const response = await IndexLibrary.PutRequest({
        endpoint: `/api/InChat/${chatId}`,
        baseAPIUrl: apiBaseUrl,
        azureAuthenticationHeader: await getAccessToken(),
        organizationId: await getOrganizationId(),
        headers: {
            'UserEmail': await getUserEmail()
        },
        body: {
            Name: name,
            SummaryContext: summaryContext
        }
    });

    if (!response.ok) {
        throw new Error(`API Error: ${response.status}`);
    }
}

function createLocalChat() {
    return {
        id: generateChatId(),
        title: 'New Chat',
        messages: [],
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
    };
}

async function createNewChat() {
    try {
        const localChat = createLocalChat();
        const chats = await getAllChats();
        chatsCache = [localChat, ...chats.filter(chat => chat.id !== localChat.id)];
        return localChat;
    } catch (e) {
        console.error('Local chat creation failed', e);
        throw e;
    }
}
async function getChatById(chatId) {
    const chats = await getAllChats();
    return chats.find(chat => chat.id === chatId) || null;
}

async function updateChat(chatId, updates) {
    const chats = await getAllChats();
    const chatIndex = chats.findIndex(chat => chat.id === chatId);

    if (chatIndex !== -1) {
        chats[chatIndex] = {
            ...chats[chatIndex],
            ...updates,
            updatedAt: new Date().toISOString()
        };
        chatsCache = chats;
    }
}

async function deleteChat(chatId) {
    if (isApiChatId(chatId)) {
        try {
            await deleteChatOnApi(chatId);
        } catch (e) {
            console.error('API chat deletion failed', e);
            throw e;
        }
    }

    IndexLibrary.ClearAttachmentUrlCache();

    const chats = await getAllChats();
    chatsCache = chats.filter(chat => chat.id !== chatId);

    if (getActiveChatId() === chatId) {
        setActiveChatId(null);
    }
}

async function addMessageToChat(chatId, role, content, extraData = null) {
    const chat = await getChatById(chatId);
    if (!chat) return;

    const message = {
        id: generateChatId(),
        role: role,
        content: content,
        timestamp: new Date().toISOString(),
        ...extraData
    };

    chat.messages.push(message);
    if (role === 'user' && chat.title === 'New Chat') {
        chat.title = content.substring(0, 50) + (content.length > 50 ? '...' : '');
    }

    await updateChat(chatId, chat);
    return message;
}

async function renderChatsList(chats) {
    if (!chats) {
        chats = await getAllChats();
    }

    if (chats.length === 0) {
        element.chatsList.innerHTML = '<li class="chatbot-empty-state"><p class="text-muted small">No chats yet. Start a new conversation!</p></li>';
        return;
    }

    const activeChatId = getActiveChatId();
    const html = chats.map(chat => `
        <li class="chatbot-chat-item ${chat.id === activeChatId ? 'active' : ''}" data-chat-id="${escapeHtml(chat.id)}">
            <div class="chatbot-chat-item-row">
                <div class="chatbot-chat-item-title-wrap">
                    <div class="chatbot-chat-item-title">${escapeHtml(chat.title)}</div>
                    <button class="chatbot-chat-menu-btn" data-chat-id="${escapeHtml(chat.id)}" title="Chat options" onclick="event.stopPropagation();">
                        <i class="bi bi-three-dots-vertical"></i>
                    </button>
                </div>
            </div>
            <div class="chatbot-chat-item-date">${formatDate(chat.updatedAt)}</div>
        </li>
    `).join('');

    element.chatsList.innerHTML = html;

    document.querySelectorAll('.chatbot-chat-menu-btn').forEach(btn => {
        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            const chatId = btn.dataset.chatId;
            showChatMenu(chatId, e);
        });
    });
}

function renderMessages(messages) {
    currentMessageNavIndex = -1;
    setInputHistory(messages);
    if (!messages || messages.length === 0) {
        element.messagesContainer.innerHTML = '<div class="chatbot-welcome"><p class="text-muted">Start a conversation...</p></div>';
        return;
    }

    const fragment = document.createDocumentFragment();
    messages.forEach(msg => {
        const wrapper = document.createElement('div');
        wrapper.className = 'chatbot-message-wrapper';

        const div = document.createElement('div');
        div.className = `chatbot-message ${msg.role}`;
        const content = document.createElement('div');
        content.className = 'chatbot-message-content';

        if (msg.role === 'assistant' && typeof marked !== 'undefined' && typeof marked.parse === 'function') {
            content.classList.add('chatbot-markdown');
            content.innerHTML = renderAssistantMarkdown(msg.content || '');
        } else if (msg.role === 'error') {
            content.className = 'chatbot-message-content error-message';

            const textSpan = document.createElement('span');
            textSpan.textContent = msg.content;
            content.appendChild(textSpan);

            const retryBtn = document.createElement('button');
            retryBtn.className = 'chatbot-retry-btn';
            retryBtn.title = 'Retry';
            retryBtn.innerHTML = '<i class="bi bi-arrow-clockwise"></i>';
            retryBtn.dataset.chatId = msg.retryData?.chatId || getActiveChatId();
            retryBtn.dataset.userMessage = msg.retryData?.userMessage || '';
            retryBtn.onclick = (e) => {
                e.stopPropagation();
                const cid = retryBtn.dataset.chatId;
                const umsg = retryBtn.dataset.userMessage;
                if (cid && umsg) {
                    const errorWrapper = wrapper;
                    const previousWrapper = errorWrapper?.previousElementSibling;

                    if (errorWrapper) {
                        errorWrapper.remove();
                    }
                    if (previousWrapper && previousWrapper.querySelector('.chatbot-message.user')) {
                        previousWrapper.remove();
                    }
                    retryMessage(cid, umsg);
                }
            };
            content.appendChild(retryBtn);
        } else {
            content.textContent = msg.content;
        }

        // Stacked in a column so the thumbnails sit above the bubble rather than beside it; the
        // message row itself is a flex row that handles left/right alignment of the whole message.
        const grid = buildAttachmentGrid(getActiveChatId(), msg.attachments);
        if (grid) {
            const stack = document.createElement('div');
            stack.className = 'chatbot-message-stack';
            stack.appendChild(grid);
            if (msg.content) stack.appendChild(content);
            div.appendChild(stack);
        } else {
            div.appendChild(content);
        }
        wrapper.appendChild(div);
        fragment.appendChild(wrapper);
    });

    element.messagesContainer.innerHTML = '';
    element.messagesContainer.appendChild(fragment);
    scrollToBottom();
}

function renderAssistantMarkdown(markdownText) {
    try {
        if (typeof DOMPurify === 'undefined') {
            return escapeHtml(markdownText);
        }
        const parsedHtml = marked.parse(markdownText);
        const temp = document.createElement('div');
        temp.innerHTML = DOMPurify.sanitize(parsedHtml, { USE_PROFILES: { html: true } });

        const links = temp.querySelectorAll('a');
        links.forEach(link => {
            link.setAttribute('target', '_blank');
            link.setAttribute('rel', 'noopener noreferrer');
        });

        const tables = temp.querySelectorAll('table');
        tables.forEach(table => {
            table.classList.add('chatbot-markdown-table');
            if (!table.parentElement || !table.parentElement.classList.contains('chatbot-table-wrap')) {
                const wrapper = document.createElement('div');
                wrapper.className = 'chatbot-table-wrap';
                table.parentNode.insertBefore(wrapper, table);
                wrapper.appendChild(table);
            }
        });

        return temp.innerHTML;
    } catch (e) {
        return escapeHtml(markdownText);
    }
}

function scrollToBottom() {
    requestAnimationFrame(() => {
        element.messagesContainer.scrollTop = element.messagesContainer.scrollHeight;
    });
}

function showTypingIndicator() {
    hideTypingIndicator();

    const wrapper = document.createElement('div');
    wrapper.id = 'chatbot-typing-indicator';
    wrapper.className = 'chatbot-message assistant typing';

    const content = document.createElement('div');
    content.className = 'chatbot-message-content';

    const text = document.createElement('span');
    text.className = 'chatbot-typing-text';
    text.textContent = 'Thinking';

    const dots = document.createElement('span');
    dots.className = 'chatbot-typing-dots';
    dots.innerHTML = '<span></span><span></span><span></span>';

    content.appendChild(text);
    content.appendChild(dots);
    wrapper.appendChild(content);
    element.messagesContainer.appendChild(wrapper);
    scrollToBottom();
}

function hideTypingIndicator() {
    const indicator = document.getElementById('chatbot-typing-indicator');
    if (indicator) {
        indicator.remove();
    }
}

function renderMessagesLoader() {
    element.messagesContainer.innerHTML = '<div class="chatbot-messages-loader"><div class="chatbot-messages-loader-spinner"></div><p>Loading messages...</p></div>';
}

async function selectChat(chatId) {
    const requestId = ++selectRequestId;
    const chat = await getChatById(chatId);
    if (!chat) return;

    hideTypingIndicator();
    setActiveChatId(chatId);
    clearStagedAttachments();
    restoreInputDraft();
    element.chatTitle.textContent = chat.title;
    if (element.editChatBtn) {
        element.editChatBtn.style.display = 'inline-flex';
    }
    element.deleteChatBtn.style.display = 'block';
    await renderChatsList();
    renderMessagesLoader();

    try {
        const hasPendingAI = pendingAIChats.has(chatId);
        const messages = isApiChatId(chatId) ? (await getChatMessagesApi(chatId, 1, messagePageSize)).reverse() : chat.messages;
        if (requestId !== selectRequestId) return;
        if (!hasPendingAI) {
            chat.messages = messages;
        }
        renderMessages(chat.messages);
        if (hasPendingAI) {
            showTypingIndicator();
        }
    } catch (e) {
        if (e.name === 'AbortError') {
            return;
        }
        if (requestId !== selectRequestId) return;
        console.warn('Failed to load chat messages from API, using local cache', e);
        renderMessages(chat.messages);
        if (pendingAIChats.has(chatId)) {
            showTypingIndicator();
        }
    }
}
async function handleNewChat() {
    try {
        element.newChatBtn.disabled = true;
        selectRequestId++;
        const chats = await getAllChats();
        const latestChat = chats[0];

        if (latestChat && latestChat.messages.length === 0 && latestChat.title === 'New Chat') {
            setActiveChatId(latestChat.id);
        } else {
            const chat = await createNewChat();
            setActiveChatId(chat.id);
        }

        element.chatTitle.textContent = 'New Chat';
        if (element.editChatBtn) {
            element.editChatBtn.style.display = 'inline-flex';
        }
        element.deleteChatBtn.style.display = 'none';
        element.messagesContainer.innerHTML = '<div class="chatbot-welcome"><p class="text-muted">Start a conversation...</p></div>';
        clearStagedAttachments();
        element.messageInput.value = '';
        autoResizeMessageInput();
        resetMessageNav();
        setInputHistory([]);

        await renderChatsList();
        focusMessageInput();
    } catch (e) {
        console.error('Error creating new chat:', e);
        IndexLibrary.ShowToast('error', `Failed to create new chat: ${e.message}`);
    } finally {
        element.newChatBtn.disabled = false;
    }
}
async function deleteChatItem(chatId) {
    const result = await Swal.fire({
        title: 'Are you sure?',
        text: 'This will Delete the Chat',
        icon: 'warning',
        showCancelButton: true,
        confirmButtonColor: '#3085d6',
        cancelButtonColor: '#d33',
        confirmButtonText: 'Yes'
    });

    if (!result.isConfirmed) return;

    try {
        const chats = await getAllChats();
        chatsCache = chats.filter(chat => chat.id !== chatId);

        const remainingChats = await getAllChats();

        if (remainingChats.length > 0) {
            await selectChat(remainingChats[0].id);
        } else {
            element.chatTitle.textContent = '';
            setActiveChatId(null);
            if (element.editChatBtn) {
                element.editChatBtn.style.display = 'none';
            }
            element.deleteChatBtn.style.display = 'none';
            element.messagesContainer.innerHTML = '<div class="chatbot-welcome"><p class="text-muted">Start a new conversation!</p></div>';
        }

        await renderChatsList();

        if (isApiChatId(chatId)) {
            deleteChatOnApi(chatId).catch(e => {
                console.error('Failed to delete chat from backend:', e);
            });
        }

        Toast.fire({
            icon: 'success',
            title: 'Chat deleted successfully'
        });
    } catch (e) {
        console.error('Error deleting chat', e);
        Toast.fire({
            icon: 'error',
            title: 'Failed to delete chat'
        });
    }
}

async function editChatName(chatId) {
    const chat = await getChatById(chatId);
    if (!chat) return;

    const result = await Swal.fire({
        title: 'Edit chat name',
        input: 'text',
        inputValue: chat.title || '',
        showCancelButton: true,
        confirmButtonText: 'OK',
        cancelButtonText: 'Cancel',
        inputValidator: (value) => {
            if (!value || !value.trim()) {
                return 'Chat name is required';
            }
        }
    });

    if (!result.isConfirmed) return;

    const newName = result.value.trim();

    try {
        if (isApiChatId(chatId)) {
            await updateChatApi(chatId, newName, '');
        }

        await updateChat(chatId, { title: newName });

        if (getActiveChatId() === chatId) {
            element.chatTitle.textContent = newName;
        }

        await renderChatsList();

        Toast.fire({
            icon: 'success',
            title: 'Chat renamed successfully'
        });
    } catch (e) {
        console.error('Error updating chat name:', e);
        Toast.fire({
            icon: 'error',
            title: 'Failed to update chat name'
        });
    }
}

/* ========================== ATTACHMENTS ========================== */

function clearStagedAttachments() {
    stagedAttachmentFiles = [];
    renderStagedAttachments();
}

function onAttachFilesSelected(e) {
    const files = Array.from(e.target.files || []);
    element.attachFileInput.value = '';

    for (const file of files) {
        if (!IndexLibrary.IsAcceptedAttachment(file)) {
            IndexLibrary.ShowToast('error', `${file.name} is not a supported type (.md or an image).`);
            continue;
        }

        if (stagedAttachmentFiles.length >= IndexLibrary.MAX_ATTACHMENTS_PER_MESSAGE) {
            IndexLibrary.ShowToast('error',
                `You can attach at most ${IndexLibrary.MAX_ATTACHMENTS_PER_MESSAGE} files to a message.`);
            break;
        }

        stagedAttachmentFiles.push(file);
    }

    renderStagedAttachments();
}

function removeStagedAttachment(file) {
    stagedAttachmentFiles = stagedAttachmentFiles.filter(f => f !== file);
    renderStagedAttachments();
}

function renderStagedAttachments() {
    if (!element.attachmentsBar) return;

    element.attachmentsBar.innerHTML = '';

    if (stagedAttachmentFiles.length === 0) {
        element.attachmentsBar.style.display = 'none';
        if (element.attachFileBtn) element.attachFileBtn.disabled = false;
        return;
    }

    element.attachmentsBar.style.display = 'flex';

    if (element.attachFileBtn) {
        element.attachFileBtn.disabled =
            stagedAttachmentFiles.length >= IndexLibrary.MAX_ATTACHMENTS_PER_MESSAGE;
    }

    stagedAttachmentFiles.forEach(file => {
        const node = document.createElement('span');
        node.className = 'chatbot-chip';

        const icon = document.createElement('i');
        icon.className = IndexLibrary.IsImageAttachment(file.name) ? 'bi bi-image' : 'bi bi-file-earmark-text';
        node.appendChild(icon);

        const label = document.createElement('span');
        label.className = 'chatbot-chip-name';
        label.textContent = file.name;
        label.title = file.name;
        node.appendChild(label);

        const remove = document.createElement('button');
        remove.type = 'button';
        remove.className = 'chatbot-chip-remove';
        remove.innerHTML = '<i class="bi bi-x"></i>';
        remove.title = 'Remove attachment';
        remove.addEventListener('click', () => removeStagedAttachment(file));
        node.appendChild(remove);

        element.attachmentsBar.appendChild(node);
    });
}

/**
 * A thumbnail the user sees immediately, backed by the local file rather than a round-trip. Replaced
 * with the stored attachment once the server confirms the message.
 */
function buildLocalPreview(file) {
    return {
        localUrl: URL.createObjectURL(file),
        fileName: file.name,
        isImage: IndexLibrary.IsImageAttachment(file.name)
    };
}

async function replaceLocalAttachments(chatId, localMessageId, serverMessage) {
    if (!localMessageId || !serverMessage) return;

    const attachments = serverMessage.attachments || serverMessage.Attachments;
    if (!attachments || attachments.length === 0) return;

    const chat = await getChatById(chatId);
    const message = chat?.messages?.find(m => m.id === localMessageId);
    if (!message) return;

    (message.attachments || []).forEach(a => {
        if (a.localUrl) URL.revokeObjectURL(a.localUrl);
    });

    message.attachments = attachments;
    await updateChat(chatId, chat);
}

async function attachmentContext() {
    return {
        baseAPIUrl: apiBaseUrl,
        azureAuthenticationHeader: await getAccessToken(),
        organizationId: await getOrganizationId(),
        userEmail: await getUserEmail()
    };
}

/**
 * Renders an attached image inside a message bubble. The blob URL is only fetched once the thumbnail
 * is near the viewport, so opening a long conversation does not request every image it ever held.
 */
function buildAttachmentTile(chatId, attachment, uniform) {
    const id = attachment.id ?? attachment.Id;
    const fileName = attachment.fileName || attachment.FileName || 'attachment';
    const isImage = attachment.isImage ?? attachment.IsImage ?? IndexLibrary.IsImageAttachment(fileName);

    if (!isImage) {
        const file = document.createElement('span');
        file.className = 'chatbot-attachment-file';
        file.innerHTML = '<i class="bi bi-file-earmark-text"></i>';
        const name = document.createElement('span');
        name.textContent = fileName;
        file.appendChild(name);
        file.title = fileName;
        return file;
    }

    const tile = document.createElement('button');
    tile.type = 'button';
    tile.className = 'chatbot-attachment-tile';
    tile.title = fileName;

    const width = attachment.width ?? attachment.Width;
    const height = attachment.height ?? attachment.Height;

    if (!uniform && width && height) {
        tile.style.aspectRatio = `${width} / ${height}`;
    }

    const img = document.createElement('img');
    img.loading = 'lazy';
    img.decoding = 'async';
    img.alt = fileName;
    tile.appendChild(img);

    const show = (url) => {
        img.src = url;
        tile.classList.add('loaded');
    };

    if (attachment.localUrl) {
        show(attachment.localUrl);
        tile.addEventListener('click', () => openAttachmentLightbox(attachment.localUrl, fileName));
        return tile;
    }

    tile.addEventListener('click', () => {
        if (img.src) openAttachmentLightbox(img.src, fileName);
    });

    const cachedUrl = IndexLibrary.GetCachedAttachmentUrl(chatId, id);
    if (cachedUrl) {
        show(cachedUrl);
        return tile;
    }

    let requested = false;
    const load = async () => {
        if (requested) return;
        requested = true;

        try {
            const url = await IndexLibrary.GetAttachmentUrl(await attachmentContext(), chatId, id);
            if (url) {
                show(url);
            } else {
                tile.classList.add('failed');
            }
        } catch (error) {
            console.warn('[Chat] could not load attachment', fileName, error);
            tile.classList.add('failed');
        }
    };

    if (typeof IntersectionObserver === 'function') {
        const observer = new IntersectionObserver(entries => {
            if (entries.some(entry => entry.isIntersecting)) {
                observer.disconnect();
                load();
            }
        }, { rootMargin: '400px' });
        observer.observe(tile);
    } else {
        load();
    }

    return tile;
}

function buildAttachmentGrid(chatId, attachments) {
    if (!attachments || attachments.length === 0) return null;

    const images = attachments.filter(a =>
        a.isImage ?? a.IsImage ?? IndexLibrary.IsImageAttachment(a.fileName || a.FileName || ''));
    const files = attachments.filter(a => !images.includes(a));

    const grid = document.createElement('div');
    const count = Math.min(attachments.length, IndexLibrary.MAX_ATTACHMENTS_PER_MESSAGE);
    grid.className = `chatbot-attachment-grid count-${count}`;

    const uniform = images.length > 1;

    images.forEach(attachment => grid.appendChild(buildAttachmentTile(chatId, attachment, uniform)));
    files.forEach(attachment => grid.appendChild(buildAttachmentTile(chatId, attachment, uniform)));

    return grid;
}

function openAttachmentLightbox(url, fileName) {
    const overlay = document.createElement('div');
    overlay.className = 'chatbot-lightbox';

    const image = document.createElement('img');
    image.src = url;
    image.alt = fileName;
    overlay.appendChild(image);

    const close = () => {
        overlay.remove();
        document.removeEventListener('keydown', onKeyDown);
    };

    const onKeyDown = (e) => {
        if (e.key === 'Escape') close();
    };

    overlay.addEventListener('click', close);
    document.addEventListener('keydown', onKeyDown);
    document.body.appendChild(overlay);
}

async function handleSendMessage() {
    const message = element.messageInput.value.trim();

    // An attachment on its own is a valid turn — "what's wrong here?" with just a screenshot.
    if (!message && stagedAttachmentFiles.length === 0) return;

    const previousDraftKey = draftKey();

    try {
        setButtonState(element.sendMessageBtn, true);

        let chatId = getActiveChatId();
        if (!chatId || !(await getChatById(chatId))) {
            const newChat = await createNewChat();
            chatId = newChat.id;
            setActiveChatId(chatId);
            element.chatTitle.textContent = 'New Chat';
            if (element.editChatBtn) element.editChatBtn.style.display = 'inline-flex';
            await renderChatsList();
        }

        // Taken and cleared before the upload so the composer empties the moment the user hits send,
        // the same way the text does.
        const filesToSend = stagedAttachmentFiles.slice();
        clearStagedAttachments();

        const userMessage = await addMessageToChat(chatId, 'user', message, {
            attachments: filesToSend.map(buildLocalPreview)
        });

        element.messageInput.value = '';
        autoResizeMessageInput();
        try {
            localStorage.removeItem(previousDraftKey);
            clearInputDraft(chatId);
        } catch (e) {
            console.error('Failed to clear input draft:', e);
        }

        const chat = await getChatById(chatId);
        renderMessages(chat.messages);
        showTypingIndicator();
        await getAssistantResponse(chatId, message, filesToSend, userMessage?.id);
    } finally {
        setButtonState(element.sendMessageBtn, false);
    }
}
async function getAssistantResponse(chatId, userMessage, filesToSend = [], localMessageId = null) {
    const originalChatId = chatId;
    pendingAIChats.add(chatId);
    try {
        if (!isApiChatId(chatId)) {
            const previousChatId = chatId;
            const apiChat = await createChatOnApi(userMessage);
            await replaceChatId(previousChatId, apiChat);
            pendingAIChats.delete(previousChatId);
            chatId = apiChat.id;
            pendingAIChats.add(chatId);

            if (getActiveChatId() === previousChatId) {
                setActiveChatId(chatId);
                element.chatTitle.textContent = apiChat.title || element.chatTitle.textContent;
            }

            await renderChatsList();
        }

        // Uploaded here rather than at compose time: the conversation only gets its API id once the
        // first message is sent, and the upload ticket is scoped to that id.
        const uploaded = await IndexLibrary.UploadAttachments(await attachmentContext(), chatId, filesToSend);

        const apiResponse = await sendMessageApi(chatId, userMessage, uploaded);
        const assistantContent = apiResponse?.aiResponse?.messageContext || apiResponse?.aiResponse?.MessageContext;
        if (!assistantContent) throw new Error('Invalid API response format');

        await replaceLocalAttachments(chatId, localMessageId, apiResponse?.userMessage);
        await addMessageToChat(chatId, 'assistant', assistantContent);
        hideTypingIndicator();
        if (getActiveChatId() === chatId) {
            const updatedChat = await getChatById(chatId);
            renderMessages(updatedChat.messages);
        }
    } catch (error) {
        console.error('[Chat API Error]', error);
        const errorMessage = {
            id: generateChatId(),
            role: 'error',
            content: error.message || 'Unable to get AI response. Please try again.',
            timestamp: new Date().toISOString(),
            retryData: {
                chatId: chatId,
                userMessage: userMessage
            }
        };
        await addMessageToChat(chatId, 'error', error.message || 'Unable to get AI response. Please try again.', errorMessage);
        hideTypingIndicator();
        if (getActiveChatId() === chatId) {
            const chat = await getChatById(chatId);
            renderMessages(chat.messages);
        }
    } finally {
        pendingAIChats.delete(originalChatId);
        pendingAIChats.delete(chatId);
    }
}

let searchTimeout;
async function handleSearchChats() {
    clearTimeout(searchTimeout);

    searchTimeout = setTimeout(async () => {
        const searchTerm = element.searchChatsInput.value.toLowerCase();
        if (!searchTerm) {
            await renderChatsList();
            return;
        }

        try {
            const searchResults = await searchChatsApi(searchTerm);
            const formattedChats = formatChatsFromApi(searchResults);
            await renderChatsList(formattedChats);
        } catch (e) {
            console.error('Search failed:', e);
        }
    }, 300);
}

async function retryMessage(chatId, userMessage) {
    if (!chatId || !userMessage) {
        console.error('[Retry Error] Missing chatId or userMessage', { chatId, userMessage });
        Toast.fire({ icon: 'error', title: 'Retry failed', text: 'Missing chat or message data' });
        return;
    }

    try {
        setButtonState(element.sendMessageBtn, true);
        hideTypingIndicator();
        showTypingIndicator();
        await getAssistantResponse(chatId, userMessage);
    } catch (error) {
        console.error('[Retry Error]', error);
    } finally {
        setButtonState(element.sendMessageBtn, false);
    }
}

function showChatMenu(chatId, event) {
    const currentMenu = document.getElementById('chatbot-context-menu');
    if (currentMenu) currentMenu.remove();

    const menu = document.createElement('div');
    menu.id = 'chatbot-context-menu';
    menu.className = 'chatbot-context-menu';
    menu.innerHTML = `
        <button class="chatbot-context-menu-item" data-action="edit" data-chat-id="${escapeHtml(chatId)}">
            <i class="bi bi-pencil-square"></i> Rename
        </button>
        <button class="chatbot-context-menu-item" data-action="delete" data-chat-id="${escapeHtml(chatId)}">
            <i class="bi bi-trash"></i> Delete
        </button>
    `;

    document.body.appendChild(menu);

    const rect = event.currentTarget.getBoundingClientRect();
    menu.style.position = 'fixed';
    menu.style.top = (rect.bottom + 5) + 'px';
    menu.style.left = (rect.left - 50) + 'px';

    menu.querySelectorAll('.chatbot-context-menu-item').forEach(item => {
        item.addEventListener('click', async (e) => {
            e.stopPropagation();
            const action = item.dataset.action;
            const cid = item.dataset.chatId;
            menu.remove();

            if (action === 'edit') {
                await editChatName(cid);
            } else if (action === 'delete') {
                await deleteChatItem(cid);
            }
        });
    });

    document.addEventListener('click', () => {
        const m = document.getElementById('chatbot-context-menu');
        if (m) m.remove();
    }, { once: true });
}

function setButtonState(button, disabled) {
    if (!button) return;
    button.disabled = disabled;
    button.style.opacity = disabled ? '0.5' : '1';
    button.style.cursor = disabled ? 'not-allowed' : 'pointer';
}

function focusMessageInput() {
    element.messageInput.focus();
}
function escapeHtml(text) {
    if (!text) return '';
    return String(text)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
}
function formatDate(isoDate) {
    const diffMs = Date.now() - new Date(isoDate).getTime();
    const diffMins = Math.floor(diffMs / 60000);
    const diffHours = Math.floor(diffMs / 3600000);
    const diffDays = Math.floor(diffMs / 86400000);

    if (diffMins < 1) return 'Now';
    if (diffMins < 60) return `${diffMins}m ago`;
    if (diffHours < 24) return `${diffHours}h ago`;
    if (diffDays < 7) return `${diffDays}d ago`;

    return new Date(isoDate).toLocaleDateString();
}
async function loadMoreChats() {
    if (isLoading || !hasMore) return;

    isLoading = true;

    try {
        showChatsSkeleton(5);

        currentPage += 1;

        const apiChats = await getAllChatsApi(currentPage, pageSize);
        const formatted = formatChatsFromApi(apiChats);

        hideChatsSkeleton();

        if (!formatted.length) {
            hasMore = false;
            return;
        }

        chatsCache = [
            ...(chatsCache || []),
            ...formatted
        ];

        await renderChatsList(chatsCache);

    } catch (e) {
        hideChatsSkeleton();
        console.error("Pagination error:", e);
    } finally {
        isLoading = false;
    }
}

async function loadOlderMessages(chatId) {

    if (isLoadingMessages || !hasMoreMessages) return;

    isLoadingMessages = true;

    const container = element.messagesContainer;

    showSkeletonMessageLoader();


    const prevHeight = container.scrollHeight;

    messagePage++;

    try {
        const olderMessages = await getChatMessagesApi(chatId, messagePage, messagePageSize);

        if (!olderMessages.length) {
            hasMoreMessages = false;
            return;
        }


        hideSkeletonMessageLoader();

        // API returns newest-first pages; reverse to chronological order
        const ordered = olderMessages.reverse();

        // Merge into cached chat.messages so later full re-renders won't drop these
        try {
            const chat = await getChatById(chatId);
            if (chat) {
                chat.messages = Array.isArray(chat.messages) ? ordered.concat(chat.messages) : ordered.slice();
                await updateChat(chatId, chat);
            }
        } catch (e) {
            console.warn('Failed to merge older messages into cache', e);
        }

        // Prepend into DOM in chronological order
        prependMessages(ordered);


        const newHeight = container.scrollHeight;
        container.scrollTop = newHeight - prevHeight;

    } catch (e) {
        console.error('Failed to load older messages', e);
    } finally {
        hideSkeletonMessageLoader();
        isLoadingMessages = false;
    }
}
function prependMessages(messages) {
    const container = element.messagesContainer;
    const fragment = document.createDocumentFragment();

    messages.forEach(msg => {
        // The wrapper matches renderMessages so message navigation still counts these rows.
        const wrapper = document.createElement('div');
        wrapper.className = 'chatbot-message-wrapper';

        const div = document.createElement('div');
        div.className = `chatbot-message ${msg.role}`;

        const content = document.createElement('div');
        content.className = 'chatbot-message-content';

        // ✅ handle markdown for assistant (same as your renderMessages)
        if (msg.role === 'assistant' && typeof marked !== 'undefined' && typeof marked.parse === 'function') {
            content.classList.add('chatbot-markdown');
            content.innerHTML = renderAssistantMarkdown(msg.content || '');
        } else {
            content.textContent = msg.content;
        }

        const grid = buildAttachmentGrid(getActiveChatId(), msg.attachments);
        if (grid) {
            const stack = document.createElement('div');
            stack.className = 'chatbot-message-stack';
            stack.appendChild(grid);
            if (msg.content) stack.appendChild(content);
            div.appendChild(stack);
        } else {
            div.appendChild(content);
        }

        wrapper.appendChild(div);
        fragment.appendChild(wrapper);
    });


    container.insertBefore(fragment, container.firstChild);
}


let isInitializing = false;

async function initializeChatbot() {
    if (isInitializing) return;
    isInitializing = true;

    cacheDOM();
    const scrollContainer = document.querySelector('.chatbot-chats-list');
    const scrollMessageContainer = document.querySelector('.chatbot-messages');
    scrollContainer.addEventListener('scroll', async () => {
        const el = scrollContainer;

        const isBottom =
            el.scrollHeight - el.scrollTop - el.clientHeight < 10;

        if (isBottom) {
            console.log('Reached bottom, loading more chats...');
            await loadMoreChats();
        }
    });
    scrollMessageContainer.addEventListener('scroll', async () => {
        const el = scrollMessageContainer;

        if (el.scrollTop <= 10) {
            console.log('Reached TOP → load older messages');

            const chatId = getActiveChatId();
            if (chatId) {
                await loadOlderMessages(chatId);
            }
        }
    });
    showLoader();
    try {
        await ensureSdkReady();
        if (!apiBaseUrl) {
            const config = await IndexLibrary.GetExtensionConfig();
            apiBaseUrl = config.apiBaseUrl || '';
            userguidelink = config.userguidelink || '';
            userhelplink = config.userhelplink || '';
        }

        const azureAuthenticationHeader = await IndexLibrary.GetAccessToken();
        const orgName = await IndexLibrary.GetAccountNameAsync();
        const userEmail = IndexLibrary.GetUserEmail();

        const licenseData = { organizationName: orgName };
        let licenseResponse;
        let licenseBlocked = false;
        try {
            const licenseRes = await IndexLibrary.PostRequest({
                endpoint: '/api/License/GetLicense',
                baseAPIUrl: apiBaseUrl,
                azureAuthenticationHeader: azureAuthenticationHeader,
                body: licenseData
            });
            if (licenseRes.status === 404) {
                document.getElementById('startTrialMessage').style.display = '';
                licenseBlocked = true;
            } else {
                licenseResponse = await licenseRes.json();
            }
        } catch (err) {
            document.getElementById('startTrialMessage').style.display = '';
            licenseBlocked = true;
        }

        if (!licenseBlocked && licenseResponse) {
            if (licenseResponse.status !== undefined && licenseResponse.status === 404) {
                document.getElementById('startTrialMessage').style.display = '';
                CreateTrialLicense();
                licenseBlocked = true;
            } else {
                const userLicensedRes = await IndexLibrary.GetRequest({
                    endpoint: `/api/License/IsUserLicensed?organizationName=${orgName}&email=${userEmail}&moduleUserType=clUserType`,
                    baseAPIUrl: apiBaseUrl,
                    azureAuthenticationHeader: azureAuthenticationHeader
                });
                const isUserLicensed = await userLicensedRes.text();
                if (isUserLicensed.toLowerCase() === 'false') {
                    const authErrEl = document.getElementById('authorizationErrorMessage');
                    authErrEl.textContent = 'You are not authorized to perform this action. Please contact your organization administrator.';
                    authErrEl.style.display = '';
                    licenseBlocked = true;
                } else if (licenseResponse.licenseType === 0) {
                    const trialEl = document.getElementById('startTrialMessage');
                    const licenseExpiryDate = new Date(licenseResponse.expiryDateTimeUtc);
                    if (licenseExpiryDate < new Date()) {
                        trialEl.textContent = 'Your license is invalid or expired. Please contact your organization administrator.';
                        trialEl.style.display = '';
                        licenseBlocked = true;
                    } else {
                        trialEl.innerHTML = `Devops Assist AI trial license is valid until ${licenseExpiryDate.toLocaleDateString('en-US')}`;
                        trialEl.style.display = '';
                    }
                }
            }
        }

        if (!licenseBlocked) {
            try {
                const cleoTypeRes = await IndexLibrary.GetRequest({
                    endpoint: `/api/License/GetUserLicenseType?organizationName=${orgName}&email=${userEmail}&fieldName=clUserType`,
                    baseAPIUrl: apiBaseUrl,
                    azureAuthenticationHeader: azureAuthenticationHeader
                });
                const cleoType = (await cleoTypeRes.text()).trim().toLowerCase();
                const hasCleoLicense = cleoType !== 'null' && cleoType !== '' && cleoType !== 'undefined';
                if (!hasCleoLicense) {
                    const authErrEl = document.getElementById('authorizationErrorMessage');
                    authErrEl.textContent = 'You do not have a Cleo Agent license. Please contact your organization administrator.';
                    authErrEl.style.display = '';
                    licenseBlocked = true;
                }
            } catch (err) {
                console.error('Failed to verify Cleo license', err);
            }
        }

        if (licenseBlocked) {
            element.messageInput.disabled = true;
            element.sendMessageBtn.disabled = true;
            element.newChatBtn.disabled = true;
            return;
        }

        const apiChats = await getAllChatsApi(currentPage, pageSize);
        chatsCache = formatChatsFromApi(apiChats);
        const chats = await getAllChats();
        const activeChatId = getActiveChatId();

        if (activeChatId && await getChatById(activeChatId)) {
            await selectChat(activeChatId);
        } else if (chats.length > 0) {
            await selectChat(chats[0].id);
        } else {
            setActiveChatId(null);
            await renderChatsList(chats);
            restoreInputDraft();
        }
    } finally {
        hideLoader();
    }

    element.newChatBtn.addEventListener('click', handleNewChat);
    element.sendMessageBtn.addEventListener('click', handleSendMessage);
    if (element.attachFileBtn && element.attachFileInput) {
        element.attachFileBtn.addEventListener('click', () => element.attachFileInput.click());
        element.attachFileInput.addEventListener('change', onAttachFilesSelected);
    }
    if (element.editChatBtn) {
        element.editChatBtn.addEventListener('click', () => {
            const chatId = getActiveChatId();
            if (chatId) editChatName(chatId).catch(e => console.error('Error editing chat name:', e));
        });
    }
    element.deleteChatBtn.addEventListener('click', () => {
        const chatId = getActiveChatId();
        if (chatId) deleteChatItem(chatId).catch(e => console.error('Error deleting chat:', e));
    });
    element.messageInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            handleSendMessage().catch(e => console.error('Error sending message:', e));
        }
    });
    document.addEventListener('keydown', (e) => {
        if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
        if (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) return;

        const active = document.activeElement;
        if (active === element.searchChatsInput) return;

        if (active === element.messageInput) {
            const recalled = e.key === 'ArrowUp' ? recallHistoryUp() : recallHistoryDown();
            if (recalled) {
                e.preventDefault();
            }
            return;
        }

        const direction = e.key === 'ArrowUp' ? -1 : 1;
        if (navigateMessages(direction)) {
            e.preventDefault();
        }
    });
    element.messageInput.addEventListener('input', () => {
        inputHistoryIndex = null;
        autoResizeMessageInput();
        saveInputDraft();
    });
    element.searchChatsInput.addEventListener('input', handleSearchChats);
    element.chatsList.addEventListener('click', (e) => {
        const chatItem = e.target.closest('.chatbot-chat-item');
        if (!chatItem) return;

        const clickedChatId = chatItem.dataset.chatId;
        if (clickedChatId === getActiveChatId()) return;
        clearTimeout(chatSelectTimeout);
        chatSelectTimeout = setTimeout(() => {
            selectChat(clickedChatId).catch(e => console.error('Error selecting chat:', e));
        }, 300);
    });
    autoResizeMessageInput();
    focusMessageInput();
}

async function CreateTrialLicense() {
    await IndexLibrary.CreateTrialLicense(false);
}

function openUserGuideLink() {
    IndexLibrary.openInNewTab(userguidelink);
}

function openUserHelpLink() {
    IndexLibrary.openInNewTab(userhelplink);
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initializeChatbot);
} else {
    initializeChatbot().catch(e => console.error('Error initializing chatbot:', e));
}