let latestPrices = null;
let lastRefreshTime = null;
let currentPage = 1;
let jsonData = [];
let originalJsonData = [];
let currentSortColumn = 0;
let currentSortDirection = 'desc';
let isPaginationInProgress = false;
let autoRefreshInterval = null;

const isSentOffers = window.offersTableConfig.isSentOffers;
const CACHE_DURATION = window.config.cacheConfig.defaultTTL;
const wsPort = window.config.wsPort;
const itemsPerPage = window.config.itemsPerPage;

const offersBody = document.getElementById('offers-body');
const filterForm = document.getElementById('filterForm');
const prevPageButton = document.getElementById('prevPage');
const nextPageButton = document.getElementById('nextPage');
const currentPageSpan = document.getElementById('currentPage');
const totalPagesSpan = document.getElementById('totalPages');
const lastRefreshTimeSpan = document.getElementById('lastRefreshTime');
const newEntriesCountSpan = document.getElementById('newEntriesCount');

window.tableRateModule = {

    cache: {},
    processedOffers: new Set(),

    getCachedValue(key) {
        if (window.CacheManager) {
            return window.CacheManager.get(key);
        }
        const cachedItem = localStorage.getItem(key);
        if (cachedItem) {
            const parsedItem = JSON.parse(cachedItem);
            if (Date.now() < parsedItem.expiry) {
                return parsedItem.value;
            } else {
                localStorage.removeItem(key);
            }
        }
        return null;
    },

    setCachedValue(key, value, resourceType = null) {
        if (window.CacheManager) {
            const ttl = resourceType ?
                window.config.cacheConfig.ttlSettings[resourceType] ||
                window.config.cacheConfig.defaultTTL :
                900000;
            window.CacheManager.set(key, value, ttl);
            return;
        }
        const ttl = resourceType ?
            window.config.cacheConfig.ttlSettings[resourceType] ||
            window.config.cacheConfig.defaultTTL :
            900000;

        const item = {
            value: value,
            expiry: Date.now() + ttl,
        };
        localStorage.setItem(key, JSON.stringify(item));
    },

    setFallbackValue(coinSymbol, value) {
        this.setCachedValue(`fallback_${coinSymbol}_usd`, value, 'fallback');
    },

    isNewOffer(offerId) {
        if (this.processedOffers.has(offerId)) {
            return false;
        }
        this.processedOffers.add(offerId);
        return true;
    },

    getFallbackValue(coinSymbol) {
        if (!coinSymbol) return null;
        const normalizedSymbol = coinSymbol.toLowerCase() === 'part' ? 'particl' : coinSymbol.toLowerCase();
        const cachedValue = this.getCachedValue(`fallback_${normalizedSymbol}_usd`);
        return cachedValue;
    },

    initializeTable() {
        document.querySelectorAll('.coinname-value').forEach(coinNameValue => {
            const coinFullNameOrSymbol = coinNameValue.getAttribute('data-coinname');
            if (!coinFullNameOrSymbol || coinFullNameOrSymbol === 'Unknown') {
                return;
            }
            coinNameValue.classList.remove('hidden');
            if (!coinNameValue.textContent.trim()) {
                coinNameValue.textContent = 'N/A';
            }
        });

        document.querySelectorAll('.usd-value').forEach(usdValue => {
            if (!usdValue.textContent.trim()) {
                usdValue.textContent = 'N/A';
            }
        });

        document.querySelectorAll('.profit-loss').forEach(profitLoss => {
            if (!profitLoss.textContent.trim() || profitLoss.textContent === 'Calculating...') {
                profitLoss.textContent = 'N/A';
            }
        });
    },

    init() {
        this.initializeTable();
    }
};

function initializeTableRateModule() {
    if (typeof window.tableRateModule !== 'undefined') {
        tableRateModule = window.tableRateModule;
        return true;
    } else {
        return false;
    }
}

function continueInitialization() {
    fetchOffers().then(() => {
        applyFilters();
        if (!isSentOffers) {
            return;
        }
    });

    const listingLabel = document.querySelector('span[data-listing-label]');
    if (listingLabel) {
        listingLabel.textContent = isSentOffers ? 'Total Listings: ' : 'Network Listings: ';
    }
}

function initializeTooltips() {
    if (window.TooltipManager) {
        window.TooltipManager.initializeTooltips();
    }
}

function initializeTooltipsInBatches() {
    if (!window.TooltipManager) return;

    const tooltipElements = document.querySelectorAll('[data-tooltip-target]');
    const BATCH_SIZE = 5; 
    let currentIndex = 0;

    function processBatch() {
        const endIndex = Math.min(currentIndex + BATCH_SIZE, tooltipElements.length);

        for (let i = currentIndex; i < endIndex; i++) {
            const element = tooltipElements[i];
            const targetId = element.getAttribute('data-tooltip-target');
            if (!targetId) continue;

            const tooltipContent = document.getElementById(targetId);
            if (tooltipContent) {
                window.TooltipManager.create(element, tooltipContent.innerHTML, {
                    placement: element.getAttribute('data-tooltip-placement') || 'top'
                });
            }
        }

        currentIndex = endIndex;

        if (currentIndex < tooltipElements.length) {
            CleanupManager.setTimeout(processBatch, 0);
        }
    }

    if (tooltipElements.length > 0) {
        CleanupManager.setTimeout(processBatch, 0);
    }
}

function getValidOffers() {
    if (!jsonData) {
        return [];
    }

    return jsonData;
}

function saveFilterSettings() {
    const storageKey = isSentOffers ? 'sentOffersTableSettings' : 'networkOffersTableSettings';

    const selectedCoinTo = getSelectedCoins('coin_to');
    const selectedCoinFrom = getSelectedCoins('coin_from');

    const statusSelect = document.getElementById('status');
    const sentFromSelect = document.getElementById('sent_from');
    const autoAcceptTypeSelect = document.getElementById('auto_accept_type');

    localStorage.setItem(storageKey, JSON.stringify({
        coin_to: selectedCoinTo,
        coin_from: selectedCoinFrom,
        status: statusSelect ? statusSelect.value : 'any',
        sent_from: sentFromSelect ? sentFromSelect.value : 'any',
        auto_accept_type: autoAcceptTypeSelect ? autoAcceptTypeSelect.value : 'any',
        sortColumn: currentSortColumn,
        sortDirection: currentSortDirection
    }));
}

function getSelectedCoins(filterType) {

    const dropdown = document.getElementById(`${filterType}_dropdown`);
    if (!dropdown) {
        return ['any'];
    }

    const allCheckboxes = dropdown.querySelectorAll('input[type="checkbox"]');

    const selected = [];
    allCheckboxes.forEach((cb) => {
        if (cb.checked && cb.value !== 'any') {
            selected.push(cb.value);
        }
    });

    return selected.length > 0 ? selected : ['any'];
}

function getCoinNameFromValue(value, filterType) {
    if (value === 'any') {
        return 'any';
    }

    const dropdown = document.getElementById(`${filterType}_dropdown`);
    if (!dropdown) {
        return value;
    }

    const checkbox = dropdown.querySelector(`input[value="${value}"]`);
    if (checkbox) {
        const label = checkbox.closest('label');
        const spans = label.querySelectorAll('span');

        const coinSpan = spans[spans.length - 1];
        if (coinSpan) {
            const text = coinSpan.textContent.trim();

            const cleanText = text.replace(/\s*\(clear all\)\s*/, '');
            return cleanText;
        }
    }

    return value;
}

function getCoinImage(coinName) {
    return window.CoinManager.getCoinIcon(coinName);
}

function updateFilterButtonText(filterType) {
    const selected = getSelectedCoins(filterType);
    const button = document.getElementById(`${filterType}_button`);
    const textSpan = document.getElementById(`${filterType}_text`);

    if (!button || !textSpan) return;

    if (selected.length === 0 || (selected.length === 1 && selected[0] === 'any')) {

        const defaultText = filterType === 'coin_to' ?
            (isSentOffers ? 'Filter Receiving' : 'Filter Bids') :
            (isSentOffers ? 'Filter Sending' : 'Filter Offers');
        textSpan.textContent = defaultText;
    } else {
        const filterLabel = filterType === 'coin_to' ?
            (isSentOffers ? 'Receiving' : 'Bids') :
            (isSentOffers ? 'Sending' : 'Offers');
        textSpan.textContent = `Filter ${filterLabel} (${selected.length} selected)`;
    }

    button.style.width = '210px';
}

function updateCoinBadges(filterType) {
    const selected = getSelectedCoins(filterType);
    const badgesContainer = document.getElementById(`${filterType}_badges`);
    const mainContainer = document.getElementById('selected_coins_container');

    if (!badgesContainer) return;

    badgesContainer.innerHTML = '';

    if (selected.length > 0 && !(selected.length === 1 && selected[0] === 'any')) {
        selected.forEach(coinValue => {
            const coinName = getCoinNameFromValue(coinValue, filterType);
            const badge = document.createElement('span');

            const isBidsFilter = filterType === 'coin_to' && !isSentOffers;
            const isOffersFilter = filterType === 'coin_from' && !isSentOffers;
            const isReceivingFilter = filterType === 'coin_to' && isSentOffers;
            const isSendingFilter = filterType === 'coin_from' && isSentOffers;

            let badgeClass = 'inline-flex items-center px-3 py-2 rounded-full text-sm font-medium mr-2 mb-1';
            if (isBidsFilter || isReceivingFilter) {
                badgeClass += ' bg-blue-500 text-white dark:bg-blue-600 dark:text-white';
            } else {
                badgeClass += ' bg-green-400 text-white dark:bg-green-500 dark:text-white';
            }

            badge.className = badgeClass + ' cursor-pointer hover:opacity-80';

            const coinImage = getCoinImage(coinName);

            badge.innerHTML = `
                <img src="/static/images/coins/${coinImage}" class="w-4 h-4 mr-1" alt="${coinName}" onerror="this.style.display='none'">
                <span>${coinName}</span>
                <button type="button" class="ml-1 text-current hover:text-red-500 focus:outline-none remove-coin-btn">
                    <span class="sr-only">Remove ${coinName}</span>
                    ×
                </button>
            `;

            CleanupManager.addListener(badge, 'click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                removeCoinFilter(filterType, coinValue);
            });

            const closeBtn = badge.querySelector('.remove-coin-btn');
            if (closeBtn) {
                CleanupManager.addListener(closeBtn, 'click', (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    removeCoinFilter(filterType, coinValue);
                });
            }

            badgesContainer.appendChild(badge);
        });

        if (mainContainer) {
            mainContainer.style.display = 'block';
        }
    } else {

        const otherType = filterType === 'coin_to' ? 'coin_from' : 'coin_to';
        const otherSelected = getSelectedCoins(otherType);
        if (otherSelected.length === 0 || (otherSelected.length === 1 && otherSelected[0] === 'any')) {
            if (mainContainer) {
                mainContainer.style.display = 'none';
            }
        }
    }
}

function coinMatches(offerCoin, filterCoins) {
    if (!offerCoin || !filterCoins) return true;

    if (typeof filterCoins === 'string') {
        filterCoins = [filterCoins];
    }

    if (filterCoins.includes('any') || filterCoins.length === 0) return true;

    const normalizedOfferCoin = offerCoin.toLowerCase().trim();

    return filterCoins.some(filterCoin => {
        if (!filterCoin) return false;

        const normalizedFilterCoin = filterCoin.toLowerCase().trim();

        if (normalizedOfferCoin === normalizedFilterCoin) {
            return true;
        }

        if (window.CoinUtils && window.CoinUtils.isSameCoin(normalizedOfferCoin, normalizedFilterCoin)) {
            return true;
        }

        const particlVariants = ['particl', 'particl anon', 'particl blind'];
        if (normalizedFilterCoin === 'particl' && particlVariants.includes(normalizedOfferCoin)) {
            return true;
        }

        if (particlVariants.includes(normalizedFilterCoin)) {
            return normalizedOfferCoin === normalizedFilterCoin;
        }

        if (normalizedFilterCoin.includes(' ') || normalizedOfferCoin.includes(' ')) {
            const filterFirstWord = normalizedFilterCoin.split(' ')[0];
            const offerFirstWord = normalizedOfferCoin.split(' ')[0];

            if (filterFirstWord === 'bitcoin' && offerFirstWord === 'bitcoin') {
                const filterHasCash = normalizedFilterCoin.includes('cash');
                const offerHasCash = normalizedOfferCoin.includes('cash');
                return filterHasCash === offerHasCash;
            }

            if (filterFirstWord === offerFirstWord && filterFirstWord.length > 4) {
                return true;
            }
        }

        return false;
    });
}

function toggleDropdown(filterType) {
    const dropdown = document.getElementById(`${filterType}_dropdown`);
    const button = document.getElementById(`${filterType}_button`);
    const container = dropdown?.closest('.dropdown-container');

    if (dropdown && button) {
        const isVisible = dropdown.style.display !== 'none';
        hideAllDropdowns();

        if (!isVisible) {
            const buttonRect = button.getBoundingClientRect();
            dropdown.style.position = 'fixed';
            dropdown.style.top = (buttonRect.bottom + 4) + 'px';
            dropdown.style.left = buttonRect.left + 'px';
            dropdown.style.width = buttonRect.width + 'px';
            dropdown.style.display = 'block';

            if (container) {
                container.classList.add('open');
            }
        }
    }
}

function hideDropdown(filterType) {
    const dropdown = document.getElementById(`${filterType}_dropdown`);
    const container = dropdown?.closest('.dropdown-container');

    if (dropdown) {
        dropdown.style.display = 'none';
        if (container) {
            container.classList.remove('open');
        }
    }
}

function hideAllDropdowns() {
    hideDropdown('coin_to');
    hideDropdown('coin_from');
}

function handleCoinCheckboxChange(filterType, checkbox) {
    if (checkbox.value === 'any') {

        if (checkbox.checked) {
            const dropdown = document.getElementById(`${filterType}_dropdown`);
            if (dropdown) {
                const otherCheckboxes = dropdown.querySelectorAll('input[type="checkbox"]:not([value="any"])');
                otherCheckboxes.forEach(cb => cb.checked = false);
            }
        }
    } else {

        if (checkbox.checked) {
            const dropdown = document.getElementById(`${filterType}_dropdown`);
            if (dropdown) {
                const anyCheckbox = dropdown.querySelector('input[value="any"]');
                if (anyCheckbox) anyCheckbox.checked = false;
            }
        }
    }

    updateFilterButtonText(filterType);
    updateCoinBadges(filterType);
    applyFilters();
    updateClearFiltersButton();
}

function removeCoinFilter(filterType, coinValue) {

    const dropdown = document.getElementById(`${filterType}_dropdown`);
    if (!dropdown) {
        return;
    }

    const checkbox = dropdown.querySelector(`input[value="${coinValue}"]`);
    if (checkbox) {
        checkbox.checked = false;

        updateFilterButtonText(filterType);
        updateCoinBadges(filterType);
        applyFilters();
        updateClearFiltersButton();
    }
}

window.removeCoinFilter = removeCoinFilter;

function filterAndSortData() {
    saveFilterSettings();

    let filteredData = [...originalJsonData];

    const statusSelect = document.getElementById('status');
    const sentFromSelect = document.getElementById('sent_from');
    const autoAcceptTypeSelect = document.getElementById('auto_accept_type');
    const selectedCoinTo = getSelectedCoins('coin_to');
    const selectedCoinFrom = getSelectedCoins('coin_from');

    const sentFromFilter = sentFromSelect ? sentFromSelect.value : 'any';
    filteredData = filteredData.filter(offer => {
        const isMatch = sentFromFilter === 'public' ? offer.is_public :
                        sentFromFilter === 'private' ? !offer.is_public :
                        true;
        return isMatch;
    });

    const autoAcceptTypeFilter = autoAcceptTypeSelect ? autoAcceptTypeSelect.value : 'any';
    if (autoAcceptTypeFilter !== 'any') {
        filteredData = filteredData.filter(offer => {
            const offerAutoAcceptType = offer.auto_accept_type !== undefined ? offer.auto_accept_type : 0;
            return offerAutoAcceptType === parseInt(autoAcceptTypeFilter);
        });
    }

    filteredData = filteredData.filter(offer => {
        if (!isSentOffers && isOfferExpired(offer)) {
            return false;
        }

        if (selectedCoinTo.length > 0 && !(selectedCoinTo.length === 1 && selectedCoinTo[0] === 'any')) {
            const coinNames = selectedCoinTo.map(value => getCoinNameFromValue(value, 'coin_to'));
            const matches = coinMatches(offer.coin_to, coinNames);
            if (!matches) {
                return false;
            }
        }

        if (selectedCoinFrom.length > 0 && !(selectedCoinFrom.length === 1 && selectedCoinFrom[0] === 'any')) {
            const coinNames = selectedCoinFrom.map(value => getCoinNameFromValue(value, 'coin_from'));
            const matches = coinMatches(offer.coin_from, coinNames);
            if (!matches) {
                return false;
            }
        }

        if (isSentOffers && statusSelect && statusSelect.value !== 'any') {
            const isExpired = offer.expire_at <= Math.floor(Date.now() / 1000);
            const isRevoked = Boolean(offer.is_revoked);

            let statusMatch = false;
            switch (statusSelect.value) {
                case 'active':
                    statusMatch = !isExpired && !isRevoked;
                    break;
                case 'expired':
                    statusMatch = isExpired && !isRevoked;
                    break;
                case 'revoked':
                    statusMatch = isRevoked;
                    break;
            }

            if (!statusMatch) {
                return false;
            }
        }

        return true;
    });

    if (currentSortColumn === 7) {
        const offersWithPercentages = [];

        for (const offer of filteredData) {
            const fromAmount = parseFloat(offer.amount_from) || 0;
            const toAmount = parseFloat(offer.amount_to) || 0;
            const coinFrom = offer.coin_from;
            const coinTo = offer.coin_to;

            const fromSymbol = getPriceKey(coinFrom);
            const toSymbol = getPriceKey(coinTo);

            const fromPriceUSD = latestPrices && fromSymbol ? latestPrices[fromSymbol]?.usd : null;
            const toPriceUSD = latestPrices && toSymbol ? latestPrices[toSymbol]?.usd : null;

            let percentDiff = null;

            if (fromPriceUSD && toPriceUSD && !isNaN(fromPriceUSD) && !isNaN(toPriceUSD)) {
                const fromValueUSD = fromAmount * fromPriceUSD;
                const toValueUSD = toAmount * toPriceUSD;

                if (fromValueUSD && toValueUSD) {
                    if (offer.is_own_offer || isSentOffers) {
                        percentDiff = ((toValueUSD / fromValueUSD) - 1) * 100;
                    } else {
                        percentDiff = (-((toValueUSD / fromValueUSD) - 1)) * 100;
                    }
                }
            }

            offersWithPercentages.push({
                offer: offer,
                percentDiff: percentDiff
            });
        }

        const validOffers = offersWithPercentages.filter(item => item.percentDiff !== null);
        const nullOffers = offersWithPercentages.filter(item => item.percentDiff === null);

        validOffers.sort((a, b) => {
            return currentSortDirection === 'asc' ? a.percentDiff - b.percentDiff : b.percentDiff - a.percentDiff;
        });

        filteredData = [...validOffers.map(item => item.offer), ...nullOffers.map(item => item.offer)];
    } else if (currentSortColumn !== null && currentSortDirection !== null) {
        filteredData.sort((a, b) => {
            let aValue, bValue;

            switch(currentSortColumn) {
                case 0:
                    aValue = a.created_at;
                    bValue = b.created_at;
                    break;
                case 1:
                    aValue = a.addr_from || '';
                    bValue = b.addr_from || '';
                    break;
                case 2:
                    aValue = parseFloat(a.amount_to) || 0;
                    bValue = parseFloat(b.amount_to) || 0;
                    break;
                case 3:
                    aValue = a.coin_from + a.coin_to;
                    bValue = b.coin_from + b.coin_to;
                    break;
                case 4:
                    aValue = parseFloat(a.amount_from) || 0;
                    bValue = parseFloat(b.amount_from) || 0;
                    break;
                case 5:
                    aValue = parseFloat(a.rate) || 0;
                    bValue = parseFloat(b.rate) || 0;
                    break;
                case 6:
                    const aSymbol = getPriceKey(a.coin_to);
                    const bSymbol = getPriceKey(b.coin_to);
                    const aRate = parseFloat(a.rate) || 0;
                    const bRate = parseFloat(b.rate) || 0;
                    const aPriceUSD = latestPrices && aSymbol ? latestPrices[aSymbol]?.usd : null;
                    const bPriceUSD = latestPrices && bSymbol ? latestPrices[bSymbol]?.usd : null;

                    aValue = aPriceUSD && !isNaN(aPriceUSD) ? aRate * aPriceUSD : 0;
                    bValue = bPriceUSD && !isNaN(bPriceUSD) ? bRate * bPriceUSD : 0;
                    break;
                default:
                    aValue = a.created_at;
                    bValue = b.created_at;
            }

            if (typeof aValue === 'number' && typeof bValue === 'number') {
                return currentSortDirection === 'asc' ? aValue - bValue : bValue - aValue;
            }

            if (typeof aValue === 'string' && typeof bValue === 'string') {
                return currentSortDirection === 'asc'
                    ? aValue.localeCompare(bValue)
                    : bValue.localeCompare(aValue);
            }

            return currentSortDirection === 'asc' ? (aValue > bValue ? 1 : -1) : (bValue > aValue ? 1 : -1);
        });
    }

    return filteredData;
}

async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer) {
    return new Promise((resolve) => {
        if (!fromCoin || !toCoin) {
            resolve(null);
            return;
        }

        const getPriceForCoin = (coin) => {
            if (!coin) return null;

            let normalizedCoin = coin.toLowerCase();

            if (window.CoinManager) {
                normalizedCoin = window.CoinManager.getPriceKey(coin) || normalizedCoin;
            } else {
                if (window.CoinUtils) {
                    normalizedCoin = window.CoinUtils.normalizeCoinName(normalizedCoin, latestPrices);
                }
            }
            let price = null;
            if (latestPrices && latestPrices[normalizedCoin]) {
                price = latestPrices[normalizedCoin].usd;
            }
            return price;
        };

        const fromPriceUSD = getPriceForCoin(fromCoin);
        const toPriceUSD = getPriceForCoin(toCoin);

        if (!fromPriceUSD || !toPriceUSD || isNaN(fromPriceUSD) || isNaN(toPriceUSD)) {
            resolve(null);
            return;
        }

        const fromValueUSD = fromAmount * fromPriceUSD;
        const toValueUSD = toAmount * toPriceUSD;

        if (isNaN(fromValueUSD) || isNaN(toValueUSD) || fromValueUSD === 0 || toValueUSD === 0) {
            resolve(null);
            return;
        }

        let percentDiff;
        if (isOwnOffer) {
            percentDiff = ((toValueUSD / fromValueUSD) - 1) * 100;
        } else {
            percentDiff = (-((toValueUSD / fromValueUSD) - 1)) * 100;
        }

        if (isNaN(percentDiff)) {
            resolve(null);
            return;
        }

        resolve(percentDiff);
    });
}

function getEmptyPriceData() {
    return window.config.utils.getEmptyPriceData();
}

async function fetchLatestPrices() {
    try {

        const prices = await window.PriceManager.getPrices(window.isManualRefresh);
        window.isManualRefresh = false;
        return prices;
    } catch (error) {
        console.error('Error fetching prices:', error);
        return getEmptyPriceData();
    }
}

async function fetchPricesAsync() {
    try {
        const prices = await window.PriceManager.getPrices(false);
        return prices;
    } catch (error) {
        console.error('Error fetching prices asynchronously:', error);
        return null;
    }
}

async function fetchOffers() {
    const refreshButton = document.getElementById('refreshOffers');
    const refreshIcon = document.getElementById('refreshIcon');
    const refreshText = document.getElementById('refreshText');

    const fetchWithRetry = async (url, maxRetries = 3) => {
        for (let i = 0; i < maxRetries; i++) {
            try {
                const response = await fetch(url);
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response;
            } catch (error) {
                if (i === maxRetries - 1) throw error;
                console.log(`Fetch retry ${i + 1}/${maxRetries} for ${url}`);
                
                await new Promise(resolve => CleanupManager.setTimeout(resolve, 100 * Math.pow(2, i)));
            }
        }
    };

    try {
        if (!NetworkManager.isOnline()) {
            throw new Error('Network is offline');
        }

        if (refreshButton) {
            refreshButton.disabled = true;
            refreshIcon.classList.add('animate-spin');
            refreshText.textContent = 'Refreshing...';
            refreshButton.classList.add('opacity-75', 'cursor-wait');
        }

        const offersResponse = await fetchWithRetry(isSentOffers ? '/json/sentoffers' : '/json/offers');
        const data = await offersResponse.json();

        if (data.error) {
            if (data.locked) {
                if (typeof ui !== 'undefined' && ui.displayErrorMessage) {
                    ui.displayErrorMessage(data.error);
                } else {
                    offersBody.innerHTML = `
                        <tr>
                            <td colspan="9" class="text-center py-8">
                                <div class="flex flex-col items-center justify-center text-yellow-600 dark:text-yellow-400">
                                    <svg class="w-8 h-8 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
                                    </svg>
                                    <span class="font-medium">${data.error}</span>
                                </div>
                            </td>
                        </tr>`;
                }
                return;
            } else {
                throw new Error(data.error);
            }
        }
        const processedData = Array.isArray(data) ? data : Object.values(data);

        jsonData = formatInitialData(processedData);
        originalJsonData = [...jsonData];

        CacheManager.set('offers_cached', jsonData, 'offers');

        applyFilters();
        updatePaginationInfo();

        fetchPricesAsync().then(prices => {
            if (prices) {
                latestPrices = prices;
                updateProfitLossDisplays();
            }
        }).catch(error => {
            console.error('Error fetching prices after offers refresh:', error);
        });

    } catch (error) {
        console.error('[Debug] Error fetching offers:', error);
        NetworkManager.handleNetworkError(error);

        const cachedOffers = CacheManager.get('offers_cached');
        if (cachedOffers?.value) {
            jsonData = cachedOffers.value;
            originalJsonData = [...jsonData];
            applyFilters();
        }
        ui.displayErrorMessage('Failed to fetch offers. Please try again later.');
    } finally {
        if (refreshButton) {
            refreshButton.disabled = false;
            refreshIcon.classList.remove('animate-spin');
            refreshText.textContent = 'Refresh';
            refreshButton.classList.remove('opacity-75', 'cursor-wait');
        }
    }
}

function formatInitialData(data) {
    return data.map(offer => ({
        offer_id: String(offer.offer_id || ''),
        swap_type: String(offer.swap_type || 'N/A'),
        addr_from: String(offer.addr_from || ''),
        addr_to: String(offer.addr_to || ''),
        coin_from: String(offer.coin_from || ''),
        coin_to: String(offer.coin_to || ''),
        amount_from: String(offer.amount_from || '0'),
        amount_to: String(offer.amount_to || '0'),
        rate: String(offer.rate || '0'),
        created_at: Number(offer.created_at || 0),
        expire_at: Number(offer.expire_at || 0),
        is_own_offer: Boolean(offer.is_own_offer),
        amount_negotiable: Boolean(offer.amount_negotiable),
        is_revoked: Boolean(offer.is_revoked),
        is_public: offer.is_public !== undefined ? Boolean(offer.is_public) : false,
        auto_accept_type: offer.auto_accept_type !== undefined ? Number(offer.auto_accept_type) : 0,
        unique_id: `${offer.offer_id}_${offer.created_at}_${offer.coin_from}_${offer.coin_to}`
    }));
}

function updateConnectionStatus(status) {
    const dot = document.getElementById('status-dot');
    const text = document.getElementById('status-text');

    if (!dot || !text) {
        return;
    }

    switch(status) {
        case 'connected':
            dot.className = 'w-2.5 h-2.5 rounded-full bg-green-500 mr-2';
            text.textContent = 'Connected';
            text.className = 'text-sm text-green-500';
            break;
        case 'disconnected':
            dot.className = 'w-2.5 h-2.5 rounded-full bg-red-500 mr-2';
            text.textContent = 'Disconnected - Reconnecting...';
            text.className = 'text-sm text-red-500';
            break;
        case 'error':
            dot.className = 'w-2.5 h-2.5 rounded-full bg-yellow-500 mr-2';
            text.textContent = 'Connection Error';
            text.className = 'text-sm text-yellow-500';
            break;
        default:
            dot.className = 'w-2.5 h-2.5 rounded-full bg-gray-500 mr-2';
            text.textContent = 'Connecting...';
            text.className = 'text-sm text-gray-500';
    }
}

function updateRowTimes() {
    
    const rows = document.querySelectorAll('[data-offer-id]');
    const updates = [];

    rows.forEach(row => {
        const offerId = row.getAttribute('data-offer-id');
        const offer = jsonData.find(o => o.offer_id === offerId);
        if (!offer) return;

        const newPostedTime = formatTime(offer.created_at, true);
        const newExpiresIn = formatTimeLeft(offer.expire_at);

        const postedElement = row.querySelector('.text-xs:first-child');
        const expiresElement = row.querySelector('.text-xs:last-child');

        updates.push({
            postedElement,
            expiresElement,
            newPostedTime,
            newExpiresIn
        });
    });

    updates.forEach(({ postedElement, expiresElement, newPostedTime, newExpiresIn }) => {
        if (postedElement && postedElement.textContent !== `Posted: ${newPostedTime}`) {
            postedElement.textContent = `Posted: ${newPostedTime}`;
        }
        if (expiresElement && expiresElement.textContent !== `Expires in: ${newExpiresIn}`) {
            expiresElement.textContent = `Expires in: ${newExpiresIn}`;
        }
    });
}

function updateLastRefreshTime() {
    if (lastRefreshTimeSpan) {
        lastRefreshTimeSpan.textContent = lastRefreshTime ? new Date(lastRefreshTime).toLocaleTimeString() : 'Never';
    }
}

function stopRefreshAnimation() {
    const refreshButton = document.getElementById('refreshOffers');
    const refreshIcon = document.getElementById('refreshIcon');
    const refreshText = document.getElementById('refreshText');

    if (refreshButton) {
        refreshButton.disabled = false;
        refreshButton.classList.remove('opacity-75', 'cursor-wait');
    }
    if (refreshIcon) {
        refreshIcon.classList.remove('animate-spin');
    }
    if (refreshText) {
        refreshText.textContent = 'Refresh';
    }
}

function updatePaginationInfo() {
    const validOffers = getValidOffers();
    const totalItems = validOffers.length;
    const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage));
    const previousPage = currentPage;

    if (previousPage !== currentPage) {
        debugPaginationChange('updatePaginationInfo', previousPage, currentPage);
    }

    if (currentPageSpan) currentPageSpan.textContent = totalPages > 0 ? currentPage : 0;
    if (totalPagesSpan) totalPagesSpan.textContent = totalPages;

    const showPrev = currentPage > 1;
    const showNext = currentPage < totalPages;

    if (prevPageButton) {
        prevPageButton.style.display = showPrev ? 'inline-flex' : 'none';
        prevPageButton.disabled = !showPrev;
    }

    if (nextPageButton) {
        nextPageButton.style.display = showNext ? 'inline-flex' : 'none';
        nextPageButton.disabled = !showNext;
    }

    if (newEntriesCountSpan) {
        newEntriesCountSpan.textContent = totalItems;
    }
}

function updatePaginationControls(totalPages) {
    prevPageButton.style.display = currentPage > 1 ? 'inline-flex' : 'none';
    nextPageButton.style.display = currentPage < totalPages ? 'inline-flex' : 'none';
    currentPageSpan.textContent = currentPage;
    totalPagesSpan.textContent = totalPages;
}

function updateProfitLoss(row, fromCoin, toCoin, fromAmount, toAmount, isOwnOffer) {
    const profitLossElement = row.querySelector('.profit-loss');
    if (!profitLossElement) {
        return;
    }

    if (!fromCoin || !toCoin) {
        profitLossElement.textContent = 'N/A';
        profitLossElement.className = 'profit-loss text-lg font-bold text-gray-300';
        return;
    }

    calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer)
        .then(percentDiff => {
            if (percentDiff === null || isNaN(percentDiff)) {
                profitLossElement.textContent = 'N/A';
                profitLossElement.className = 'profit-loss text-lg font-bold text-gray-300';
                return;
            }

            const percentDiffDisplay = percentDiff === "0.00" ? "0.00" :
                                     (percentDiff > 0 ? percentDiff : -percentDiff);

            const colorClass = getProfitColorClass(percentDiff);
            profitLossElement.textContent = `${percentDiffDisplay.toFixed(2)}%`;
            profitLossElement.className = `profit-loss text-lg font-bold ${colorClass}`;

            const tooltipId = `percentage-tooltip-${row.getAttribute('data-offer-id')}`;
            const tooltipElement = document.getElementById(tooltipId);
            if (tooltipElement) {
                const tooltipContent = createTooltipContent(isSentOffers || isOwnOffer, fromCoin, toCoin, fromAmount, toAmount);
                tooltipElement.innerHTML = `
                    <div class="tooltip-content">
                        ${tooltipContent}
                    </div>
                    <div class="tooltip-arrow" data-popper-arrow></div>
                `;
            }
        })
        .catch(error => {
            profitLossElement.textContent = 'N/A';
            profitLossElement.className = 'profit-loss text-lg font-bold text-gray-300';
        });
}

function updateCoinFilterImages() {
    return;
}

function updateClearFiltersButton() {
    const clearButton = document.getElementById('clearFilters');
    if (clearButton) {
        const hasFilters = hasActiveFilters();
        clearButton.classList.toggle('opacity-50', !hasFilters);
        clearButton.disabled = !hasFilters;

        if (hasFilters) {
            clearButton.classList.add('hover:bg-green-600', 'hover:text-white');
            clearButton.classList.remove('cursor-not-allowed');
        } else {
            clearButton.classList.remove('hover:bg-green-600', 'hover:text-white');
            clearButton.classList.add('cursor-not-allowed');
        }
    }
}

function cleanupRow(row) {
  if (!row) return;

  const tooltipTriggers = row.querySelectorAll('[data-tooltip-trigger-id]');
  tooltipTriggers.forEach(trigger => {
    if (window.TooltipManager) {
      window.TooltipManager.destroy(trigger);
    }
  });

  CleanupManager.removeListenersByElement(row);

  while (row.attributes && row.attributes.length > 0) {
    row.removeAttribute(row.attributes[0].name);
  }

  while (row.firstChild) {
    const child = row.firstChild;
    row.removeChild(child);
  }
}

function cleanupTable() {
  if (!offersBody) return;

  const existingRows = Array.from(offersBody.querySelectorAll('tr'));

  existingRows.forEach(row => cleanupRow(row));

  offersBody.innerHTML = '';

  if (window.TooltipManager) {
    window.TooltipManager.cleanup();
  }
}

function handleNoOffersScenario() {
    const hasFilters = hasActiveFilters();

    stopRefreshAnimation();

    const existingRows = offersBody.querySelectorAll('tr');
    existingRows.forEach(row => {
        cleanupRow(row);
    });

    if (hasFilters) {
        offersBody.innerHTML = `
            <tr>
                <td colspan="9" class="text-center py-8">
                    <div class="flex items-center justify-center text-gray-500 dark:text-white">
                        No offers match the selected filters. Try different filter options or
                        <button onclick="clearFilters()" class="ml-1 text-blue-500 hover:text-blue-700 font-semibold">
                            clear filters
                        </button>
                    </div>
                </td>
            </tr>`;
    } else {
        offersBody.innerHTML = `
            <tr>
                <td colspan="9" class="text-center py-8 text-gray-500 dark:text-white">
                    No active offers available.
                </td>
            </tr>`;
    }
}

async function updateOffersTable(options = {}) {
    try {

        if (isPaginationInProgress && !options.fromPaginationClick) {
            console.log('Skipping table update during pagination operation');
            return;
        }

        const isIncrementalUpdate = options.incremental === true;
        if (!options.skipSkeleton && !isIncrementalUpdate && offersBody) {
            offersBody.innerHTML = '<tr><td colspan="10" class="text-center py-8 text-gray-500 dark:text-gray-400"><div class="animate-pulse">Loading offers...</div></td></tr>';
        }

        if (window.TooltipManager) {
            requestAnimationFrame(() => window.TooltipManager.cleanup());
        }

        const validOffers = getValidOffers();
        if (validOffers.length === 0) {
            handleNoOffersScenario();
            return;
        }

        const totalPages = Math.ceil(validOffers.length / itemsPerPage);

        if (!options.fromPaginationClick) {
            const oldPage = currentPage;
            currentPage = Math.max(1, Math.min(currentPage, totalPages));
            if (oldPage !== currentPage) {
                debugPaginationChange('updateOffersTable auto-adjust', oldPage, currentPage);
            }
        }

        const startIndex = (currentPage - 1) * itemsPerPage;
        const endIndex = Math.min(startIndex + itemsPerPage, validOffers.length);
        const itemsToDisplay = validOffers.slice(startIndex, endIndex);

        const fragment = document.createDocumentFragment();

        const BATCH_SIZE = 10;
        for (let i = 0; i < itemsToDisplay.length; i += BATCH_SIZE) {
            const batch = itemsToDisplay.slice(i, i + BATCH_SIZE);

            const batchPromises = batch.map(offer =>
                offer.addr_from ? IdentityManager.getIdentityData(offer.addr_from) : Promise.resolve(null)
            );

            const batchIdentities = await Promise.all(batchPromises);

            batch.forEach((offer, index) => {
                const row = createTableRow(offer, batchIdentities[index]);
                if (row) fragment.appendChild(row);
            });

        }

        if (offersBody) {
            if (isIncrementalUpdate && offersBody.children.length > 0) {
                
                const existingRows = Array.from(offersBody.querySelectorAll('tr[data-offer-id]'));
                const newRows = Array.from(fragment.querySelectorAll('tr[data-offer-id]'));

                const existingMap = new Map(existingRows.map(row => [row.getAttribute('data-offer-id'), row]));
                const newMap = new Map(newRows.map(row => [row.getAttribute('data-offer-id'), row]));

                existingRows.forEach(row => {
                    const offerId = row.getAttribute('data-offer-id');
                    if (!newMap.has(offerId)) {
                        cleanupRow(row);
                        row.remove();
                    }
                });

                newRows.forEach((newRow, index) => {
                    const offerId = newRow.getAttribute('data-offer-id');
                    const existingRow = existingMap.get(offerId);

                    if (existingRow) {
                        
                        const currentIndex = Array.from(offersBody.children).indexOf(existingRow);
                        if (currentIndex !== index) {
                            
                            if (index >= offersBody.children.length) {
                                offersBody.appendChild(existingRow);
                            } else {
                                offersBody.insertBefore(existingRow, offersBody.children[index]);
                            }
                        }
                        
                    } else {
                        
                        if (index >= offersBody.children.length) {
                            offersBody.appendChild(newRow);
                        } else {
                            offersBody.insertBefore(newRow, offersBody.children[index]);
                        }
                    }
                });
            } else {
                
                const existingRows = offersBody.querySelectorAll('tr');
                existingRows.forEach(row => cleanupRow(row));
                offersBody.textContent = '';
                offersBody.appendChild(fragment);
            }
        }

        initializeTooltipsInBatches();

        CleanupManager.setTimeout(() => {
            updateRowTimes();
            updatePaginationInfo();
            updateProfitLossDisplays();
        }, 10);

        CleanupManager.setTimeout(() => {
            if (tableRateModule?.initializeTable) {
                tableRateModule.initializeTable();
            }
        }, 50);

        lastRefreshTime = Date.now();
        updateLastRefreshTime();

    } catch (error) {
        console.error('[Debug] Error in updateOffersTable:', error);
        handleTableError();
    }
}

function updateProfitLossDisplays() {
    
    const rows = document.querySelectorAll('[data-offer-id]');
    const updates = [];

    rows.forEach(row => {
        const offerId = row.getAttribute('data-offer-id');
        const offer = jsonData.find(o => o.offer_id === offerId);
        if (!offer) return;

        const fromAmount = parseFloat(offer.amount_from) || 0;
        const toAmount = parseFloat(offer.amount_to) || 0;

        updates.push({
            row,
            offerId,
            offer,
            fromAmount,
            toAmount
        });
    });

    updates.forEach(({ row, offerId, offer, fromAmount, toAmount }) => {
        updateProfitLoss(row, offer.coin_from, offer.coin_to, fromAmount, toAmount, offer.is_own_offer);

        const rateTooltipId = `tooltip-rate-${offerId}`;
        const rateTooltip = document.getElementById(rateTooltipId);
        if (rateTooltip) {
            const tooltipContent = createCombinedRateTooltip(offer, offer.coin_from, offer.coin_to, offer.is_own_offer);
            rateTooltip.innerHTML = tooltipContent;
        }
    });
}

function handleTableError() {
    offersBody.innerHTML = `
        <tr>
            <td colspan="8" class="text-center py-4 text-gray-500">
                <div class="flex flex-col items-center justify-center gap-2">
                    <span>An error occurred while updating the table.</span>
                    <span class="text-sm">The table will continue to function with cached data.</span>
                </div>
            </td>
        </tr>`;
}

function getIdentityInfo(address, identity) {
    if (!identity) {
        return {
            displayAddr: address ? `${address.substring(0, 10)}...` : 'Unspecified',
            fullAddress: address || '',
            label: '',
            note: '',
            automationOverride: 0,
            stats: {
                sentBidsSuccessful: 0,
                recvBidsSuccessful: 0,
                sentBidsRejected: 0,
                recvBidsRejected: 0,
                sentBidsFailed: 0,
                recvBidsFailed: 0
            }
        };
    }

    return {
        displayAddr: address ? `${address.substring(0, 10)}...` : 'Unspecified',
        fullAddress: address || '',
        label: identity.label || '',
        note: identity.note || '',
        automationOverride: identity.automation_override || 0,
        stats: {
            sentBidsSuccessful: identity.num_sent_bids_successful || 0,
            recvBidsSuccessful: identity.num_recv_bids_successful || 0,
            sentBidsRejected: identity.num_sent_bids_rejected || 0,
            recvBidsRejected: identity.num_recv_bids_rejected || 0,
            sentBidsFailed: identity.num_sent_bids_failed || 0,
            recvBidsFailed: identity.num_recv_bids_failed || 0
        }
    };
}

function createTableRow(offer, identity = null) {
   const row = document.createElement('tr');
   const uniqueId = `${offer.offer_id}_${offer.created_at}`;

   row.className = 'relative opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600';
   row.setAttribute('data-offer-id', uniqueId);

   const {
       coin_from: coinFrom,
       coin_to: coinTo,
       created_at: createdAt,
       expire_at: expireAt,
       amount_from: amountFrom,
       amount_to: amountTo,
       is_own_offer: isOwnOffer,
       is_revoked: isRevoked,
       is_public: isPublic
   } = offer;

   let coinFromSymbol, coinToSymbol;

   if (window.CoinManager) {
       coinFromSymbol = window.CoinManager.getSymbol(coinFrom) || coinFrom.toLowerCase();
       coinToSymbol = window.CoinManager.getSymbol(coinTo) || coinTo.toLowerCase();
   } else {
       coinFromSymbol = coinFrom.toLowerCase();
       coinToSymbol = coinTo.toLowerCase();
   }

   let coinFromDisplay, coinToDisplay;

   if (window.CoinManager) {
       coinFromDisplay = window.CoinManager.getDisplayName(coinFrom) || coinFrom;
       coinToDisplay = window.CoinManager.getDisplayName(coinTo) || coinTo;
   } else {
       coinFromDisplay = coinFrom;
       coinToDisplay = coinTo;
       if (coinFromDisplay.toLowerCase() === 'zcoin') coinFromDisplay = 'Firo';
       if (coinToDisplay.toLowerCase() === 'zcoin') coinToDisplay = 'Firo';
   }

   const postedTime = formatTime(createdAt, true);
   const expiresIn = formatTime(expireAt);
   const currentTime = Math.floor(Date.now() / 1000);
   const isActuallyExpired = currentTime > expireAt;
   const fromAmount = parseFloat(amountFrom) || 0;
   const toAmount = parseFloat(amountTo) || 0;

   row.innerHTML = `
       ${!isPublic ? createPrivateIndicator() : '<td class="w-0 p-0 m-0"></td>'}
       ${createTimeColumn(offer, postedTime, expiresIn)}
       ${createDetailsColumn(offer, identity)}
       ${createTakerAmountColumn(offer, coinTo, coinFrom)}
       ${createSwapColumn(offer, coinFromDisplay, coinToDisplay, coinFromSymbol, coinToSymbol)}
       ${createOrderbookColumn(offer, coinFrom)}
       ${createRateColumn(offer, coinFrom, coinTo)}
       ${createPercentageColumn(offer)}
       ${createActionColumn(offer, isActuallyExpired)}
       ${createTooltips(
           offer,
           isOwnOffer,
           coinFrom,
           coinTo,
           fromAmount,
           toAmount,
           postedTime,
           expiresIn,
           isActuallyExpired,
           Boolean(isRevoked),
           identity
       )}
   `;

   updateTooltipTargets(row, uniqueId);
   updateProfitLoss(row, coinFrom, coinTo, fromAmount, toAmount, isOwnOffer);

   return row;
}

function createPrivateIndicator() {
    return `<td class="relative w-0 p-0 m-0">
        <div class="absolute top-0 bottom-0 left-0 w-1 bg-red-700" style="min-height: 100%;"></div>
    </td>`;
}

function createTimeColumn(offer, postedTime, expiresIn) {
    const now = Math.floor(Date.now() / 1000);
    const timeLeft = offer.expire_at - now;

    let strokeColor = '#10B981';
    if (timeLeft <= 300) {
        strokeColor = '#9CA3AF';
    } else if (timeLeft <= 1800) {
        strokeColor = '#3B82F6';
    }

    return `
        <td class="py-3 pl-1 pr-2 text-xs whitespace-nowrap">
            <div class="flex items-center">
                <div class="relative" data-tooltip-target="tooltip-active${escapeHtml(offer.offer_id)}">
                    <svg alt="" class="w-5 h-5 rounded-full mr-4 cursor-pointer" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
                        <g stroke-linecap="round" stroke-width="2" fill="none" stroke="${strokeColor}" stroke-linejoin="round">
                            <circle cx="12" cy="12" r="11"></circle>
                            <polyline points="12,6 12,12 18,12" stroke="${strokeColor}"></polyline>
                        </g>
                    </svg>
                </div>
                <div class="flex flex-col hidden xl:block">
                    <div class="text-xs whitespace-nowrap"><span class="bold">Posted:</span> ${escapeHtml(postedTime)}</div>
                    <div class="text-xs whitespace-nowrap"><span class="bold">Expires in:</span> ${escapeHtml(expiresIn)}</div>
                </div>
            </div>
        </td>
    `;
}

function truncateText(text, maxLength = 15) {
    if (typeof text !== 'string') return '';
    return text.length > maxLength
        ? text.slice(0, maxLength) + '...'
        : text;
}

function createDetailsColumn(offer, identity = null) {
    const addrFrom = offer.addr_from || '';
    const identityInfo = getIdentityInfo(addrFrom, identity);

    const showPublicPrivateTags = originalJsonData.some(o => o.is_public !== offer.is_public);

    const tagClass = offer.is_public
        ? 'bg-green-600 dark:bg-green-600'
        : 'bg-red-500 dark:bg-red-500';
    const tagText = offer.is_public ? 'Public' : 'Private';

    const displayIdentifier = truncateText(
        identityInfo.label || addrFrom || 'Unspecified'
    );

    const identifierTextClass = identityInfo.label
        ? 'dark:text-white'
        : 'monospace';

    return `
        <td class="py-8 px-4 text-xs text-left hidden xl:block">
            <div class="flex flex-col gap-2 relative">
                ${showPublicPrivateTags ? `<span class="inline-flex pl-6 pr-6 py-1 justify-center text-[10px] w-1/4 font-medium text-gray-100 rounded-md ${tagClass}">${tagText}</span>
                ` : ''}

                <a data-tooltip-target="tooltip-recipient-${escapeHtml(offer.offer_id)}" href="/identity/${escapeHtml(addrFrom)}" class="flex items-center">
                    <svg class="w-4 h-4 mr-2 text-gray-400 dark:text-white" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                     <path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
                    </svg>
                    <span class="${identifierTextClass}">
                        ${escapeHtml(displayIdentifier)}
                    </span>
                </a>
            </div>
        </td>
    `;
}

function createTakerAmountColumn(offer, coinTo) {
    const fromAmount = parseFloat(offer.amount_to);
    const toSymbol = getCoinSymbol(coinTo);
    return `
        <td class="py-0">
            <div class="py-3 px-4 text-left">
                <a data-tooltip-target="tooltip-wallet${escapeHtml(offer.offer_id)}" href="/wallet/${escapeHtml(toSymbol)}" class="items-center monospace">
                    <div class="pr-2">
                        <div class="text-sm font-semibold">${fromAmount.toFixed(4)}</div>
                        <div class="text-sm text-gray-500 dark:text-gray-400">${coinTo}</div>
                    </div>
                </a>
            </div>
        </td>
    `;
}

function createSwapColumn(offer, coinFromDisplay, coinToDisplay, coinFromSymbol, coinToSymbol) {
    const getImageFilename = (symbol, displayName) => {
        if (displayName.toLowerCase() === 'zcoin' || displayName.toLowerCase() === 'firo') {
            return 'Firo.png';
        }
        return `${displayName.replace(' ', '-')}.png`;
    };

    return `
        <td class="py-0 px-0 text-right text-sm">
            <a data-tooltip-target="tooltip-offer${offer.offer_id}" href="/offer/${offer.offer_id}">
                <div class="flex items-center justify-evenly monospace">
                    <span class="inline-flex mr-3 ml-3 align-middle items-center justify-center w-18 h-20 rounded">
                        <img class="h-12" src="/static/images/coins/${getImageFilename(coinToSymbol, coinToDisplay)}" alt="${coinToDisplay}">
                    </span>
                    <svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                        <path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>
                    </svg>
                    <span class="inline-flex ml-3 mr-3 align-middle items-center justify-center w-18 h-20 rounded">
                        <img class="h-12" src="/static/images/coins/${getImageFilename(coinFromSymbol, coinFromDisplay)}" alt="${coinFromDisplay}">
                    </span>
                </div>
            </a>
        </td>
    `;
}

function createOrderbookColumn(offer, coinFrom) {
    const toAmount = parseFloat(offer.amount_from);
    const fromSymbol = getCoinSymbol(coinFrom);
    return `
        <td class="p-0">
            <div class="py-3 px-4 text-right">
                <a data-tooltip-target="tooltip-wallet-maker${escapeHtml(offer.offer_id)}" href="/wallet/${escapeHtml(fromSymbol)}" class="items-center monospace">
                    <div class="pr-2">
                        <div class="text-sm font-semibold">${toAmount.toFixed(4)}</div>
                        <div class="text-sm text-gray-500 dark:text-gray-400">${coinFrom}</div>
                    </div>
                </a>
            </div>
        </td>
    `;
}

function createRateColumn(offer, coinFrom, coinTo) {
    const rate = parseFloat(offer.rate) || 0;
    const inverseRate = rate ? (1 / rate) : 0;

    const toSymbolKey = getPriceKey(coinTo);
    let toPriceUSD = latestPrices && latestPrices[toSymbolKey] ? latestPrices[toSymbolKey].usd : null;

    if (!toPriceUSD || isNaN(toPriceUSD)) {
        toPriceUSD = tableRateModule.getFallbackValue(toSymbolKey);
    }

    const rateInUSD = toPriceUSD && !isNaN(toPriceUSD) && !isNaN(rate) ? rate * toPriceUSD : null;
    const fromSymbol = getCoinSymbol(coinFrom);
    const toSymbol = getCoinSymbol(coinTo);

    return `
        <td class="py-3 semibold monospace text-xs text-right items-center rate-table-info">
            <div class="relative">
                <div class="flex flex-col items-end pr-3" data-tooltip-target="tooltip-rate-${offer.offer_id}">
                    <span class="text-sm bold text-gray-700 dark:text-white">
                        ${rateInUSD !== null ? `$${rateInUSD.toFixed(2)} USD` : 'N/A'}
                    </span>
                    <span class="bold text-gray-700 dark:text-white">
                        ${rate.toFixed(8)} ${toSymbol}/${fromSymbol}
                    </span>
                    <span class="semibold text-gray-400 dark:text-gray-300">
                        ${inverseRate.toFixed(8)} ${fromSymbol}/${toSymbol}
                    </span>
                </div>
            </div>
        </td>
    `;
}

function createPercentageColumn(offer) {
    return `
        <td class="py-3 px-2 bold text-sm text-center monospace items-center rate-table-info">
            <div class="relative" data-tooltip-target="percentage-tooltip-${offer.offer_id}">
                <div class="profittype">
                    <span class="profit-loss text-lg font-bold" data-offer-id="${offer.offer_id}">
                        Calculating...
                    </span>
                </div>
            </div>
        </td>
    `;
}

function createActionColumn(offer, isActuallyExpired = false) {
    const isRevoked = Boolean(offer.is_revoked);
    const isTreatedAsSentOffer = offer.is_own_offer;

    let buttonClass, buttonText;

    if (isRevoked) {
        buttonClass = 'bg-red-500 text-white hover:bg-red-600 transition duration-200';
        buttonText = 'Revoked';
    } else if (isActuallyExpired && isSentOffers) {
        buttonClass = 'bg-gray-400 text-white dark:border-gray-300 text-white hover:bg-red-700 transition duration-200';
        buttonText = 'Expired';
    } else if (isTreatedAsSentOffer) {
        buttonClass = 'bg-gray-300 bold text-white bold hover:bg-green-600 transition duration-200';
        buttonText = 'Edit';
    } else {
        buttonClass = 'bg-blue-500 text-white hover:bg-green-600 transition duration-200';
        buttonText = 'Swap';
    }

    return `
        <td class="py-6 px-2 text-center">
            <div class="flex justify-center items-center h-full">
                <a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md ${buttonClass}"
                   href="/offer/${offer.offer_id}">
                    ${buttonText}
                </a>
            </div>
        </td>
    `;
}

function createTooltips(offer, treatAsSentOffer, coinFrom, coinTo, fromAmount, toAmount, postedTime, expiresIn, isActuallyExpired, isRevoked, identity = null) {
    const uniqueId = `${offer.offer_id}_${offer.created_at}`;

    const addrFrom = offer.addr_from || '';
    const identityInfo = getIdentityInfo(addrFrom, identity);

    const totalBids = identity ? (
        identityInfo.stats.sentBidsSuccessful +
        identityInfo.stats.recvBidsSuccessful +
        identityInfo.stats.sentBidsFailed +
        identityInfo.stats.recvBidsFailed +
        identityInfo.stats.sentBidsRejected +
        identityInfo.stats.recvBidsRejected
    ) : 0;

    const successRate = totalBids ? (
        ((identityInfo.stats.sentBidsSuccessful + identityInfo.stats.recvBidsSuccessful) / totalBids) * 100
    ).toFixed(1) : 0;

    const combinedRateTooltip = createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer);
    const percentageTooltipContent = createTooltipContent(treatAsSentOffer, coinFrom, coinTo, fromAmount, toAmount);

    return `
        <div id="tooltip-active-${uniqueId}" role="tooltip" class="inline-block absolute hidden z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
            <div class="active-revoked-expired">
                <span class="bold">
                    <div class="text-xs"><span class="bold">Posted:</span> ${postedTime}</div>
                    <div class="text-xs"><span class="bold">Expires in:</span> ${expiresIn}</div>
                    ${isRevoked ? '<div class="text-xs text-red-300"><span class="bold">Status:</span> Revoked</div>' : ''}
                </span>
            </div>
            <div class="mt-5 text-xs">
                <p class="font-bold mb-3">Time Indicator Colors:</p>
                <p class="flex items-center">
                    <svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <g stroke-linecap="round" stroke-width="2" fill="none" stroke="#10B981" stroke-linejoin="round">
                            <circle cx="12" cy="12" r="11"></circle>
                            <polyline points="12,6 12,12 18,12" stroke="#10B981"></polyline>
                        </g>
                    </svg>
                    Green: More than 30 minutes left
                </p>
                <p class="flex items-center mt-3">
                    <svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round">
                            <circle cx="12" cy="12" r="11"></circle>
                            <polyline points="12,6 12,12 18,12" stroke="#3B82F6"></polyline>
                        </g>
                    </svg>
                    Blue: Between 5 and 30 minutes left
                </p>
                <p class="flex items-center mt-3 mb-3">
                    <svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <g stroke-linecap="round" stroke-width="2" fill="none" stroke="#9CA3AF" stroke-linejoin="round">
                            <circle cx="12" cy="12" r="11"></circle>
                            <polyline points="12,6 12,12 18,12" stroke="#9CA3AF"></polyline>
                        </g>
                    </svg>
                    Grey: Less than 5 minutes left or expired
                </p>
            </div>
        </div>

        <div id="tooltip-wallet-${uniqueId}" role="tooltip" class="inline-block absolute hidden z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
            <div class="active-revoked-expired">
                <span class="bold">${treatAsSentOffer ? 'My' : ''} ${coinTo} Wallet</span>
            </div>
        </div>

        <div id="tooltip-offer-${uniqueId}" role="tooltip" class="inline-block absolute hidden z-50 py-2 px-3 text-sm font-medium text-white ${isRevoked ? 'bg-red-500' : (offer.is_own_offer ? 'bg-gray-300' : 'bg-green-700')} rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
            <div class="active-revoked-expired">
                <span class="bold">
                    ${isRevoked ? 'Offer Revoked' : (offer.is_own_offer ? 'Edit Offer' : `Buy ${coinFrom}`)}
                </span>
            </div>
        </div>

        <div id="tooltip-wallet-maker-${uniqueId}" role="tooltip" class="inline-block absolute hidden z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
            <div class="active-revoked-expired">
                <span class="bold">${treatAsSentOffer ? 'My' : ''} ${coinFrom} Wallet</span>
            </div>
        </div>

        <div id="tooltip-rate-${uniqueId}" role="tooltip" class="inline-block absolute hidden z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
            <div class="tooltip-content">
                ${combinedRateTooltip}
            </div>
        </div>

        <div id="percentage-tooltip-${uniqueId}" role="tooltip" class="inline-block absolute hidden z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
            <div class="tooltip-content">
                ${percentageTooltipContent}
            </div>
        </div>

        ${createRecipientTooltip(uniqueId, identityInfo, identity, successRate, totalBids)}
    `;
}

function createRecipientTooltip(uniqueId, identityInfo, identity, successRate, totalBids) {

    const getSuccessRateColor = (rate) => {
        if (rate >= 80) return 'text-green-600';
        if (rate >= 60) return 'text-yellow-600';
        return 'text-red-600';
    };

    const truncateText = (text, maxLength) => {
        if (text.length <= maxLength) return text;
        return text.substring(0, maxLength) + '...';
    };

    return `
        <div id="tooltip-recipient-${uniqueId}" role="tooltip"
            class="fixed z-50 py-3 px-4 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip max-w-sm pointer-events-none">
            <div class="identity-info space-y-2">
                ${identityInfo.label ? `
                    <div class="border-b border-gray-400 pb-2">
                        <div class="text-white text-xs tracking-wide font-semibold">Label:</div>
                        <div class="text-white">${escapeHtml(identityInfo.label)}</div>
                    </div>
                ` : ''}

                <div class="space-y-1">
                    <div class="text-white text-xs tracking-wide font-semibold">Recipient Address:</div>
                    <div class="monospace text-xs break-all bg-gray-500 p-2 rounded-md text-white">
                        ${escapeHtml(identityInfo.fullAddress)}
                    </div>
                </div>

                ${identityInfo.note ? `
                    <div class="space-y-1 hidden">
                        <div class="text-white text-xs tracking-wide font-semibold">Note:</div>
                        <div class="text-white text-sm italic" title="${escapeHtml(identityInfo.note)}">
                            ${escapeHtml(truncateText(identityInfo.note, 150))}
                        </div>
                    </div>
                ` : ''}

                ${identity ? `
                    <div class= pt-2 mt-2">
                        <div class="text-white text-xs tracking-wide font-semibold mb-2">Swap History:</div>
                        <div class="grid grid-cols-2 gap-2">
                            <div class="text-center p-2 bg-gray-500 rounded-md">
                                <div class="text-lg font-bold ${getSuccessRateColor(successRate)}">${successRate}%</div>
                                <div class="text-xs text-white">Success Rate</div>
                            </div>
                            <div class="text-center p-2 bg-gray-500 rounded-md">
                                <div class="text-lg font-bold text-blue-500">${totalBids}</div>
                                <div class="text-xs text-white">Total Trades</div>
                            </div>
                        </div>
                        <div class="grid grid-cols-3 gap-2 mt-2 text-center text-xs">
                            <div>
                                <div class="text-green-600 font-semibold">
                                    ${identityInfo.stats.sentBidsSuccessful + identityInfo.stats.recvBidsSuccessful}
                                </div>
                                <div class="text-white">Successful</div>
                            </div>
                            <div>
                                <div class="text-yellow-600 font-semibold">
                                    ${identityInfo.stats.sentBidsRejected + identityInfo.stats.recvBidsRejected}
                                </div>
                                <div class="text-white">Rejected</div>
                            </div>
                            <div>
                                <div class="text-red-600 font-semibold">
                                    ${identityInfo.stats.sentBidsFailed + identityInfo.stats.recvBidsFailed}
                                </div>
                                <div class="text-white">Failed</div>
                            </div>
                        </div>
                    </div>
                ` : ''}
            </div>
            <div class="tooltip-arrow" data-popper-arrow></div>
        </div>`;
}

function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmount, isOwnOffer) {
    if (!coinFrom || !coinTo) {
        return `<p class="font-bold mb-1">Unable to calculate profit/loss</p>
                <p>Invalid coin data.</p>`;
    }

    fromAmount = parseFloat(fromAmount) || 0;
    toAmount = parseFloat(toAmount) || 0;

    const getPriceKey = (coin) => {
        if (!coin) return null;
        if (window.CoinUtils) {
            return window.CoinUtils.normalizeCoinName(coin, latestPrices);
        }
        return coin.toLowerCase();
    };

    const fromSymbol = getPriceKey(coinFrom);
    const toSymbol = getPriceKey(coinTo);

    let fromPriceUSD = latestPrices && fromSymbol ? latestPrices[fromSymbol]?.usd : null;
    let toPriceUSD = latestPrices && toSymbol ? latestPrices[toSymbol]?.usd : null;

    if (!fromPriceUSD && window.tableRateModule) {
        fromPriceUSD = tableRateModule.getFallbackValue(fromSymbol);
    }
    if (!toPriceUSD && window.tableRateModule) {
        toPriceUSD = tableRateModule.getFallbackValue(toSymbol);
    }

    if (fromPriceUSD === null || toPriceUSD === null ||
        fromPriceUSD === undefined || toPriceUSD === undefined ||
        isNaN(fromPriceUSD) || isNaN(toPriceUSD)) {
        return `<p class="font-bold mb-1">Price Information Unavailable</p>
                <p>Current market prices are temporarily unavailable.</p>
                <p class="mt-2">You are ${isSentOffers ? 'selling' : 'buying'} ${fromAmount.toFixed(8)} ${coinFrom}
                for ${toAmount.toFixed(8)} ${coinTo}.</p>
                <p class="font-bold mt-2">Note:</p>
                <p>Profit/loss calculations will be available when price data is restored.</p>`;
    }

    const fromValueUSD = fromAmount * fromPriceUSD;
    const toValueUSD = toAmount * toPriceUSD;
    const profitUSD = toValueUSD - fromValueUSD;

    const marketRate = fromPriceUSD / toPriceUSD;
    const offerRate = toAmount / fromAmount;
    let percentDiff;

    if (isSentOffers || isOwnOffer) {
        percentDiff = ((toValueUSD / fromValueUSD) - 1) * 100;
    } else {
        percentDiff = (-((toValueUSD / fromValueUSD) - 1)) * 100;
    }

    const percentDiffDisplay = percentDiff === "0.00" ? "0.00" :
                             (percentDiff > 0 ? percentDiff : -percentDiff);

    const profitLabel = percentDiff > 0 ? "Max Profit" : "Max Loss";
    const actionLabel = (isSentOffers || isOwnOffer) ? "selling" : "buying";
    const directionLabel = (isSentOffers || isOwnOffer) ? "receiving" : "paying";

    return `
        <p class="font-bold mb-1">Profit/Loss Calculation:</p>
        <p>You are ${actionLabel} ${fromAmount.toFixed(8)} ${coinFrom} ($${fromValueUSD.toFixed(2)} USD) <br/> and ${directionLabel} ${toAmount.toFixed(8)} ${coinTo} ($${toValueUSD.toFixed(2)} USD).</p>
        <p class="mt-1">Percentage difference: ${percentDiffDisplay.toFixed(2)}%</p>
        <p>${profitLabel}: ${Math.abs(profitUSD).toFixed(2)} USD</p>
        <p class="font-bold mt-2">Calculation:</p>
        <p>Percentage = ${(isSentOffers || isOwnOffer) ?
            "((To Amount in USD / From Amount in USD) - 1) * 100" :
            "(-((To Amount in USD / From Amount in USD) - 1)) * 100"}</p>
        <p>USD ${profitLabel} = To Amount in USD - From Amount in USD</p>
        <p class="font-bold mt-1">Interpretation:</p>
        ${(isSentOffers || isOwnOffer) ? `
            <p><span class="text-green-500">Green:</span> You're selling above market rate (profitable)</p>
            <p><span class="text-red-500">Red:</span> You're selling below market rate (loss)</p>
        ` : `
            <p><span class="text-green-500">Green:</span> You're buying below market rate (savings)</p>
            <p><span class="text-red-500">Red:</span> You're buying above market rate (premium)</p>
        `}
        <p class="mt-1"><strong>Note:</strong> ${(isSentOffers || isOwnOffer) ?
            "As a seller, a green percentage means <br/> you're selling for more than the current market rate." :
            "As a buyer, a green percentage indicates </br> potential savings compared to current market rate."}</p>
        <p class="mt-1"><strong>Market Rate:</strong> 1 ${coinFrom} = ${marketRate.toFixed(8)} ${coinTo}</p>
        <p><strong>Offer Rate:</strong> 1 ${coinFrom} = ${offerRate.toFixed(8)} ${coinTo}</p>
    `;
}

function createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer) {
    const rate = parseFloat(offer.rate) || 0;
    const inverseRate = rate ? (1 / rate) : 0;

    const getPriceKey = (coin) => {
        if (!coin) return null;
        if (window.CoinUtils) {
            return window.CoinUtils.normalizeCoinName(coin, latestPrices);
        }
        return coin.toLowerCase();
    };

    const fromSymbol = getPriceKey(coinFrom);
    const toSymbol = getPriceKey(coinTo);

    let fromPriceUSD = latestPrices && fromSymbol ? latestPrices[fromSymbol]?.usd : null;
    let toPriceUSD = latestPrices && toSymbol ? latestPrices[toSymbol]?.usd : null;

    if (!fromPriceUSD && window.tableRateModule) {
        fromPriceUSD = tableRateModule.getFallbackValue(fromSymbol);
    }
    if (!toPriceUSD && window.tableRateModule) {
        toPriceUSD = tableRateModule.getFallbackValue(toSymbol);
    }

    if (fromPriceUSD === null || toPriceUSD === null ||
        fromPriceUSD === undefined || toPriceUSD === undefined ||
        isNaN(fromPriceUSD) || isNaN(toPriceUSD)) {
        return `
            <p class="font-bold mb-1">Exchange Rate Information</p>
            <p>Market price data is temporarily unavailable.</p>
            <p class="font-bold mt-2">Current Offer Rates:</p>
            <p>1 ${coinFrom} = ${rate.toFixed(8)} ${coinTo}</p>
            <p>1 ${coinTo} = ${inverseRate.toFixed(8)} ${coinFrom}</p>
            <p class="font-bold mt-2">Note:</p>
            <p>Market comparison will be available when price data is restored.</p>
        `;
    }

    const rateInUSD = rate * toPriceUSD;
    const marketRate = fromPriceUSD / toPriceUSD;
    const percentDiff = marketRate ? ((rate - marketRate) / marketRate) * 100 : 0;
    const percentDiffDisplay = percentDiff === "0.00" ? "0.00" :
    (percentDiff > 0 ? percentDiff : -percentDiff);
    const aboveOrBelow = percentDiff > 0 ? "above" : percentDiff < 0 ? "below" : "at";
    const action = treatAsSentOffer ? "selling" : "buying";

    return `
        <p class="font-bold mb-1">Exchange Rate Explanation:</p>
        <p>This offer is ${action} ${coinFrom} for ${coinTo} <br/>at a rate that is ${percentDiffDisplay.toFixed(2)}% ${aboveOrBelow} market price.</p>
        <p class="font-bold mt-1">Exchange Rates:</p>
        <p>1 ${coinFrom} = ${rate.toFixed(8)} ${coinTo}</p>
        <p>1 ${coinTo} = ${inverseRate.toFixed(8)} ${coinFrom}</p>
        <p class="font-bold mt-2">USD Equivalent:</p>
        <p>1 ${coinFrom} = $${rateInUSD.toFixed(2)} USD</p>
        <p class="font-bold mt-2">Current market prices:</p>
        <p>${coinFrom}: $${fromPriceUSD.toFixed(2)} USD</p>
        <p>${coinTo}: $${toPriceUSD.toFixed(2)} USD</p>
        <p class="mt-1">Market rate: 1 ${coinFrom} = ${marketRate.toFixed(8)} ${coinTo}</p>
    `;
}

function updateTooltipTargets(row, uniqueId) {
    const tooltipElements = [
        { prefix: 'tooltip-active', selector: '[data-tooltip-target^="tooltip-active"]' },
        { prefix: 'tooltip-recipient', selector: '[data-tooltip-target^="tooltip-recipient"]' },
        { prefix: 'tooltip-wallet', selector: '[data-tooltip-target^="tooltip-wallet"]' },
        { prefix: 'tooltip-offer', selector: '[data-tooltip-target^="tooltip-offer"]' },
        { prefix: 'tooltip-wallet-maker', selector: '[data-tooltip-target^="tooltip-wallet-maker"]' },
        { prefix: 'tooltip-rate', selector: '[data-tooltip-target^="tooltip-rate"]' },
        { prefix: 'percentage-tooltip', selector: '[data-tooltip-target^="percentage-tooltip"]' }
    ];

    tooltipElements.forEach(({ prefix, selector }) => {
        const element = row.querySelector(selector);
        if (element) {
            element.setAttribute('data-tooltip-target', `${prefix}-${uniqueId}`);
        }
    });
}

function applyFilters(options = {}) {
    if (window.filterTimeout) {
        clearTimeout(window.filterTimeout);
        window.filterTimeout = null;
    }

    try {
        window.filterTimeout = CleanupManager.setTimeout(() => {
            currentPage = 1;
            jsonData = filterAndSortData();
            updateOffersTable(options);
            updateClearFiltersButton();
            window.filterTimeout = null;
        }, 250);
    } catch (error) {
        console.error('Error in filter timeout:', error);
        window.filterTimeout = null;
    }
}

function clearFilters() {
    document.querySelectorAll('.coin-to-checkbox, .coin-from-checkbox').forEach(checkbox => {
        checkbox.checked = checkbox.value === 'any';
    });

    const statusSelect = document.getElementById('status');
    if (statusSelect) {
        statusSelect.value = 'any';
    }

    const sentFromSelect = document.getElementById('sent_from');
    if (sentFromSelect) {
        sentFromSelect.value = 'any';
    }

    const autoAcceptTypeSelect = document.getElementById('auto_accept_type');
    if (autoAcceptTypeSelect) {
        autoAcceptTypeSelect.value = 'any';
    }

    updateFilterButtonText('coin_to');
    updateFilterButtonText('coin_from');
    updateCoinBadges('coin_to');
    updateCoinBadges('coin_from');
    hideAllDropdowns();

    jsonData = [...originalJsonData];
    currentPage = 1;

    const storageKey = isSentOffers ? 'sentOffersTableSettings' : 'networkOffersTableSettings';
    localStorage.removeItem(storageKey);

    updateOffersTable();
    updateClearFiltersButton();
}

function hasActiveFilters() {
    const selectedCoinTo = getSelectedCoins('coin_to');
    const selectedCoinFrom = getSelectedCoins('coin_from');

    const hasCoinToFilter = selectedCoinTo.length > 0 && !(selectedCoinTo.length === 1 && selectedCoinTo[0] === 'any');
    const hasCoinFromFilter = selectedCoinFrom.length > 0 && !(selectedCoinFrom.length === 1 && selectedCoinFrom[0] === 'any');

    const statusSelect = document.getElementById('status');
    const sentFromSelect = document.getElementById('sent_from');
    const autoAcceptTypeSelect = document.getElementById('auto_accept_type');

    const hasStatusFilter = statusSelect && statusSelect.value !== 'any';
    const hasSentFromFilter = sentFromSelect && sentFromSelect.value !== 'any';
    const hasAutoAcceptTypeFilter = autoAcceptTypeSelect && autoAcceptTypeSelect.value !== 'any';

    return hasCoinToFilter || hasCoinFromFilter || hasStatusFilter || hasSentFromFilter || hasAutoAcceptTypeFilter;
}

function formatTimeLeft(timestamp) {
    return window.config.utils.formatTimeLeft(timestamp);
}

function getDisplayName(coinName) {
    if (window.CoinManager && window.CoinManager.getDisplayName) {
        return window.CoinManager.getDisplayName(coinName) || coinName;
    }
    return coinName;
}

function getCoinSymbolLowercase(coin) {
    if (typeof coin === 'string') {
        if (coin.toLowerCase() === 'bitcoin cash') {
            return 'bitcoin-cash';
        }
        return (window.config.coinMappings.nameToSymbol[coin] || coin).toLowerCase();
    } else if (coin && typeof coin === 'object' && coin.symbol) {
        return coin.symbol.toLowerCase();
    } else {
        return 'unknown';
    }
}

function getProfitColorClass(percentage) {
    const numericPercentage = parseFloat(percentage);
    if (numericPercentage > 0) return 'text-green-500';
    if (numericPercentage < 0) return 'text-red-500';
    if (numericPercentage === 0) return 'text-yellow-400';
    return 'text-white';
}

function isOfferExpired(offer) {
    if (isSentOffers) {
        return false;
    }
    const currentTime = Math.floor(Date.now() / 1000);
    const isExpired = offer.expire_at <= currentTime;
    return isExpired;
}

function formatTime(timestamp, addAgoSuffix = false) {
    return window.config.utils.formatTime(timestamp, addAgoSuffix);
}

function escapeHtml(unsafe) {
    return window.config.utils.escapeHtml(unsafe);
}

function getPriceKey(coin) {
    if (window.CoinManager) {
        return window.CoinManager.getPriceKey(coin);
    }
    if (window.CoinUtils) {
        return window.CoinUtils.normalizeCoinName(coin);
    }
    return coin ? coin.toLowerCase() : null;
}

function getCoinSymbol(fullName) {
    if (window.CoinManager) {
        return window.CoinManager.getSymbol(fullName) || fullName;
    }
    if (window.CoinUtils) {
        return window.CoinUtils.getCoinSymbol(fullName);
    }
    return fullName;
}

function initializeTableEvents() {
    const filterForm = document.getElementById('filterForm');
    if (filterForm) {
        CleanupManager.addListener(filterForm, 'submit', (e) => {
            e.preventDefault();
            applyFilters();
        });

        CleanupManager.addListener(filterForm, 'change', () => {
            applyFilters();
            updateClearFiltersButton();
        });
    }

    const coinToButton = document.getElementById('coin_to_button');
    const coinFromButton = document.getElementById('coin_from_button');
    const coinToDropdown = document.getElementById('coin_to_dropdown');
    const coinFromDropdown = document.getElementById('coin_from_dropdown');
    const statusSelect = document.getElementById('status');
    const sentFromSelect = document.getElementById('sent_from');

    if (coinToButton && coinToDropdown) {
        CleanupManager.addListener(coinToButton, 'click', (e) => {
            e.stopPropagation();
            toggleDropdown('coin_to');
        });

        const coinToCheckboxes = coinToDropdown.querySelectorAll('.coin-to-checkbox');
        coinToCheckboxes.forEach(checkbox => {
            CleanupManager.addListener(checkbox, 'change', (e) => {
                handleCoinCheckboxChange('coin_to', e.target);
            });
        });
    }

    if (coinFromButton && coinFromDropdown) {
        CleanupManager.addListener(coinFromButton, 'click', (e) => {
            e.stopPropagation();
            toggleDropdown('coin_from');
        });

        const coinFromCheckboxes = coinFromDropdown.querySelectorAll('.coin-from-checkbox');
        coinFromCheckboxes.forEach(checkbox => {
            CleanupManager.addListener(checkbox, 'change', (e) => {
                handleCoinCheckboxChange('coin_from', e.target);
            });
        });
    }

    CleanupManager.addListener(window, 'resize', () => {
        hideAllDropdowns();
    });

    CleanupManager.addListener(window, 'scroll', () => {
        hideAllDropdowns();
    });

    if (statusSelect) {
        CleanupManager.addListener(statusSelect, 'change', () => {
            applyFilters();
        });
    }

    if (sentFromSelect) {
        CleanupManager.addListener(sentFromSelect, 'change', () => {
            applyFilters();
        });
    }

    const autoAcceptTypeSelect = document.getElementById('auto_accept_type');
    if (autoAcceptTypeSelect) {
        CleanupManager.addListener(autoAcceptTypeSelect, 'change', () => {
            applyFilters();
        });
    }

    const clearFiltersBtn = document.getElementById('clearFilters');
    if (clearFiltersBtn) {
        CleanupManager.addListener(clearFiltersBtn, 'click', () => {
            clearFilters();
        });
    }

    const refreshButton = document.getElementById('refreshOffers');
    if (refreshButton) {
        let lastRefreshTime = 0;
        const REFRESH_COOLDOWN = 6000;
        let countdownInterval;

        CleanupManager.addListener(refreshButton, 'click', async () => {
            const now = Date.now();
            if (now - lastRefreshTime < REFRESH_COOLDOWN) {
                console.log('Refresh rate limited. Please wait before refreshing again.');
                const startTime = now;
                const refreshText = document.getElementById('refreshText');

                refreshButton.classList.remove('bg-blue-600', 'hover:bg-green-600', 'border-blue-500', 'hover:border-green-600');
                refreshButton.classList.add('bg-red-600', 'border-red-500', 'cursor-not-allowed');

                if (window.countdownInterval) clearInterval(window.countdownInterval);

                window.countdownInterval = CleanupManager.setInterval(() => {
                    const currentTime = Date.now();
                    const elapsedTime = currentTime - startTime;
                    const remainingTime = Math.ceil((REFRESH_COOLDOWN - elapsedTime) / 1000);

                    if (remainingTime <= 0) {
                        clearInterval(window.countdownInterval);
                        window.countdownInterval = null;
                        refreshText.textContent = 'Refresh';

                        refreshButton.classList.remove('bg-red-600', 'border-red-500', 'cursor-not-allowed');
                        refreshButton.classList.add('bg-blue-600', 'hover:bg-green-600', 'border-blue-500', 'hover:border-green-600');
                    } else {
                        refreshText.textContent = `Refresh (${remainingTime}s)`;
                    }
                }, 100);
                return;
            }

            lastRefreshTime = now;
            const refreshIcon = document.getElementById('refreshIcon');
            const refreshText = document.getElementById('refreshText');
            refreshButton.disabled = true;
            refreshIcon.classList.add('animate-spin');
            refreshText.textContent = 'Refreshing...';
            refreshButton.classList.add('opacity-75', 'cursor-wait');

            try {
                const cachedPrices = CacheManager.get('prices_coingecko');
                const previousPrices = cachedPrices ? cachedPrices.value : null;
                CacheManager.clear();
                window.isManualRefresh = true;
                const endpoint = isSentOffers ? '/json/sentoffers' : '/json/offers';
                const response = await fetch(endpoint);
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const newData = await response.json();
                const processedNewData = Array.isArray(newData) ? newData : Object.values(newData);
                jsonData = formatInitialData(processedNewData);
                originalJsonData = [...jsonData];
                const priceData = await fetchLatestPrices();
                if (!priceData && previousPrices) {
                    console.log('Using previous price data after failed refresh');
                    latestPrices = previousPrices;
                    applyFilters({ incremental: false });
                } else if (priceData) {
                    latestPrices = priceData;
                    applyFilters({ incremental: false });
                } else {
                    throw new Error('Unable to fetch price data');
                }
                updatePaginationInfo();
                lastRefreshTime = now;
                updateLastRefreshTime();

            } catch (error) {
                console.error('Error during manual refresh:', error);
                NetworkManager.handleNetworkError(error);
                ui.displayErrorMessage('Unable to refresh data. Previous data will be preserved.');

                const cachedData = CacheManager.get('prices_coingecko');
                if (cachedData?.value) {
                    latestPrices = cachedData.value;
                    applyFilters();
                }
            } finally {
                window.isManualRefresh = false;
                refreshButton.disabled = false;
                refreshIcon.classList.remove('animate-spin');
                refreshText.textContent = 'Refresh';
                refreshButton.classList.remove('opacity-75', 'cursor-wait');

                refreshButton.classList.remove('bg-red-600', 'border-red-500', 'cursor-not-allowed');
                refreshButton.classList.add('bg-blue-600', 'hover:bg-green-600', 'border-blue-500', 'hover:border-green-600');

                if (countdownInterval) {
                    clearInterval(countdownInterval);
                }
            }
        });
    }

    const prevPageButton = document.getElementById('prevPage');
    const nextPageButton = document.getElementById('nextPage');

    if (prevPageButton) {
        CleanupManager.addListener(prevPageButton, 'click', async () => {
            if (currentPage > 1 && !isPaginationInProgress) {
                try {
                    isPaginationInProgress = true;
                    const oldPage = currentPage;
                    currentPage--;
                    await updateOffersTable({ fromPaginationClick: true });
                    updatePaginationInfo();
                } finally {
                    CleanupManager.setTimeout(() => {
                        isPaginationInProgress = false;
                    }, 100);
                }
            }
        });
    }

    if (nextPageButton) {
        CleanupManager.addListener(nextPageButton, 'click', async () => {
            const validOffers = getValidOffers();
            const totalPages = Math.ceil(validOffers.length / itemsPerPage);
            if (currentPage < totalPages && !isPaginationInProgress) {
                try {
                    isPaginationInProgress = true;
                    const oldPage = currentPage;
                    currentPage++;
                    await updateOffersTable({ fromPaginationClick: true });
                    updatePaginationInfo();
                } finally {
                    CleanupManager.setTimeout(() => {
                        isPaginationInProgress = false;
                    }, 100);
                }
            }
        });
    }

    document.querySelectorAll('th[data-sortable="true"]').forEach(header => {
        CleanupManager.addListener(header, 'click', () => {
            const columnIndex = parseInt(header.getAttribute('data-column-index'));
            handleTableSort(columnIndex, header);
        });
    });
}

function handleTableSort(columnIndex, header) {
    if (currentSortColumn === columnIndex) {
        if (currentSortDirection === null) {
            currentSortDirection = 'desc';
        } else if (currentSortDirection === 'desc') {
            currentSortDirection = 'asc';
        } else if (currentSortDirection === 'asc') {
            currentSortColumn = null;
            currentSortDirection = null;
        }
    } else {
        currentSortColumn = columnIndex;
        currentSortDirection = 'desc';
    }

    document.querySelectorAll('th[data-sortable="true"]').forEach(th => {
        const columnSpan = th.querySelector('span:not(.sort-icon)');
        const icon = th.querySelector('.sort-icon');
        const thisColumnIndex = parseInt(th.getAttribute('data-column-index'));

        if (thisColumnIndex === columnIndex) {
            if (columnSpan) {
                columnSpan.classList.remove('text-gray-600', 'dark:text-gray-300');
                columnSpan.classList.add('text-blue-500', 'dark:text-blue-500');
            }
            if (icon) {
                icon.classList.remove('text-gray-600', 'dark:text-gray-400');
                icon.classList.add('text-blue-500', 'dark:text-blue-500');

                if (currentSortDirection === 'desc') {
                    icon.textContent = '↓';
                } else if (currentSortDirection === 'asc') {
                    icon.textContent = '↑';
                } else {
                    icon.textContent = '↓';
                    columnSpan.classList.remove('text-blue-500', 'dark:text-blue-500');
                    columnSpan.classList.add('text-gray-600', 'dark:text-gray-300');
                    icon.classList.remove('text-blue-500', 'dark:text-blue-500');
                    icon.classList.add('text-gray-600', 'dark:text-gray-400');
                }
            }
        } else {
            if (columnSpan) {
                columnSpan.classList.remove('text-blue-500', 'dark:text-blue-500');
                columnSpan.classList.add('text-gray-600', 'dark:text-gray-300');
            }
            if (icon) {
                icon.classList.remove('text-blue-500', 'dark:text-blue-500');
                icon.classList.add('text-gray-600', 'dark:text-gray-400');
                icon.textContent = '↓';
            }
        }
    });

    saveFilterSettings();

    if (window.sortTimeout) {
        clearTimeout(window.sortTimeout);
    }

    window.sortTimeout = CleanupManager.setTimeout(() => {
        applyFilters();
    }, 100);
}

function startAutoRefresh() {
    const REFRESH_INTERVAL = 2 * 60 * 1000; // 2 minutes

    if (autoRefreshInterval) {
        clearInterval(autoRefreshInterval);
    }

    autoRefreshInterval = CleanupManager.setInterval(async () => {
        try {
          
            const response = await fetch(isSentOffers ? '/json/sentoffers' : '/json/offers');
            if (response.ok) {
                
            }
        } catch (error) {
            console.error('[Auto-refresh] Error during background refresh:', error);
        }
    }, REFRESH_INTERVAL);
}

function stopAutoRefresh() {
    if (autoRefreshInterval) {
        clearInterval(autoRefreshInterval);
        autoRefreshInterval = null;
    }
}

async function initializeTableAndData() {
    loadSavedSettings();
    updateClearFiltersButton();
    initializeTableEvents();
    initializeTooltips();

    updateFilterButtonText('coin_to');
    updateFilterButtonText('coin_from');
    updateCoinBadges('coin_to');
    updateCoinBadges('coin_from');

    try {
        await fetchOffers();
        applyFilters();
    } catch (error) {
        console.error('Error loading initial data:', error);
        NetworkManager.handleNetworkError(error);
        ui.displayErrorMessage('Error loading data. Retrying in background...');
    }
}

function loadSavedSettings() {
    const storageKey = isSentOffers ? 'sentOffersTableSettings' : 'networkOffersTableSettings';
    const saved = localStorage.getItem(storageKey);

    if (saved) {
        const settings = JSON.parse(saved);

        ['coin_to', 'coin_from'].forEach(filterType => {
            const savedCoins = settings[filterType];
            if (savedCoins && Array.isArray(savedCoins)) {

                document.querySelectorAll(`.${filterType}-checkbox`).forEach(cb => cb.checked = false);

                savedCoins.forEach(coinValue => {
                    const checkbox = document.querySelector(`.${filterType}-checkbox[value="${coinValue}"]`);
                    if (checkbox) checkbox.checked = true;
                });

                updateFilterButtonText(filterType);
                updateCoinBadges(filterType);
            }
        });

        ['status', 'sent_from', 'auto_accept_type'].forEach(id => {
            const element = document.getElementById(id);
            if (element && settings[id]) element.value = settings[id];
        });

        if (settings.sortColumn !== undefined) {
            currentSortColumn = settings.sortColumn;
            currentSortDirection = settings.sortDirection;
            updateSortIndicators();
        }
    }
}

function updateSortIndicators() {
    document.querySelectorAll('.sort-icon').forEach(icon => {
        icon.classList.remove('text-blue-500');
        icon.textContent = '↓';
    });

    const sortIcon = document.getElementById(`sort-icon-${currentSortColumn}`);
    if (sortIcon) {
        sortIcon.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
        sortIcon.classList.add('text-blue-500');
    }
}

document.addEventListener('DOMContentLoaded', async function() {
    try {
        if (typeof window.PriceManager === 'undefined') {
            console.error('PriceManager module not loaded');
            ui.displayErrorMessage('Price data unavailable. Some features may not work correctly.');
        }

        if (initializeTableRateModule()) {
            tableRateModule.init();
        }

        document.addEventListener('memoryOptimized', (e) => {
            if (jsonData && jsonData.length > e.detail.maxDataSize) {
                console.log(`Trimming offers data from ${jsonData.length} to ${e.detail.maxDataSize} items`);
                jsonData = jsonData.slice(0, e.detail.maxDataSize);
            }

            if (originalJsonData && originalJsonData.length > e.detail.maxDataSize) {
                console.log(`Trimming original offers data from ${originalJsonData.length} to ${e.detail.maxDataSize} items`);
                originalJsonData = originalJsonData.slice(0, e.detail.maxDataSize);
            }
        });

        await initializeTableAndData();

        if (window.PriceManager) {
            window.PriceManager.addEventListener('priceUpdate', function(prices) {
                latestPrices = prices;
                updateProfitLossDisplays();
            });
        }

        if (window.WebSocketManager) {
            WebSocketManager.addMessageHandler('message', async (data) => {
                if (data.event === 'new_offer' || data.event === 'offer_revoked') {
                    try {
                        
                        const fetchWithRetry = async (url, maxRetries = 3) => {
                            for (let i = 0; i < maxRetries; i++) {
                                try {
                                    const response = await fetch(url);
                                    if (!response.ok) {
                                        throw new Error(`HTTP error! status: ${response.status}`);
                                    }
                                    return response;
                                } catch (error) {
                                    if (i === maxRetries - 1) throw error;
                                    
                                    await new Promise(resolve => CleanupManager.setTimeout(resolve, 100 * Math.pow(2, i)));
                                }
                            }
                        };

                        const offersResponse = await fetchWithRetry(isSentOffers ? '/json/sentoffers' : '/json/offers');
                        const newData = await offersResponse.json();
                        const processedNewData = Array.isArray(newData) ? newData : Object.values(newData);
                        const newFormattedData = formatInitialData(processedNewData);

                        const oldOfferIds = originalJsonData.map(o => o.offer_id).sort().join(',');
                        const newOfferIds = newFormattedData.map(o => o.offer_id).sort().join(',');
                        const dataChanged = oldOfferIds !== newOfferIds;

                        if (!dataChanged) {
                            return;
                        }

                        jsonData = newFormattedData;
                        originalJsonData = [...jsonData];

                        const previousPrices = latestPrices;
                        let priceData;
                        if (window.PriceManager) {
                            priceData = await window.PriceManager.getPrices(false); 
                        } else {
                            priceData = await fetchLatestPrices();
                        }

                        if (priceData) {
                            latestPrices = priceData;
                            CacheManager.set('prices_coingecko', priceData, 'prices');
                        } else if (previousPrices) {
                            latestPrices = previousPrices;
                        }

                        applyFilters({ incremental: true, skipSkeleton: true });
                        updateProfitLossDisplays();

                        document.querySelectorAll('.usd-value').forEach(usdValue => {
                            const coinName = usdValue.getAttribute('data-coin');
                            if (coinName) {
                                const priceKey = getPriceKey(coinName);
                                const price = latestPrices[priceKey]?.usd;
                                if (price !== undefined && price !== null) {
                                    const amount = parseFloat(usdValue.getAttribute('data-amount') || '0');
                                    if (!isNaN(amount) && amount > 0) {
                                        const calculatedUSD = amount * price;
                                        const formattedUSD = calculatedUSD < 0.01
                                            ? calculatedUSD.toFixed(8) + ' USD'
                                            : calculatedUSD.toFixed(2) + ' USD';

                                        if (usdValue.textContent !== formattedUSD) {
                                            usdValue.textContent = formattedUSD;
                                        }
                                    }
                                }
                            }
                        });

                        updatePaginationInfo();

                    } catch (error) {
                        console.error('Error during WebSocket-triggered refresh:', error);
                        NetworkManager.handleNetworkError(error);
                    }
                }
            });

            if (!WebSocketManager.isConnected()) {
                WebSocketManager.connect();
            }
        }

        document.querySelectorAll('th[data-sortable="true"]').forEach(header => {
            CleanupManager.addListener(header, 'click', () => {
                const columnIndex = parseInt(header.getAttribute('data-column-index'), 10);
                if (columnIndex !== null && !isNaN(columnIndex)) {
                    handleTableSort(columnIndex, header);
                }
            });
        });

        if (window.NetworkManager) {
            NetworkManager.addHandler('online', () => {
                updateConnectionStatus('connected');
                if (document.getElementById('error-overlay').classList.contains('hidden')) {
                    fetchOffers();
                }
            });

            NetworkManager.addHandler('offline', () => {
                updateConnectionStatus('disconnected');
            });
        }

        startAutoRefresh();

        const filterForm = document.getElementById('filterForm');
        if (filterForm) {
            filterForm.querySelectorAll('select').forEach(select => {
                CleanupManager.addListener(select, 'change', () => {
                    applyFilters();
                    updateClearFiltersButton();
                });
            });
        }

        const clearFiltersBtn = document.getElementById('clearFilters');
        if (clearFiltersBtn) {
            CleanupManager.addListener(clearFiltersBtn, 'click', clearFilters);
        }

        CleanupManager.addListener(document, 'click', (e) => {
            const coinToDropdown = document.getElementById('coin_to_dropdown');
            const coinFromDropdown = document.getElementById('coin_from_dropdown');

            if (coinToDropdown && coinToDropdown.style.display !== 'none') {
                if (!e.target.closest('#coin_to_button') && !e.target.closest('#coin_to_dropdown')) {
                    hideDropdown('coin_to');
                }
            }

            if (coinFromDropdown && coinFromDropdown.style.display !== 'none') {
                if (!e.target.closest('#coin_from_button') && !e.target.closest('#coin_from_dropdown')) {
                    hideDropdown('coin_from');
                }
            }
        });

        const rowTimeInterval = CleanupManager.setInterval(updateRowTimes, 30000);
        CleanupManager.registerResource('rowTimeInterval', rowTimeInterval, () => {
            clearInterval(rowTimeInterval);
        });

    } catch (error) {
        console.error('Error during initialization:', error);
        ui.displayErrorMessage('Error initializing application. Please refresh the page.');
    }
});

function cleanup() {
  console.log('Starting offers.js cleanup process');

  try {
    if (window.filterTimeout) {
      clearTimeout(window.filterTimeout);
      window.filterTimeout = null;
    }

    if (window.sortTimeout) {
      clearTimeout(window.sortTimeout);
      window.sortTimeout = null;
    }

    if (window.refreshInterval) {
      clearInterval(window.refreshInterval);
      window.refreshInterval = null;
    }

    if (window.countdownInterval) {
      clearInterval(window.countdownInterval);
      window.countdownInterval = null;
    }

    stopAutoRefresh();

    if (window._cleanupIntervals && Array.isArray(window._cleanupIntervals)) {
      window._cleanupIntervals.forEach(interval => {
        clearInterval(interval);
      });
      window._cleanupIntervals = [];
    }

    if (window.PriceManager) {
      if (typeof window.PriceManager.removeEventListener === 'function') {
        window.PriceManager.removeEventListener('priceUpdate');
      }
    }

    const offersBody = document.getElementById('offers-body');
    if (offersBody) {
      const existingRows = Array.from(offersBody.querySelectorAll('tr'));
      existingRows.forEach(row => {
        const tooltipTriggers = row.querySelectorAll('[data-tooltip-trigger-id]');
        tooltipTriggers.forEach(trigger => {
          if (window.TooltipManager) {
            window.TooltipManager.destroy(trigger);
          }
        });

        if (window.CleanupManager) {
          window.CleanupManager.removeListenersByElement(row);
        }
      });
      offersBody.innerHTML = '';
    }

    jsonData = null;
    originalJsonData = null;
    latestPrices = null;

    if (window.TooltipManager) {
      window.TooltipManager.cleanup();
    }

    if (window.MemoryManager) {
      if (window.MemoryManager.forceCleanup) {
        window.MemoryManager.forceCleanup();
      }
    }

  } catch (error) {
    console.error('Error during cleanup:', error);
  }
}
window.cleanup = cleanup;
