Scrap2Mark

Comprehensive inventory management and market posting tool for Torn.com

当前为 2025-07-24 提交的版本,查看 最新版本

// ==UserScript==
// @name         Scrap2Mark
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Comprehensive inventory management and market posting tool for Torn.com
// @author       vALT0r [767373]
// @match        https://www.torn.com/item.php*
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @grant        none
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const SCRIPT_NAME = 'Torn Inventory Scanner';
    const STORAGE_KEY = 'torn_inventory_data';
    const API_KEY_STORAGE_KEY = 'torn_api_key';
    const API_DATA_STORAGE_KEY = 'torn_api_items_data';
    const API_EXPIRY_STORAGE_KEY = 'torn_api_items_expiry';
    const API_KEY_EXPIRY_STORAGE_KEY = 'torn_api_key_expiry';
    const WINDOW_STATE_STORAGE_KEY = 'torn_scanner_window_state';
    const MARKET_DATA_STORAGE_KEY = 'torn_market_data';
    const MARKET_EXPIRY_STORAGE_KEY = 'torn_market_expiry';
    const IGNORED_ITEMS_STORAGE_KEY = 'scrap2mark_ignored_items';
    const FILTER_STATE_STORAGE_KEY = 'scrap2mark_filter_state';
    const API_CACHE_DURATION = 30 * 24 * 60 * 60 * 1000; // 30 days in milliseconds
    const API_KEY_EXPIRY_DURATION = 90 * 24 * 60 * 60 * 1000; // 90 days in milliseconds
    const MARKET_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
    const OUTLIER_THRESHOLD = 3.0; // Standard deviations for outlier detection

    // Window size configuration
    const WINDOW_MIN_WIDTH = '410px';
    const WINDOW_MIN_HEIGHT = '600px';
    const WINDOW_DEFAULT_WIDTH = '510px';
    const WINDOW_DEFAULT_HEIGHT = '600px';
    const WINDOW_MINIMIZED_WIDTH = '180px';
    const WINDOW_MINIMIZED_HEIGHT = '50px';

    // State variables
    let isScanning = false;
    let scannedItems = new Map();
    let ignoredItems = new Set(); // Track ignored items
    let floatingTable = null;
    let apiItemsData = null;
    let apiKey = null;
    let marketData = new Map();
    let apiRequestQueue = [];
    let isProcessingQueue = false;
    let lastApiRequest = 0;
    let apiRequestCount = 0;
    let apiRequestResetTime = 0;

    // Utility functions
    function log(message) {
        console.log(`[${SCRIPT_NAME}] ${message}`);
    }

    // Load ignored items from localStorage
    function loadIgnoredItems() {
        try {
            const stored = localStorage.getItem(IGNORED_ITEMS_STORAGE_KEY);
            if (stored) {
                const ignoredArray = JSON.parse(stored);
                ignoredItems = new Set(ignoredArray);
            } else {
                ignoredItems = new Set();
            }
        } catch (e) {
            log('Error loading ignored items: ' + e.message);
            ignoredItems = new Set();
        }
    }

    // Save ignored items to localStorage
    function saveIgnoredItems() {
        try {
            const ignoredArray = Array.from(ignoredItems);
            localStorage.setItem(IGNORED_ITEMS_STORAGE_KEY, JSON.stringify(ignoredArray));
        } catch (e) {
            log('Error saving ignored items: ' + e.message);
        }
    }

    // Toggle ignore status for an item
    function toggleIgnoreItem(itemId) {
        // Convert to string to ensure consistency
        const itemIdStr = itemId.toString();

        if (ignoredItems.has(itemIdStr)) {
            ignoredItems.delete(itemIdStr);
            log(`Removed item ${itemIdStr} from ignored list`);
        } else {
            ignoredItems.add(itemIdStr);
            log(`Added item ${itemIdStr} to ignored list`);
        }
        saveIgnoredItems();
        updateTableDisplay();

        // Show visual feedback with toast notification
        const statusDiv = document.getElementById('scan-status');
        const isIgnored = ignoredItems.has(itemIdStr);
        const itemName = Array.from(scannedItems.values()).find(item => item.id.toString() === itemIdStr)?.name || 'Unknown';
        const message = isIgnored ? `"${itemName}" ignored` : `"${itemName}" un-ignored`;

        // Create a temporary toast notification
        const toast = document.createElement('div');
        toast.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${isIgnored ? '#dc3545' : '#28a745'};
            color: white;
            padding: 10px 15px;
            border-radius: 5px;
            z-index: 100002;
            font-size: 14px;
            font-weight: bold;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            animation: slideIn 0.3s ease-out;
        `;
        toast.textContent = message;

        // Add animation keyframes if not already added
        if (!document.querySelector('#toast-animations')) {
            const animationStyle = document.createElement('style');
            animationStyle.id = 'toast-animations';
            animationStyle.textContent = `
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
                @keyframes slideOut {
                    from { transform: translateX(0); opacity: 1; }
                    to { transform: translateX(100%); opacity: 0; }
                }
            `;
            document.head.appendChild(animationStyle);
        }

        document.body.appendChild(toast);

        // Remove toast after 3 seconds
        setTimeout(() => {
            toast.style.animation = 'slideOut 0.3s ease-out';
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 300);
        }, 3000);

        // Also update status briefly if available
        if (statusDiv) {
            const originalContent = statusDiv.innerHTML;
            statusDiv.innerHTML = `<span style="color: ${isIgnored ? '#dc3545' : '#28a745'};">${message}</span>`;
            setTimeout(() => {
                statusDiv.innerHTML = originalContent;
            }, 2000);
        }
    }

    // Check if an item is ignored
    function isItemIgnored(itemId) {
        return ignoredItems.has(itemId.toString());
    }

    // Filter state management
    function loadFilterState() {
        try {
            const saved = localStorage.getItem(FILTER_STATE_STORAGE_KEY);
            if (saved) {
                return JSON.parse(saved);
            }
        } catch (e) {
            log('Error loading filter state: ' + e.message);
        }

        // Default state - only hideLowValue is true
        return {
            showNonTradeable: false,
            showIgnored: false,
            hideLowValue: true,
            minValue: 5000000
        };
    }

    function saveFilterState(state) {
        try {
            localStorage.setItem(FILTER_STATE_STORAGE_KEY, JSON.stringify(state));
        } catch (e) {
            log('Error saving filter state: ' + e.message);
        }
    }

    function getCurrentFilterState() {
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');

        return {
            showNonTradeable: showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false,
            showIgnored: showIgnoredCheckbox ? showIgnoredCheckbox.checked : false,
            hideLowValue: hideLowValueCheckbox ? hideLowValueCheckbox.checked : true,
            minValue: minValueInput ? parseFloat(minValueInput.value.replace(/[,$]/g, '')) || 5000000 : 5000000
        };
    }

    function applyFilterState(state) {
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');

        if (showNonTradeableCheckbox) showNonTradeableCheckbox.checked = state.showNonTradeable;
        if (showIgnoredCheckbox) showIgnoredCheckbox.checked = state.showIgnored;
        if (hideLowValueCheckbox) hideLowValueCheckbox.checked = state.hideLowValue;
        if (minValueInput) minValueInput.value = state.minValue.toLocaleString();
    }

    function waitForElement(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const element = document.querySelector(selector);
            if (element) {
                resolve(element);
                return;
            }

            const observer = new MutationObserver(() => {
                const element = document.querySelector(selector);
                if (element) {
                    observer.disconnect();
                    resolve(element);
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });

            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Element ${selector} not found within ${timeout}ms`));
            }, timeout);
        });
    }

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    // Window state management
    function loadWindowState() {
        try {
            const stateData = localStorage.getItem(WINDOW_STATE_STORAGE_KEY);
            if (stateData) {
                return JSON.parse(stateData);
            }
        } catch (error) {
            log(`Failed to load window state: ${error.message}`);
        }
        return {
            x: 20,
            y: 20,
            width: WINDOW_DEFAULT_WIDTH,
            height: WINDOW_DEFAULT_HEIGHT,
            minimized: false
        };
    }

    function saveWindowState(x, y, width, height, minimized) {
        try {
            const state = { x, y, width, height, minimized };
            localStorage.setItem(WINDOW_STATE_STORAGE_KEY, JSON.stringify(state));
        } catch (error) {
            log(`Failed to save window state: ${error.message}`);
        }
    }

    // API Key management
    function loadApiKey() {
        try {
            const keyData = localStorage.getItem(API_KEY_STORAGE_KEY);
            const keyExpiry = localStorage.getItem(API_KEY_EXPIRY_STORAGE_KEY);

            if (keyData && keyExpiry) {
                const expiryDate = new Date(parseInt(keyExpiry));
                if (new Date() < expiryDate) {
                    apiKey = keyData;
                    log('API key loaded from storage');
                    return true;
                }
            }
        } catch (error) {
            log(`Failed to load API key: ${error.message}`);
        }
        return false;
    }

    function saveApiKey(key) {
        try {
            const expiryDate = new Date(Date.now() + API_KEY_EXPIRY_DURATION);
            localStorage.setItem(API_KEY_STORAGE_KEY, key);
            localStorage.setItem(API_KEY_EXPIRY_STORAGE_KEY, expiryDate.getTime().toString());
            apiKey = key;
            log('API key saved to storage');
        } catch (error) {
            log(`Failed to save API key: ${error.message}`);
        }
    }

    // API Items data management
    function loadApiItemsData() {
        try {
            const itemsData = localStorage.getItem(API_DATA_STORAGE_KEY);
            const itemsExpiry = localStorage.getItem(API_EXPIRY_STORAGE_KEY);

            if (itemsData && itemsExpiry) {
                const expiryDate = new Date(parseInt(itemsExpiry));
                if (new Date() < expiryDate) {
                    apiItemsData = JSON.parse(itemsData);
                    log(`Loaded ${Object.keys(apiItemsData).length} items from API cache`);
                    return true;
                }
            }
        } catch (error) {
            log(`Failed to load API items data: ${error.message}`);
        }
        return false;
    }

    function saveApiItemsData(data) {
        try {
            const expiryDate = new Date(Date.now() + API_CACHE_DURATION);
            localStorage.setItem(API_DATA_STORAGE_KEY, JSON.stringify(data));
            localStorage.setItem(API_EXPIRY_STORAGE_KEY, expiryDate.getTime().toString());
            apiItemsData = data;
            log(`Saved ${Object.keys(data).length} items to API cache`);
        } catch (error) {
            log(`Failed to save API items data: ${error.message}`);
        }
    }

    // Fetch items from API
    async function fetchItemsFromAPI(key) {
        try {
            const response = await fetch(`https://api.torn.com/v2/torn/items?cat=All&sort=ASC&key=${key}`);
            if (!response.ok) {
                throw new Error(`API request failed: ${response.status} ${response.statusText}`);
            }

            const data = await response.json();
            if (data.error) {
                throw new Error(`API error: ${data.error.error}`);
            }

            // Convert array to object with ID as key for easier lookup
            const itemsObject = {};
            if (data.items && Array.isArray(data.items)) {
                data.items.forEach(item => {
                    itemsObject[item.id] = item;
                });
            }

            saveApiItemsData(itemsObject);
            return itemsObject;
        } catch (error) {
            throw new Error(`Failed to fetch items from API: ${error.message}`);
        }
    }

    // Check if item is tradeable
    function isItemTradeable(itemId) {
        if (!apiItemsData || !itemId) return null; // null means unknown

        const item = apiItemsData[itemId];
        if (!item) return null;

        return item.is_tradable === true;
    }

    // Market data management
    function loadMarketData() {
        try {
            const marketDataStr = localStorage.getItem(MARKET_DATA_STORAGE_KEY);
            const marketExpiry = localStorage.getItem(MARKET_EXPIRY_STORAGE_KEY);

            if (marketDataStr && marketExpiry) {
                const expiryDate = new Date(parseInt(marketExpiry));
                if (new Date() < expiryDate) {
                    const savedData = JSON.parse(marketDataStr);
                    marketData = new Map(savedData);
                    log(`Loaded market data for ${marketData.size} items from cache`);
                    return true;
                }
            }
        } catch (error) {
            log(`Failed to load market data: ${error.message}`);
        }
        return false;
    }

    function saveMarketData() {
        try {
            const expiryDate = new Date(Date.now() + MARKET_CACHE_DURATION);
            const dataArray = Array.from(marketData.entries());
            localStorage.setItem(MARKET_DATA_STORAGE_KEY, JSON.stringify(dataArray));
            localStorage.setItem(MARKET_EXPIRY_STORAGE_KEY, expiryDate.getTime().toString());
            log(`Saved market data for ${marketData.size} items to cache`);
        } catch (error) {
            log(`Failed to save market data: ${error.message}`);
        }
    }

    // Calculate estimated value based on lowest market price after outlier removal
    function calculateEstimatedValue(itemId, quantity) {
        const marketInfo = marketData.get(itemId);
        if (!marketInfo || !marketInfo.listings || marketInfo.listings.length === 0) {
            return 0;
        }

        // Calculate price stats to get consistent pricing
        const stats = calculatePriceStats(marketInfo.listings);
        if (!stats) {
            return 0;
        }

        // Use the same low-end price calculation as the Price column
        const lowPrice = Math.min(stats.p25, stats.min * 1.2);

        // Calculate estimated value: low-end price * quantity
        return Math.round(lowPrice * quantity);
    }

    // Global sorting state
    let currentSortColumn = -1;
    let currentSortDirection = 'asc';
    let currentSortField = null; // Track which field we're sorting by

    // Apply current sort to items array
    function applySortToItems(items) {
        if (currentSortColumn === -1 || !currentSortField) {
            // No active sort, use default sorting
            return items.sort((a, b) => {
                const aIgnored = isItemIgnored(a.id);
                const bIgnored = isItemIgnored(b.id);

                // If showing ignored items, put ignored items at the top for easy un-ignoring
                const showIgnoredCheckbox = document.getElementById('show-ignored');
                const showIgnored = showIgnoredCheckbox ? showIgnoredCheckbox.checked : false;

                if (showIgnored) {
                    if (aIgnored && !bIgnored) return -1;
                    if (bIgnored && !aIgnored) return 1;
                } else {
                    // If not showing ignored items, put ignored items last (they'll be filtered out anyway)
                    if (aIgnored && !bIgnored) return 1;
                    if (bIgnored && !aIgnored) return -1;
                }

                // If showing non-tradeable items, put them after ignored items
                const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
                const showNonTradeable = showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false;

                if (showNonTradeable && !aIgnored && !bIgnored) {
                    if (a.tradeable === false && b.tradeable !== false) return -1;
                    if (b.tradeable === false && a.tradeable !== false) return 1;
                }

                const categoryA = a.category || '';
                const categoryB = b.category || '';
                const nameA = a.name || '';
                const nameB = b.name || '';

                if (categoryA !== categoryB) {
                    return categoryA.localeCompare(categoryB);
                }
                return nameA.localeCompare(nameB);
            });
        }

        // Apply active sort
        return items.sort((a, b) => {
            let aValue, bValue;

            switch (currentSortField) {
                case 'name':
                    aValue = a.name || '';
                    bValue = b.name || '';
                    break;
                case 'category':
                    aValue = a.category || '';
                    bValue = b.category || '';
                    break;
                case 'qty':
                    aValue = a.quantity || 0;
                    bValue = b.quantity || 0;
                    break;
                case 'trade':
                    aValue = a.tradeable === true ? 1 : 0;
                    bValue = b.tradeable === true ? 1 : 0;
                    break;
                case 'price':
                    // Get market price for comparison
                    const aMarketInfo = marketData.get(a.id.toString());
                    const bMarketInfo = marketData.get(b.id.toString());

                    if (aMarketInfo && a.tradeable === true) {
                        const aStats = calculatePriceStats(aMarketInfo.listings);
                        aValue = aStats ? Math.min(aStats.p25, aStats.min * 1.2) : 0;
                    } else {
                        aValue = 0;
                    }

                    if (bMarketInfo && b.tradeable === true) {
                        const bStats = calculatePriceStats(bMarketInfo.listings);
                        bValue = bStats ? Math.min(bStats.p25, bStats.min * 1.2) : 0;
                    } else {
                        bValue = 0;
                    }
                    break;
                case 'estimated':
                    aValue = calculateEstimatedValue(a.id, a.quantity);
                    bValue = calculateEstimatedValue(b.id, b.quantity);
                    break;
                default:
                    return 0;
            }

            // Apply sort direction
            if (currentSortDirection === 'asc') {
                if (typeof aValue === 'string') {
                    return aValue.localeCompare(bValue);
                }
                return aValue > bValue ? 1 : aValue < bValue ? -1 : 0;
            } else {
                if (typeof aValue === 'string') {
                    return bValue.localeCompare(aValue);
                }
                return aValue < bValue ? 1 : aValue > bValue ? -1 : 0;
            }
        });
    }

    // Dynamic sort function that adjusts for Trade column visibility
    function sortTableDynamic(columnName) {
        const tradeHeader = document.getElementById('trade-column-header');
        if (!tradeHeader) {
            return;
        }

        const tradeColumnVisible = tradeHeader.style.display !== 'none';

        let columnIndex;

        // Map column names to indices, adjusting for Trade column visibility
        switch (columnName) {
            case 'name': columnIndex = 0; break;
            case 'category': columnIndex = 1; break;
            case 'qty': columnIndex = 2; break;
            case 'trade': columnIndex = tradeColumnVisible ? 3 : -1; break;
            case 'price': columnIndex = tradeColumnVisible ? 4 : 3; break;
            case 'estimated': columnIndex = tradeColumnVisible ? 5 : 4; break;
            default: return;
        }

        if (columnIndex === -1) return; // Trade column not visible

        // Update sort direction
        if (currentSortColumn === columnIndex && currentSortField === columnName) {
            currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            currentSortDirection = 'asc';
            currentSortColumn = columnIndex;
            currentSortField = columnName;
        }

        // Clear all sort indicators
        ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
            const indicator = document.getElementById(`sort-${col}`);
            if (indicator) indicator.textContent = '↕';
        });

        // Set current sort indicator
        const currentIndicator = document.getElementById(`sort-${columnName}`);
        if (currentIndicator) {
            currentIndicator.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
        }

        // Re-render table with new sort
        updateTableDisplay();
    }

    // Make sorting functions global immediately after definition
    window.sortTableDynamic = sortTableDynamic;

    // Sort table function
    function sortTable(columnIndex) {
        const table = document.getElementById('all-items-table');
        if (!table) {
            return;
        }

        const tbody = table.querySelector('tbody');
        if (!tbody) {
            return;
        }

        const rows = Array.from(tbody.querySelectorAll('tr'));

        if (rows.length === 0) {
            return;
        }

        const tradeColumnVisible = document.getElementById('trade-column-header').style.display !== 'none';

        // Update sort direction
        if (currentSortColumn === columnIndex) {
            currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            currentSortDirection = 'asc';
            currentSortColumn = columnIndex;
        }

        // Clear all sort indicators
        ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
            const indicator = document.getElementById(`sort-${col}`);
            if (indicator) indicator.textContent = '↕';
        });

        // Set current sort indicator
        const columnNames = ['name', 'category', 'qty', 'trade', 'price', 'estimated'];

        if (columnNames[columnIndex]) {
            const currentIndicator = document.getElementById(`sort-${columnNames[columnIndex]}`);
            if (currentIndicator) {
                currentIndicator.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
            }
        }

        // Sort rows
        rows.sort((a, b) => {
            let aValue = a.cells[columnIndex].textContent.trim();
            let bValue = b.cells[columnIndex].textContent.trim();

            // Handle different data types based on fixed column positions
            let qtyColumnIndex = 2;
            let tradeColumnIndex = 3;
            let priceColumnIndex = 4;
            let estimatedColumnIndex = 5;

            // Handle different data types
            if (columnIndex === qtyColumnIndex || columnIndex === priceColumnIndex || columnIndex === estimatedColumnIndex) {
                // Remove currency symbols and commas, convert to numbers
                aValue = parseFloat(aValue.replace(/[$,]/g, '')) || 0;
                bValue = parseFloat(bValue.replace(/[$,]/g, '')) || 0;
            } else if (columnIndex === tradeColumnIndex) {
                // Convert ✓/✗ to sortable values
                aValue = aValue === '✓' ? 1 : 0;
                bValue = bValue === '✓' ? 1 : 0;
            }

            if (currentSortDirection === 'asc') {
                return aValue > bValue ? 1 : aValue < bValue ? -1 : 0;
            } else {
                return aValue < bValue ? 1 : aValue > bValue ? -1 : 0;
            }
        });

        // Re-append sorted rows
        rows.forEach(row => tbody.appendChild(row));
    }

    // Make sorting functions global
    window.sortTable = sortTable;
    window.sortTableDynamic = sortTableDynamic;
    // Make functions globally accessible for debugging
    window.toggleIgnoreItem = toggleIgnoreItem;
    window.resetApiState = resetApiState;

    // Check if market data is fresh (less than 1 hour old)
    function isMarketDataFresh(marketInfo) {
        if (!marketInfo || !marketInfo.timestamp) return false;
        const oneHour = 60 * 60 * 1000; // 1 hour in milliseconds
        return (Date.now() - marketInfo.timestamp) < oneHour;
    }

    // Remove price outliers using statistical methods (only high outliers and extreme prices)
    function filterOutliers(listings) {
        if (!listings || listings.length < 3) return listings; // Need at least 3 listings for meaningful filtering

        // Get all prices and sort them
        const prices = listings.map(l => l.price).sort((a, b) => a - b);
        const minPrice = prices[0];
        const maxPrice = prices[prices.length - 1];

        // Rule 1: Remove prices more than 10x the minimum price
        const priceCapFilter = listings.filter(listing => listing.price <= minPrice * 10);

        // Rule 2: Statistical outlier detection for high outliers only
        if (priceCapFilter.length < 3) return priceCapFilter; // Not enough data for statistical analysis

        const uniquePrices = [...new Set(priceCapFilter.map(l => l.price))];
        if (uniquePrices.length < 3) return priceCapFilter; // Need at least 3 unique prices

        const mean = uniquePrices.reduce((a, b) => a + b, 0) / uniquePrices.length;
        const variance = uniquePrices.reduce((sum, price) => sum + Math.pow(price - mean, 2), 0) / uniquePrices.length;
        const stdDev = Math.sqrt(variance);

        if (stdDev === 0) return priceCapFilter; // All prices are the same

        // Only filter high outliers (prices significantly above the mean)
        const finalFiltered = priceCapFilter.filter(listing => {
            const zScore = (listing.price - mean) / stdDev;
            return zScore <= OUTLIER_THRESHOLD; // Only remove high outliers
        });

        // More conservative approach: only apply statistical filtering if we don't remove too many sellers
        const uniqueOriginalPrices = [...new Set(priceCapFilter.map(l => l.price))];
        const uniqueFinalPrices = [...new Set(finalFiltered.map(l => l.price))];
        const sellerRetainRatio = uniqueFinalPrices.length / uniqueOriginalPrices.length;

        // Keep statistical filtering only if we retain at least 70% of sellers
        if (sellerRetainRatio >= 0.7) {
            return finalFiltered;
        } else {
            return priceCapFilter; // Fall back to just the 10x price cap filter
        }
    }

    // API rate limiting and error handling
    function shouldThrottleApiRequest() {
        const now = Date.now();

        // Reset counter every minute
        if (now - apiRequestResetTime > 60000) {
            apiRequestCount = 0;
            apiRequestResetTime = now;
        }

        // Check if we've exceeded rate limit
        if (apiRequestCount >= 95) { // Leave some buffer below 100
            return true;
        }

        // Check minimum time between requests (600ms = ~100 requests per minute)
        if (now - lastApiRequest < 600) {
            return true;
        }

        return false;
    }

    async function fetchMarketData(itemId) {
        if (!apiKey) {
            throw new Error('API key not available');
        }

        try {
            // Add timeout for throttling wait to prevent infinite hangs
            let throttleAttempts = 0;
            const maxThrottleAttempts = 60; // Max 60 seconds of waiting

            // Wait for throttling if needed
            while (shouldThrottleApiRequest() && throttleAttempts < maxThrottleAttempts) {
                await sleep(1000);
                throttleAttempts++;
            }

            if (throttleAttempts >= maxThrottleAttempts) {
                throw new Error('Throttling timeout - API requests are being rate limited too heavily');
            }

            lastApiRequest = Date.now();
            apiRequestCount++;

            // Add timeout to the fetch request
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout

            try {
                const response = await fetch(`https://api.torn.com/v2/market/${itemId}/itemmarket?offset=0&key=${apiKey}`, {
                    signal: controller.signal
                });

                clearTimeout(timeoutId);

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

                const data = await response.json();

                if (data.error) {
                    const errorCode = data.error.code;
                    const errorMessage = data.error.error;

                    // Handle specific API errors
                    if (errorCode === 5) { // Too many requests
                        throw new Error('Rate limit exceeded. Please wait before making more requests.');
                    } else if (errorCode === 8) { // IP block
                        throw new Error('IP temporarily blocked due to abuse. Please wait.');
                    } else if (errorCode === 14) { // Daily limit
                        throw new Error('Daily API read limit reached.');
                    } else if (errorCode === 17) { // Backend error
                        throw new Error('Torn backend error. Please try again later.');
                    }

                    throw new Error(`API Error ${errorCode}: ${errorMessage}`);
                }

                if (data.itemmarket && data.itemmarket.listings) {
                    const marketInfo = {
                        item: data.itemmarket.item,
                        listings: data.itemmarket.listings,
                        timestamp: Date.now()
                    };

                    marketData.set(itemId.toString(), marketInfo);
                    saveMarketData();
                    return marketInfo;
                }

                return null;

            } catch (fetchError) {
                clearTimeout(timeoutId);
                if (fetchError.name === 'AbortError') {
                    throw new Error('Request timeout - API request took too long');
                }
                throw fetchError;
            }

        } catch (error) {
            // If it's a rate limiting error, we should back off
            if (error.message.includes('Rate limit') || error.message.includes('Too many requests')) {
                // Exponential backoff
                await sleep(Math.min(30000, 1000 * Math.pow(2, apiRequestCount % 5)));
            }
            throw error;
        }
    }

    // Process API request queue
    async function processApiQueue() {
        if (isProcessingQueue || apiRequestQueue.length === 0) {
            return;
        }

        isProcessingQueue = true;

        while (apiRequestQueue.length > 0) {
            const { itemId, resolve, reject } = apiRequestQueue.shift();

            try {
                const result = await fetchMarketData(itemId);
                resolve(result);
            } catch (error) {
                reject(error);
            }

            // Small delay between requests
            await sleep(100);
        }

        isProcessingQueue = false;
    }

    // Queue API request
    function queueMarketDataRequest(itemId) {
        return new Promise((resolve, reject) => {
            // Check if we already have cached data that's fresh
            const cached = marketData.get(itemId.toString());
            if (cached && isMarketDataFresh(cached)) {
                resolve(cached);
                return;
            }

            // Add to queue
            apiRequestQueue.push({ itemId, resolve, reject });

            // Start processing if not already running
            processApiQueue();
        });
    }

    // Calculate price statistics
    function calculatePriceStats(listings, filterOutliersEnabled = true) {
        if (!listings || listings.length === 0) {
            return null;
        }

        // Filter outliers if enabled
        const processedListings = filterOutliersEnabled ? filterOutliers(listings) : listings;

        if (processedListings.length === 0) {
            return null;
        }

        const prices = processedListings.map(l => l.price).sort((a, b) => a - b);
        const quantities = processedListings.reduce((sum, l) => sum + l.amount, 0);

        // Calculate weighted average
        const totalValue = processedListings.reduce((sum, l) => sum + (l.price * l.amount), 0);
        const weightedAverage = totalValue / quantities;

        // Calculate percentiles
        const p25Index = Math.floor(prices.length * 0.25);
        const p75Index = Math.floor(prices.length * 0.75);

        // Calculate seller-based statistics
        const uniquePrices = [...new Set(processedListings.map(l => l.price))];
        const originalUniquePrices = [...new Set(listings.map(l => l.price))];

        // Calculate filtering breakdown
        const priceCapFiltered = listings.filter(l => l.price > Math.min(...listings.map(p => p.price)) * 10);
        const statFiltered = listings.length - processedListings.length - priceCapFiltered.length;

        return {
            min: Math.min(...prices),
            max: Math.max(...prices),
            median: prices[Math.floor(prices.length / 2)],
            average: weightedAverage,
            p25: prices[p25Index],
            p75: prices[p75Index],
            listings: processedListings.length,
            totalListings: listings.length,
            uniqueSellers: uniquePrices.length,
            totalUniqueSellers: originalUniquePrices.length,
            totalQuantity: quantities,
            outliersFiltered: listings.length - processedListings.length,
            sellersFiltered: originalUniquePrices.length - uniquePrices.length,
            priceCapFiltered: priceCapFiltered.length,
            statFiltered: Math.max(0, statFiltered)
        };
    }

    // Create price distribution mini chart
    function createPriceChart(listings, width = 60, height = 20) {
        if (!listings || listings.length === 0) {
            return `<div style="width: ${width}px; height: ${height}px; background: #f0f0f0; display: flex; align-items: center; justify-content: center; font-size: 8px; color: #999;">No data</div>`;
        }

        const filteredListings = filterOutliers(listings);
        const stats = calculatePriceStats(filteredListings, false);
        if (!stats) return '';

        // Group prices into bins for visualization
        const bins = Math.min(10, filteredListings.length);
        const binWidth = (stats.max - stats.min) / bins;
        const binCounts = new Array(bins).fill(0);

        filteredListings.forEach(listing => {
            const binIndex = Math.min(Math.floor((listing.price - stats.min) / binWidth), bins - 1);
            binCounts[binIndex] += listing.amount;
        });

        const maxBinCount = Math.max(...binCounts);

        // Create SVG chart
        const bars = binCounts.map((count, index) => {
            const barHeight = maxBinCount > 0 ? (count / maxBinCount) * (height - 2) : 0;
            const x = (index * width) / bins;
            const barWidth = width / bins - 1;
            const y = height - barHeight;

            return `<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" fill="#007bff" opacity="0.7"/>`;
        }).join('');

        return `
            <svg width="${width}" height="${height}" style="border: 1px solid #ddd;">
                ${bars}
            </svg>
        `;
    }

    // Create high-resolution popup chart with zoom functionality
    function createDetailedPriceChart(listings, itemName, zoomMin = null, zoomMax = null) {
        if (!listings || listings.length === 0) {
            return '<div style="padding: 20px;">No market data available</div>';
        }

        const filteredListings = filterOutliers(listings);
        const stats = calculatePriceStats(filteredListings, false);
        if (!stats) return '<div style="padding: 20px;">No valid price data</div>';

        const width = 300;
        const height = 200;
        const padding = 40;
        const chartWidth = width - padding * 2;
        const chartHeight = height - padding * 2;

        // Determine price range for chart (zoom or full range)
        const displayMin = zoomMin || stats.min;
        const displayMax = zoomMax || stats.max;
        const displayRange = displayMax - displayMin;
        const isZoomed = zoomMin !== null || zoomMax !== null;

        // Filter listings to zoom range if zooming
        const displayListings = isZoomed ?
            filteredListings.filter(l => l.price >= displayMin && l.price <= displayMax) :
            filteredListings;

        if (displayListings.length === 0) {
            return '<div style="padding: 20px;">No data in selected range</div>';
        }

        // Create bins for histogram
        const bins = Math.min(20, displayListings.length);
        const binWidth = displayRange / bins;
        const binData = [];

        for (let i = 0; i < bins; i++) {
            binData.push({
                min: displayMin + i * binWidth,
                max: displayMin + (i + 1) * binWidth,
                count: 0,
                totalAmount: 0
            });
        }

        displayListings.forEach(listing => {
            const binIndex = Math.min(Math.floor((listing.price - displayMin) / binWidth), bins - 1);
            if (binIndex >= 0) {
                binData[binIndex].count++;
                binData[binIndex].totalAmount += listing.amount;
            }
        });

        const maxCount = Math.max(...binData.map(b => b.totalAmount));

        // Create SVG bars with selection capability
        const bars = binData.map((bin, index) => {
            const barHeight = maxCount > 0 ? (bin.totalAmount / maxCount) * chartHeight : 0;
            const x = padding + (index * chartWidth) / bins;
            const barWidth = chartWidth / bins - 2;
            const y = padding + chartHeight - barHeight;

            const midPrice = (bin.min + bin.max) / 2;

            return `
                <rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}"
                      fill="#007bff" opacity="0.7" stroke="#0056b3" stroke-width="1"
                      data-price-min="${bin.min}" data-price-max="${bin.max}" data-amount="${bin.totalAmount}"
                      class="chart-bar">
                </rect>
            `;
        }).join('');

        // Create axes
        const xAxis = `<line x1="${padding}" y1="${padding + chartHeight}" x2="${padding + chartWidth}" y2="${padding + chartHeight}" stroke="#666" stroke-width="1"/>`;
        const yAxis = `<line x1="${padding}" y1="${padding}" x2="${padding}" y2="${padding + chartHeight}" stroke="#666" stroke-width="1"/>`;

        // Create price labels
        const priceLabels = [displayMin, (displayMin + displayMax) / 2, displayMax].map((price, index) => {
            const x = padding + (index * chartWidth) / 2;
            return `<text x="${x}" y="${padding + chartHeight + 15}" text-anchor="middle" font-size="10" fill="#666">$${Math.round(price).toLocaleString()}</text>`;
        }).join('');

        // Create quantity labels
        const quantityLabels = [0, maxCount / 2, maxCount].map((qty, index) => {
            const y = padding + chartHeight - (index * chartHeight) / 2;
            return `<text x="${padding - 5}" y="${y + 3}" text-anchor="end" font-size="10" fill="#666">${Math.round(qty)}</text>`;
        }).join('');

        const outliersText = stats.outliersFiltered > 0 ? ` (${stats.outliersFiltered} high outliers filtered)` : '';
        const sellersText = stats.sellersFiltered > 0 ? ` | ${stats.sellersFiltered} sellers filtered` : '';
        const filterBreakdown = stats.priceCapFiltered > 0 || stats.statFiltered > 0 ?
            ` (${stats.priceCapFiltered} 10x+ prices, ${stats.statFiltered} statistical outliers)` : '';
        const zoomText = (zoomMin !== null || zoomMax !== null) ?
            ` | Zoomed: $${Math.round(displayMin).toLocaleString()}-$${Math.round(displayMax).toLocaleString()}` : '';

        // Selection overlay rectangle (initially hidden)
        const selectionOverlay = `
            <rect id="selection-rect" x="0" y="0" width="0" height="0"
                  fill="rgba(0,123,255,0.2)" stroke="rgba(0,123,255,0.8)"
                  stroke-width="1" style="display: none; pointer-events: none;"/>
        `;

        return `
            <div style="padding: 15px; background: white; border: 1px solid #ddd; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); position: relative; display: flex;">
                <div style="flex: 1;">
                    <h4 style="margin: 0 0 10px 0; font-size: 14px; color: #333;">${itemName} - Market Analysis${zoomText}</h4>
                    <svg id="price-chart-svg" width="${width}" height="${height + 20}" style="background: white; cursor: crosshair;"
                         data-padding="${padding}" data-chart-width="${chartWidth}" data-chart-height="${chartHeight}"
                         data-display-min="${displayMin}" data-display-max="${displayMax}">
                        ${bars}
                        ${xAxis}
                        ${yAxis}
                        ${priceLabels}
                        ${quantityLabels}
                        ${selectionOverlay}
                        <text x="${width / 2}" y="15" text-anchor="middle" font-size="12" font-weight="bold" fill="#333">Price Distribution</text>
                        <text x="15" y="${height / 2}" text-anchor="middle" font-size="10" fill="#666" transform="rotate(-90, 15, ${height / 2})">Quantity</text>
                        <text x="${width / 2}" y="${height + 15}" text-anchor="middle" font-size="10" fill="#666">Price ($)</text>
                    </svg>
                    <div style="font-size: 11px; margin-top: 10px; color: #666;">
                        <div><strong>Statistics:</strong></div>
                        <div>Average: $${Math.round(stats.average).toLocaleString()} | Median: $${stats.median.toLocaleString()}</div>
                        <div>Range: $${stats.min.toLocaleString()} - $${stats.max.toLocaleString()}</div>
                        <div>Q1: $${stats.p25.toLocaleString()} | Q3: $${stats.p75.toLocaleString()}</div>
                        <div>Listings: ${stats.listings}${outliersText} | Total Quantity: ${stats.totalQuantity.toLocaleString()}</div>
                        <div>Sellers: ${stats.uniqueSellers}/${stats.totalUniqueSellers}${sellersText}${filterBreakdown}</div>
                        ${(zoomMin !== null || zoomMax !== null) ? '<div style="margin-top: 5px;"><button id="reset-zoom" style="background: #6c757d; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 10px;">Reset Zoom</button></div>' : ''}
                    </div>
                </div>
                <div style="position: relative;">
                    <button id="expand-listings" style="position: absolute; top: 50%; right: -12px; transform: translateY(-50%); width: 24px; height: 48px; background: #007bff; color: white; border: none; border-radius: 0 8px 8px 0; cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; z-index: 1;">›</button>
                    <div id="listings-panel" style="display: none; position: absolute; left: 100%; top: 0; width: 280px; background: white; border: 1px solid #ccc; border-left: none; border-radius: 0 4px 4px 0; max-height: 400px; overflow-y: auto; z-index: 1000001;">
                        <div style="padding: 10px; border-bottom: 1px solid #eee; background: #f8f9fa; font-weight: bold; font-size: 12px;">
                            Individual Listings ${isZoomed ? '(Filtered)' : ''}
                            <button id="collapse-listings" style="float: right; background: none; border: none; cursor: pointer; font-size: 16px; color: #666;">×</button>
                        </div>
                        <div id="listings-content" style="padding: 5px;">
                            ${createListingsTable(displayListings)}
                        </div>
                    </div>
                </div>
            </div>
        `;
    }

    // Create listings table for the expanded panel
    function createListingsTable(listings) {
        if (!listings || listings.length === 0) {
            return '<div style="padding: 10px; text-align: center; color: #666;">No listings available</div>';
        }

        // Sort listings by price (lowest first)
        const sortedListings = [...listings].sort((a, b) => a.price - b.price);

        // Group identical prices
        const priceGroups = new Map();
        sortedListings.forEach(listing => {
            const price = listing.price;
            if (!priceGroups.has(price)) {
                priceGroups.set(price, {
                    price: price,
                    totalQuantity: 0,
                    listings: []
                });
            }
            const group = priceGroups.get(price);
            group.totalQuantity += listing.amount || 1; // Use amount, fallback to 1 if not available
            group.listings.push(listing);
        });

        let tableContent = `
            <table style="width: 100%; font-size: 11px; border-collapse: collapse;">
                <thead>
                    <tr style="background: #f8f9fa; border-bottom: 1px solid #dee2e6;">
                        <th style="padding: 4px 6px; text-align: left; border-right: 1px solid #dee2e6;">Qty</th>
                        <th style="padding: 4px 6px; text-align: right; border-right: 1px solid #dee2e6;">Price</th>
                        <th style="padding: 4px 6px; text-align: center;">Target</th>
                    </tr>
                </thead>
                <tbody>
        `;

        // Convert map to array and limit to first 50 entries for performance
        const groupArray = Array.from(priceGroups.values()).slice(0, 50);

        groupArray.forEach((group, index) => {
            const targetPrice = Math.max(1, group.price - 1); // $1 less, minimum $1
            const rowId = `price-row-${group.price}`;

            tableContent += `
                <tr style="border-bottom: 1px solid #eee; ${index % 2 === 0 ? 'background: #f9f9f9;' : ''}">
                    <td style="padding: 3px 6px; border-right: 1px solid #eee;">${group.totalQuantity.toLocaleString()}</td>
                    <td style="padding: 3px 6px; text-align: right; border-right: 1px solid #eee; font-weight: bold;">$${group.price.toLocaleString()}</td>
                    <td style="padding: 3px 6px; text-align: center;">
                        <div style="display: flex; align-items: center; justify-content: center; gap: 4px;">
                            <input type="checkbox" id="${rowId}" data-original-price="${group.price}" data-target-price="${targetPrice}" class="price-target-checkbox" style="margin: 0;">
                            <span style="font-size: 10px; color: #666;">$${targetPrice.toLocaleString()}</span>
                        </div>
                    </td>
                </tr>
            `;
        });

        if (priceGroups.size > 50) {
            tableContent += `
                <tr>
                    <td colspan="3" style="padding: 6px; text-align: center; color: #666; font-style: italic;">
                        ... and ${priceGroups.size - 50} more price points
                    </td>
                </tr>
            `;
        }

        tableContent += `
                </tbody>
            </table>
            <div style="padding: 8px; background: #f8f9fa; border-top: 1px solid #dee2e6; font-size: 10px; color: #666;">
                <div><strong>Selected Targets:</strong></div>
                <div id="selected-targets" style="margin-top: 4px; min-height: 16px;">None selected</div>
                <div style="margin-top: 6px;">
                    <button id="clear-targets-btn" style="background: #dc3545; color: white; border: none; padding: 2px 6px; border-radius: 2px; cursor: pointer; font-size: 10px;">Clear All</button>
                </div>
            </div>
        `;

        return tableContent;
    }

    // Extract item data from HTML element
    function extractItemData(element) {
        const itemId = element.getAttribute('data-item');
        const category = element.getAttribute('data-category') || 'Unknown';
        const qty = element.getAttribute('data-qty') || '1';
        const sort = element.getAttribute('data-sort') || '';
        const equipped = element.getAttribute('data-equipped') === 'true';
        const armoryId = element.getAttribute('data-armoryid');

        // Try to get item name from various sources
        let itemName = sort;
        if (!itemName) {
            const nameElement = element.querySelector('.name-wrap span, .item-name, .tooltip-wrap');
            if (nameElement) {
                itemName = nameElement.textContent.trim();
            }
        }

        // Clean up item name - remove quantity prefix if present (e.g., "1 Xanax" -> "Xanax")
        if (itemName) {
            // Remove leading numbers and spaces (quantity information)
            itemName = itemName.replace(/^\d+\s+/, '').trim();
        }

        // Fallback if still no name found
        if (!itemName) {
            itemName = `Item ${itemId}`;
        }

        return {
            id: itemId,
            name: itemName,
            category: category,
            quantity: parseInt(qty) || 1,
            equipped: equipped,
            armoryId: armoryId,
            tradeable: isItemTradeable(itemId),
            element: element
        };
    }

    // Scan currently visible items
    function scanVisibleItems() {
        // Try to find the currently active inventory tab/container
        let itemElements = [];

        // Check for all-items container first
        const allItemsContainer = document.querySelector('#all-items');
        if (allItemsContainer && allItemsContainer.style.display !== 'none') {
            itemElements = allItemsContainer.querySelectorAll('li[data-item][data-category]:not([data-action])');
        } else {
            // If not in all-items, look for any visible inventory container with items
            const inventoryContainers = document.querySelectorAll('.inventory-wrap, .category-wrap, [id*="items"]');
            for (const container of inventoryContainers) {
                if (container.style.display !== 'none' && !container.hidden) {
                    const containerItems = container.querySelectorAll('li[data-item][data-category]:not([data-action])');
                    if (containerItems.length > 0) {
                        itemElements = containerItems;
                        break;
                    }
                }
            }

            // Fallback: scan any visible items on the page
            if (itemElements.length === 0) {
                itemElements = document.querySelectorAll('li[data-item][data-category]:not([data-action])');
            }
        }

        if (itemElements.length === 0) {
            log('No items found in current view');
            return 0;
        }

        let newItems = 0;
        itemElements.forEach(element => {
            const itemData = extractItemData(element);
            if (itemData.id) {
                const key = `${itemData.id}_${itemData.armoryId || 'base'}`;
                if (!scannedItems.has(key)) {
                    scannedItems.set(key, itemData);
                    newItems++;
                }
            }
        });

        log(`Scanned ${newItems} new items from current category (Total: ${scannedItems.size})`);

        // Save scanned items to localStorage
        if (newItems > 0) {
            saveScannedItems();
        }

        return newItems;
    }

    // Save scanned items to localStorage
    function saveScannedItems() {
        try {
            const itemsArray = Array.from(scannedItems.values());
            localStorage.setItem(STORAGE_KEY, JSON.stringify(itemsArray));
            log(`Saved ${itemsArray.length} items to localStorage`);
        } catch (error) {
            log(`Failed to save items: ${error.message}`);
        }
    }

    // Navigate to all-items tab
    async function navigateToAllItems() {
        const allItemsTab = document.querySelector('a[href="#all-items"], #ui-id-1');
        if (allItemsTab) {
            log('Clicking all-items tab');
            allItemsTab.click();
            await sleep(1000);
            return true;
        }

        // Check if we're already on all-items
        const allItemsContainer = document.querySelector('#all-items');
        if (allItemsContainer && allItemsContainer.classList.contains('ui-tabs-panel')) {
            log('Already on all-items tab');
            return true;
        }

        log('Could not find all-items tab');
        return false;
    }

    // Check for new items without auto-scrolling
    function checkForNewItems() {
        // Just scan what's currently visible without scrolling
        return scanVisibleItems();
    }

    // Create floating table to display results
    function createFloatingTable() {
        if (floatingTable) {
            floatingTable.remove();
        }

        // Load saved window state
        const windowState = loadWindowState();

        floatingTable = document.createElement('div');
        floatingTable.id = 'inventory-scanner-table';
        floatingTable.style.position = 'fixed';
        floatingTable.style.left = windowState.x + 'px';
        floatingTable.style.top = windowState.y + 'px';
        floatingTable.style.width = windowState.width || WINDOW_DEFAULT_WIDTH;
        floatingTable.style.height = windowState.height || WINDOW_DEFAULT_HEIGHT;
        floatingTable.style.minWidth = WINDOW_MIN_WIDTH;
        floatingTable.style.minHeight = WINDOW_MIN_HEIGHT;
        floatingTable.style.maxWidth = '90vw';
        floatingTable.style.maxHeight = '90vh';
        floatingTable.style.background = '#fff';
        floatingTable.style.border = '2px solid #333';
        floatingTable.style.borderRadius = '8px';
        floatingTable.style.zIndex = '100000';
        floatingTable.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
        floatingTable.style.resize = 'both';
        floatingTable.style.overflow = 'hidden';
        floatingTable.innerHTML = `
                <div id="header-drag" style="background: #333; color: #fff; padding: 10px; border-radius: 6px 6px 0 0; cursor: move; display: flex; justify-content: space-between; align-items: center; user-select: none;">
                    <span id="window-title" style="cursor: pointer; flex: 1;">Scrap2Mark</span>
                    <div style="display: flex; align-items: center;">
                        <button id="close-table" style="background: none; border: none; color: #fff; cursor: pointer; font-size: 14px; line-height: 1; padding: 2px;">✕</button>
                    </div>
                </div>
                <div id="table-content" style="padding: 10px; height: calc(100% - 50px); overflow-y: auto; display: flex; flex-direction: column;">
                    <div id="api-controls" style="margin-bottom: 10px; padding: 6px; background: #f8f9fa; border-radius: 4px;">
                        <div style="display: flex; gap: 6px; align-items: center;">
                            <input type="text" id="api-key-input" placeholder="Insert API key here" autocomplete="on" name="api_key" style="flex: 1; padding: 4px 6px; border: 1px solid #ccc; border-radius: 3px; font-size: 11px; font-family: monospace;">
                            <button id="save-api-key" style="background: #17a2b8; color: white; border: none; padding: 4px 12px; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap;">Save</button>
                            <button id="logout-api-key" style="background: #dc3545; color: white; border: none; padding: 4px 12px; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap; display: none;">Logout</button>
                            <button id="fetch-api-data" style="background: #ffc107; color: black; border: none; padding: 4px 12px; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap;">Fetch</button>
                        </div>
                        <div style="font-size: 9px; color: #666; min-height: 12px; margin-top: 4px;">
                            <span id="api-status"></span>
                        </div>
                    </div>
                    <div id="scan-controls" style="margin-bottom: 10px;">
                        <button id="start-scan" style="background: #28a745; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; height: 36px;">Scan Visible Items</button>
                        <button id="clear-data" style="background: #dc3545; color: white; border: none; padding: 8px 8px; border-radius: 4px; cursor: pointer; margin-left: 2px; margin-right: 10px; height: 36px; font-size: 12px;">✗</button>
                        <button id="fetch-market-prices" style="background: #ffc107; color: black; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; height: 36px;">Get Market Prices</button>
                        <button id="clear-market-cache" style="background: #dc3545; color: white; border: none; padding: 8px 8px; border-radius: 4px; cursor: pointer; margin-left: 2px; margin-right: 10px; height: 36px; font-size: 12px;">✗</button>
                        <button id="post-market-button" style="background: #17a2b8; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; height: 36px;">Fill Market Forms</button>
                        <button id="clear-price-targets" style="background: #dc3545; color: white; border: none; padding: 8px 8px; border-radius: 4px; cursor: pointer; margin-left: 2px; height: 36px; font-size: 12px;">✗</button>
                    </div>
                    <div style="margin-bottom: 10px; font-size: 11px; color: #666;">
                        Manual: Scroll down to load more items, then click "Scan Visible Items" again.
                    </div>
                    <div id="scan-status" style="margin-bottom: 10px; font-weight: bold;"></div>
                    <div style="flex: 1; overflow-y: auto; min-height: 200px;">
                        <div style="margin-bottom: 10px;">
                            <div style="display: flex; align-items: center; margin-bottom: 5px; flex-wrap: wrap; gap: 10px;">
                                <h4 style="margin: 0; font-size: 14px;">Scanned Items:</h4>
                                <label style="display: flex; align-items: center; font-size: 12px; cursor: pointer;">
                                    <input type="checkbox" id="show-non-tradeable" style="margin-right: 4px;">
                                    Show non-tradeable
                                </label>
                                <label style="display: flex; align-items: center; font-size: 12px; cursor: pointer;">
                                    <input type="checkbox" id="show-ignored" style="margin-right: 4px;">
                                    Show ignored items
                                </label>
                                <label style="display: flex; align-items: center; font-size: 12px; cursor: pointer;">
                                    <input type="checkbox" id="hide-low-value" checked style="margin-right: 4px;">
                                    Hide below $
                                    <input type="text" id="min-value-input" value="5,000,000" style="width: 70px; margin-left: 4px; padding: 2px; border: 1px solid #ccc; border-radius: 2px; font-size: 11px;" placeholder="5000000">
                                </label>
                            </div>
                            <table id="all-items-table" style="width: 100%; border-collapse: collapse; font-size: 12px; table-layout: fixed;">
                                <thead>
                                    <tr style="background: #f8f9fa; border-bottom: 2px solid #dee2e6;">
                                        <th id="header-name" style="padding: 8px; text-align: left; border: 1px solid #dee2e6; cursor: pointer; user-select: none;">
                                            Name <span id="sort-name" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-category" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 80px; cursor: pointer; user-select: none;">
                                            Category <span id="sort-category" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-qty" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 40px; cursor: pointer; user-select: none;">
                                            Qty <span id="sort-qty" style="float: right;">↕</span>
                                        </th>
                                        <th id="trade-column-header" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 50px; cursor: pointer; user-select: none; display: none;">
                                            Trade <span id="sort-trade" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-price" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 70px; cursor: pointer; user-select: none;">
                                            Price <span id="sort-price" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-estimated" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 90px; cursor: pointer; user-select: none;">
                                            Est. Value <span id="sort-estimated" style="float: right;">↕</span>
                                        </th>
                                        <th style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 50px;">
                                            Action
                                        </th>
                                    </tr>
                                </thead>
                                <tbody id="items-table-body">
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        `;

        document.body.appendChild(floatingTable);

        // Make draggable
        let isDragging = false;
        let dragStarted = false;
        let currentX, currentY, initialX, initialY;
        const header = document.getElementById('header-drag');
        const windowTitle = document.getElementById('window-title');

        function startDrag(e) {
            e.preventDefault();
            isDragging = true;
            dragStarted = false;

            // Get the current position of the floating table
            const rect = floatingTable.getBoundingClientRect();

            // Calculate offset from mouse to top-left corner of element
            initialX = e.clientX - rect.left;
            initialY = e.clientY - rect.top;

            document.addEventListener('mousemove', doDrag);
            document.addEventListener('mouseup', stopDrag);
        }

        function doDrag(e) {
            if (!isDragging) return;
            e.preventDefault();

            // Mark that dragging has actually started (mouse moved)
            if (!dragStarted) {
                dragStarted = true;
            }

            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;

            // Keep the window within the viewport
            const maxX = window.innerWidth - floatingTable.offsetWidth;
            const maxY = window.innerHeight - floatingTable.offsetHeight;

            currentX = Math.max(0, Math.min(currentX, maxX));
            currentY = Math.max(0, Math.min(currentY, maxY));

            floatingTable.style.left = currentX + 'px';
            floatingTable.style.top = currentY + 'px';
            floatingTable.style.right = 'auto'; // Remove right positioning when dragging
        }

        function stopDrag() {
            isDragging = false;
            document.removeEventListener('mousemove', doDrag);
            document.removeEventListener('mouseup', stopDrag);

            // Save position when drag ends (but keep existing dimensions)
            const rect = floatingTable.getBoundingClientRect();
            saveWindowState(rect.left, rect.top, windowState.width || WINDOW_DEFAULT_WIDTH, windowState.height || WINDOW_DEFAULT_HEIGHT, isMinimized());
        }

        function handleTitleClick(e) {
            // Only toggle minimize if we didn't actually drag
            if (!dragStarted) {
                toggleMinimize();
            }
        }

        function handleHeaderClick(e) {
            // Only toggle minimize if we didn't actually drag and didn't click on close button
            if (!dragStarted && e.target.id !== 'close-table') {
                toggleMinimize();
            }
        }

        function toggleMinimize() {
            const content = document.getElementById('table-content');
            if (content.style.display === 'none') {
                // Restoring from minimized state
                content.style.display = 'flex';
                floatingTable.style.width = windowState.width || WINDOW_DEFAULT_WIDTH;
                floatingTable.style.height = windowState.height || WINDOW_DEFAULT_HEIGHT;
                floatingTable.style.resize = 'both';
                // Restore normal constraints
                floatingTable.style.minWidth = WINDOW_MIN_WIDTH;
                floatingTable.style.minHeight = WINDOW_MIN_HEIGHT;
                floatingTable.style.maxWidth = '90vw';
                floatingTable.style.maxHeight = '90vh';
            } else {
                // Minimizing
                content.style.display = 'none';
                floatingTable.style.width = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.height = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.resize = 'none';
                // Override constraints for minimized state
                floatingTable.style.minWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.minHeight = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.maxWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.maxHeight = WINDOW_MINIMIZED_HEIGHT;
            }

            // Save minimized state
            const rect = floatingTable.getBoundingClientRect();
            saveWindowState(rect.left, rect.top, windowState.width || WINDOW_DEFAULT_WIDTH, windowState.height || WINDOW_DEFAULT_HEIGHT, content.style.display === 'none');
        }

        function isMinimized() {
            const content = document.getElementById('table-content');
            return content && content.style.display === 'none';
        }

        header.addEventListener('mousedown', startDrag);
        header.addEventListener('mouseup', handleHeaderClick);

        // Restore minimized state
        if (windowState.minimized) {
            const content = document.getElementById('table-content');
            if (content) {
                content.style.display = 'none';
                floatingTable.style.width = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.height = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.resize = 'none';
                // Force the size override any stored dimensions
                floatingTable.style.minWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.minHeight = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.maxWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.maxHeight = WINDOW_MINIMIZED_HEIGHT;
            }
        } else {
            // Ensure resize is enabled for normal state and restore constraints
            floatingTable.style.resize = 'both';
            floatingTable.style.minWidth = WINDOW_MIN_WIDTH;
            floatingTable.style.minHeight = WINDOW_MIN_HEIGHT;
            floatingTable.style.maxWidth = '90vw';
            floatingTable.style.maxHeight = '90vh';
        }

        // Add resize observer to save window state when resized
        if (window.ResizeObserver) {
            const resizeObserver = new ResizeObserver((entries) => {
                for (let entry of entries) {
                    const rect = entry.target.getBoundingClientRect();

                    // Only update dimensions if not minimized
                    if (!isMinimized()) {
                        windowState.width = rect.width + 'px';
                        windowState.height = rect.height + 'px';
                        // Debounce the save operation
                        clearTimeout(floatingTable.resizeTimeout);
                        floatingTable.resizeTimeout = setTimeout(() => {
                            saveWindowState(rect.left, rect.top, rect.width + 'px', rect.height + 'px', isMinimized());
                        }, 500);
                    }
                }
            });
            resizeObserver.observe(floatingTable);
        }

        // Event listeners
        document.getElementById('close-table').addEventListener('click', () => {
            floatingTable.remove();
            floatingTable = null;
        });

        document.getElementById('start-scan').addEventListener('click', startInventoryScan);
        document.getElementById('clear-data').addEventListener('click', () => {
            if (confirm('Clear all scanned inventory data? This cannot be undone.')) {
                clearInventoryData();
            }
        });
        document.getElementById('post-market-button').addEventListener('click', handlePostToMarket);
        document.getElementById('save-api-key').addEventListener('click', handleSaveApiKey);
        document.getElementById('logout-api-key').addEventListener('click', handleLogoutApiKey);
        document.getElementById('fetch-api-data').addEventListener('click', handleFetchApiData);
        document.getElementById('show-non-tradeable').addEventListener('change', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('show-ignored').addEventListener('change', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('hide-low-value').addEventListener('change', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('min-value-input').addEventListener('input', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('fetch-market-prices').addEventListener('click', handleFetchMarketPrices);
        document.getElementById('clear-market-cache').addEventListener('click', () => {
            if (confirm('Clear market price cache? This will remove all cached market data.')) {
                handleClearMarketCache();
            }
        });
        document.getElementById('clear-price-targets').addEventListener('click', () => {
            if (confirm('Clear all price targets? This will remove all selected prices for market posting.')) {
                clearAllPriceTargets();
            }
        });

        // Add column header event listeners for sorting
        document.getElementById('header-name').addEventListener('click', () => sortTableDynamic('name'));
        document.getElementById('header-category').addEventListener('click', () => sortTableDynamic('category'));
        document.getElementById('header-qty').addEventListener('click', () => sortTableDynamic('qty'));
        document.getElementById('trade-column-header').addEventListener('click', () => sortTableDynamic('trade'));
        document.getElementById('header-price').addEventListener('click', () => sortTableDynamic('price'));
        document.getElementById('header-estimated').addEventListener('click', () => sortTableDynamic('estimated'));

        // Load ignored items from storage
        loadIgnoredItems();

        // Load and apply filter state
        const filterState = loadFilterState();
        applyFilterState(filterState);

        updateTableDisplay();
    }

    // Handle API key saving
    function handleSaveApiKey() {
        const apiKeyInput = document.getElementById('api-key-input');
        const apiStatus = document.getElementById('api-status');

        const key = apiKeyInput.value.trim();
        if (!key) {
            apiStatus.textContent = 'Please enter an API key';
            apiStatus.style.color = '#dc3545';
            return;
        }

        saveApiKey(key);
        apiKeyInput.value = '';
        apiStatus.textContent = 'API key saved!';
        apiStatus.style.color = '#28a745';

        // Update button visibility
        updateApiButtonVisibility();

        setTimeout(() => {
            updateApiStatusDisplay();
        }, 3000);
    }

    // Handle API key logout
    function handleLogoutApiKey() {
        // Clear API key from storage
        localStorage.removeItem(API_KEY_STORAGE_KEY);
        localStorage.removeItem(API_KEY_EXPIRY_STORAGE_KEY);
        apiKey = null;

        const apiStatus = document.getElementById('api-status');
        apiStatus.textContent = 'Logged out successfully';
        apiStatus.style.color = '#28a745';

        // Update button visibility
        updateApiButtonVisibility();

        setTimeout(() => {
            updateApiStatusDisplay();
        }, 3000);
    }

    // Update API button visibility based on key status
    function updateApiButtonVisibility() {
        const saveBtn = document.getElementById('save-api-key');
        const logoutBtn = document.getElementById('logout-api-key');
        const apiKeyInput = document.getElementById('api-key-input');

        if (apiKey) {
            saveBtn.style.display = 'none';
            logoutBtn.style.display = 'block';
            if (apiKeyInput) {
                apiKeyInput.placeholder = 'API key is saved';
                apiKeyInput.disabled = true;
                apiKeyInput.style.backgroundColor = '#f8f9fa';
                apiKeyInput.style.color = '#6c757d';
            }
        } else {
            saveBtn.style.display = 'block';
            logoutBtn.style.display = 'none';
            if (apiKeyInput) {
                apiKeyInput.placeholder = 'Insert API key here';
                apiKeyInput.disabled = false;
                apiKeyInput.style.backgroundColor = '#fff';
                apiKeyInput.style.color = '#000';
            }
        }
    }

    // Handle API data fetching
    async function handleFetchApiData() {
        const apiStatus = document.getElementById('api-status');
        const fetchBtn = document.getElementById('fetch-api-data');

        if (!apiKey) {
            apiStatus.textContent = 'Please save an API key first';
            apiStatus.style.color = '#dc3545';
            return;
        }

        fetchBtn.disabled = true;
        apiStatus.textContent = 'Fetching items data...';
        apiStatus.style.color = '#17a2b8';

        try {
            await fetchItemsFromAPI(apiKey);
            apiStatus.textContent = 'Items data fetched successfully!';
            apiStatus.style.color = '#28a745';

            // Update display if we have scanned items
            updateTableDisplay();

        } catch (error) {
            log(`Failed to fetch API data: ${error.message}`);
            apiStatus.textContent = `Error: ${error.message}`;
            apiStatus.style.color = '#dc3545';
        } finally {
            fetchBtn.disabled = false;
            setTimeout(() => {
                apiStatus.textContent = '';
            }, 5000);
        }
    }

    // Handle market price fetching
    async function handleFetchMarketPrices() {
        const marketBtn = document.getElementById('fetch-market-prices');
        const statusDiv = document.getElementById('scan-status');

        if (!apiKey) {
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #dc3545;">Please save an API key first</span>';
            }
            return;
        }

        if (scannedItems.size === 0) {
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #dc3545;">No items to fetch prices for. Scan items first.</span>';
            }
            return;
        }

        // Reset any stuck queue state
        if (isProcessingQueue) {
            log('Resetting stuck queue state');
            isProcessingQueue = false;
            apiRequestQueue.length = 0; // Clear any pending requests
        }

        marketBtn.disabled = true;
        marketBtn.textContent = 'Fetching...';

        const tradeableItems = Array.from(scannedItems.values()).filter(item => item.tradeable === true);

        if (tradeableItems.length === 0) {
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #ffc107;">No tradeable items found to fetch prices for</span>';
            }
            marketBtn.disabled = false;
            marketBtn.textContent = 'Get Market Prices';
            return;
        }

        let processed = 0;
        let errors = 0;
        const total = tradeableItems.length;
        const startTime = Date.now();
        const maxProcessingTime = 300000; // 5 minutes maximum

        try {
            for (const item of tradeableItems) {
                // Check for timeout
                if (Date.now() - startTime > maxProcessingTime) {
                    log('Market price fetching timed out after 5 minutes');
                    if (statusDiv) {
                        statusDiv.innerHTML = '<span style="color: #ffc107;">Market price fetching timed out. Try again or reduce the number of items.</span>';
                    }
                    break;
                }

                try {
                    if (statusDiv) {
                        statusDiv.innerHTML = `Fetching market data: ${processed + 1}/${total} (${item.name})`;
                    }

                    // Add timeout for individual requests
                    const requestPromise = queueMarketDataRequest(item.id);
                    const timeoutPromise = new Promise((_, reject) => {
                        setTimeout(() => reject(new Error('Individual request timeout')), 60000); // 1 minute per request
                    });

                    await Promise.race([requestPromise, timeoutPromise]);
                    processed++;

                } catch (error) {
                    errors++;
                    log(`Failed to fetch market data for item ${item.id}: ${error.message}`);

                    // If we hit rate limits or serious errors, stop
                    if (error.message.includes('Rate limit') ||
                        error.message.includes('IP temporarily blocked') ||
                        error.message.includes('Daily API read limit') ||
                        error.message.includes('Throttling timeout')) {
                        break;
                    }
                }

                // Update table periodically
                if (processed % 5 === 0) {
                    updateTableDisplay();
                }
            }

            updateTableDisplay();

            if (statusDiv) {
                if (errors > 0) {
                    statusDiv.innerHTML = `<span style="color: #ffc107;">Market data fetch completed: ${processed}/${total} successful, ${errors} errors</span>`;
                } else {
                    statusDiv.innerHTML = `<span style="color: #28a745;">Market data fetch completed: ${processed}/${total} items processed</span>`;
                }
            }

        } catch (error) {
            log(`Market data fetch failed: ${error.message}`);
            if (statusDiv) {
                statusDiv.innerHTML = `<span style="color: #dc3545;">Market data fetch failed: ${error.message}</span>`;
            }
        } finally {
            marketBtn.disabled = false;
            marketBtn.textContent = 'Get Market Prices';

            // Reset queue state to prevent hanging
            isProcessingQueue = false;

            setTimeout(() => {
                if (statusDiv && statusDiv.innerHTML.includes('Market data fetch')) {
                    updateTableDisplay(); // This will restore the normal status display
                }
            }, 5000);
        }
    }

    // Reset API state (useful for debugging hanging issues)
    function resetApiState() {
        log('Resetting API state');
        isProcessingQueue = false;
        apiRequestQueue.length = 0;
        apiRequestCount = 0;
        apiRequestResetTime = Date.now();
        lastApiRequest = 0;
    }

    // Clear sort state
    function clearSortState() {
        currentSortColumn = -1;
        currentSortDirection = 'asc';
        currentSortField = null;

        // Clear all sort indicators
        ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
            const indicator = document.getElementById(`sort-${col}`);
            if (indicator) indicator.textContent = '↕';
        });
    }

    // Handle clear market cache
    function handleClearMarketCache() {
        const clearBtn = document.getElementById('clear-market-cache');
        const statusDiv = document.getElementById('scan-status');

        // Clear market data from memory and storage
        marketData.clear();
        localStorage.removeItem(MARKET_DATA_STORAGE_KEY);
        localStorage.removeItem(MARKET_EXPIRY_STORAGE_KEY);

        // Also reset API state to prevent any stuck conditions
        resetApiState();

        // Update display
        updateTableDisplay();

        if (statusDiv) {
            statusDiv.innerHTML = '<span style="color: #28a745;">Market cache cleared successfully</span>';
            setTimeout(() => {
                updateTableDisplay(); // This will restore the normal status display
            }, 3000);
        }

        log('Market cache cleared and API state reset');
    }
    // Update table display
    function updateTableDisplay() {
        if (!floatingTable) {
            log('Warning: floatingTable not found in updateTableDisplay');
            return;
        }

        const tbody = document.getElementById('items-table-body');
        const statusDiv = document.getElementById('scan-status');
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');

        if (!tbody) {
            log('Warning: items-table-body not found in updateTableDisplay');
            return;
        }

        tbody.innerHTML = '';

        const showNonTradeable = showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false;
        const showIgnored = showIgnoredCheckbox ? showIgnoredCheckbox.checked : false;
        const hideLowValue = hideLowValueCheckbox ? hideLowValueCheckbox.checked : false;
        const minValue = minValueInput ? parseFloat(minValueInput.value.replace(/[,$]/g, '')) || 0 : 0;

        // Toggle Trade column visibility based on checkbox state
        const tradeColumnHeader = document.getElementById('trade-column-header');

        if (tradeColumnHeader) {
            tradeColumnHeader.style.display = showNonTradeable ? 'table-cell' : 'none';
        }

        // Sort items using current sort state or default sorting
        const sortedItems = applySortToItems(Array.from(scannedItems.values()));

        let tradeableCount = 0;
        let nonTradeableCount = 0;
        let ignoredCount = 0;
        let displayedCount = 0;
        let hiddenLowValueCount = 0;

        sortedItems.forEach(item => {
            const itemId = item.id || 'N/A';
            const itemName = item.name || 'Unknown';
            const itemCategory = item.category || 'Unknown';
            const itemQuantity = item.quantity || 0;
            const itemTradeable = item.tradeable;
            const itemIgnored = isItemIgnored(itemId);

            // Count all items
            if (itemTradeable === true) {
                tradeableCount++;
            } else if (itemTradeable === false) {
                nonTradeableCount++;
            }

            if (itemIgnored) {
                ignoredCount++;
            }

            // Filter display based on checkboxes
            if (!showNonTradeable && itemTradeable === false) {
                return; // Skip non-tradeable items when checkbox is unchecked
            }

            if (!showIgnored && itemIgnored) {
                return; // Skip ignored items when checkbox is unchecked
            }

            // Calculate estimated value for low-value filtering
            const estimatedValueForFilter = calculateEstimatedValue(itemId, itemQuantity);

            // Filter by minimum value if enabled
            if (hideLowValue && estimatedValueForFilter < minValue) {
                hiddenLowValueCount++;
                return; // Skip low-value items when checkbox is checked
            }

            displayedCount++;

            // Determine tradeable status display and row styling
            let tradeableDisplay = '';
            let tradeableColor = '#666';
            let rowStyle = '';

            if (itemIgnored) {
                rowStyle = 'background-color: #f0f0f0; opacity: 0.7;'; // Gray background for ignored items
            } else if (itemTradeable === true) {
                tradeableDisplay = '✓';
                tradeableColor = '#28a745';
            } else if (itemTradeable === false) {
                tradeableDisplay = '✗';
                tradeableColor = '#dc3545';
                rowStyle = 'background-color: #f8d7da;'; // Light red background for non-tradeable
            } else {
                tradeableDisplay = '?';
                tradeableColor = '#ffc107';
            }

            // Override trade display for ignored items
            if (itemIgnored) {
                tradeableDisplay = itemTradeable === true ? '✓' : itemTradeable === false ? '✗' : '?';
            }

            // Get market data for price display
            let priceDisplay = '';
            const marketInfo = marketData.get(itemId.toString());

            if (marketInfo && itemTradeable === true) {
                const stats = calculatePriceStats(marketInfo.listings);
                if (stats) {
                    const chart = createPriceChart(marketInfo.listings);
                    const age = Math.round((Date.now() - marketInfo.timestamp) / (1000 * 60)); // age in minutes
                    const ageText = age < 60 ? `${age}m` : `${Math.round(age / 60)}h`;
                    const outliersText = stats.outliersFiltered > 0 ? ` (${stats.outliersFiltered} high outliers, ${stats.sellersFiltered} sellers filtered)` : '';

                    // Use a price closer to low end - take the lower of P25 or 20% above minimum
                    const lowPrice = Math.min(stats.p25, stats.min * 1.2);
                    const displayPrice = Math.round(lowPrice);

                    const tooltip = `Display: $${displayPrice.toLocaleString()} (Low-end price)\nMin: $${stats.min.toLocaleString()}\nMax: $${stats.max.toLocaleString()}\nAvg: $${Math.round(stats.average).toLocaleString()}\nMedian: $${stats.median.toLocaleString()}\nQ1-Q3: $${stats.p25.toLocaleString()}-$${stats.p75.toLocaleString()}\nSellers: ${stats.uniqueSellers}/${stats.totalUniqueSellers}\nListings: ${stats.listings}${outliersText}\nFiltered: ${stats.priceCapFiltered} 10x+ prices, ${stats.statFiltered} statistical\nTotal Qty: ${stats.totalQuantity.toLocaleString()}\nAge: ${ageText}`;

                    priceDisplay = `
                        <div style="display: flex; flex-direction: column; align-items: center;">
                            <div style="font-size: 9px; color: #666;">$${displayPrice.toLocaleString()}</div>
                            <div class="price-chart-hover" data-item-id="${itemId}" data-item-name="${itemName}" style="cursor: help;">${chart}</div>
                        </div>
                    `;
                }
            } else if (itemTradeable === true) {
                priceDisplay = '<div style="font-size: 9px; color: #999; text-align: center;">No data</div>';
            } else {
                priceDisplay = '<div style="font-size: 9px; color: #999; text-align: center;">-</div>';
            }

            // Calculate estimated value
            const estimatedValue = calculateEstimatedValue(itemId, itemQuantity);
            const estimatedValueDisplay = estimatedValue > 0 ? `$${Math.round(estimatedValue).toLocaleString()}` : '-';

            // Create action button (ignore)
            const ignoreButtonText = itemIgnored ? 'Unignore' : 'Ignore';
            const ignoreButtonClass = itemIgnored ? 'ignore-btn unignore' : 'ignore-btn ignore';

            // Create row
            const row = document.createElement('tr');
            row.style.borderBottom = '1px solid #dee2e6';
            row.style.cssText += rowStyle;
            row.innerHTML = `
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${itemName}">${itemName}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center;">${itemCategory}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center;">${itemQuantity}</td>
                <td class="trade-column-cell" style="padding: 4px; border: 1px solid #dee2e6; color: ${tradeableColor}; font-weight: bold; text-align: center; display: none;">${tradeableDisplay}</td>
                <td style="padding: 2px; border: 1px solid #dee2e6; text-align: center;">${priceDisplay}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center; font-weight: bold; color: ${estimatedValue > 0 ? '#28a745' : '#666'};">${estimatedValueDisplay}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center;">
                    <button class="${ignoreButtonClass}" data-item-id="${itemId}">${ignoreButtonText}</button>
                </td>
            `;

            // Add event listener to the ignore button
            const ignoreButton = row.querySelector('.ignore-btn');
            if (ignoreButton) {
                ignoreButton.addEventListener('click', () => {
                    toggleIgnoreItem(itemId);
                });
            }

            tbody.appendChild(row);
        });

        // Update Trade column cell visibility for all rows
        const tradeColumnCells = document.querySelectorAll('.trade-column-cell');
        tradeColumnCells.forEach(cell => {
            cell.style.display = showNonTradeable ? 'table-cell' : 'none';
        });

        const unknownCount = sortedItems.length - tradeableCount - nonTradeableCount;
        const filterText = showNonTradeable ? '' : ' (filtered)';

        // Status line: Total, Tradeable, and conditionally Non-Tradeable/Unknown/Ignored/Low-Value
        let statusHtml = `Total: ${displayedCount}${filterText} | <span style="color: #28a745;">Tradeable: ${tradeableCount}</span>`;

        if (showNonTradeable && (nonTradeableCount > 0 || unknownCount > 0)) {
            statusHtml += ` | <span style="color: #dc3545;">Non-Tradeable: ${nonTradeableCount}</span> | <span style="color: #ffc107;">Unknown: ${unknownCount}</span>`;
        }

        if (ignoredCount > 0) {
            statusHtml += ` | <span style="color: #6c757d;">Ignored: ${ignoredCount}</span>`;
        }

        if (hiddenLowValueCount > 0) {
            statusHtml += ` | <span style="color: #17a2b8;">Low-Value Hidden: ${hiddenLowValueCount}</span>`;
        }

        if (statusDiv) {
            statusDiv.innerHTML = statusHtml;
        } else {
            log('Warning: scan-status element not found, unable to update status display');
        }

        // Add hover popup functionality for price charts (with small delay to ensure DOM is updated)
        setTimeout(() => {
            addPriceChartHoverHandlers();

            // Restore sort indicators after table update
            if (currentSortField && currentSortColumn !== -1) {
                // Clear all sort indicators
                ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
                    const indicator = document.getElementById(`sort-${col}`);
                    if (indicator) indicator.textContent = '↕';
                });

                // Set current sort indicator
                const currentIndicator = document.getElementById(`sort-${currentSortField}`);
                if (currentIndicator) {
                    currentIndicator.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
                }
            }
        }, 10);
    }

    // Global timeout for popup hiding
    let globalHideTimeout;

    // Add hover popup functionality
    function addPriceChartHoverHandlers() {
        const priceCharts = document.querySelectorAll('.price-chart-hover');
        console.log(`Found ${priceCharts.length} price charts to attach hover handlers to`);

        priceCharts.forEach(chart => {
            let showTimeout;

            chart.addEventListener('mouseenter', (e) => {
                console.log('Price chart hover started');
                // Clear any existing hide timeout
                if (globalHideTimeout) {
                    clearTimeout(globalHideTimeout);
                    globalHideTimeout = null;
                }

                // Capture the data immediately before the timeout
                const itemId = e.currentTarget.getAttribute('data-item-id');
                const itemName = e.currentTarget.getAttribute('data-item-name');
                const mouseEvent = { clientX: e.clientX, clientY: e.clientY };

                // Show popup after 300ms delay (reduced for faster response)
                showTimeout = setTimeout(() => {
                    console.log('Showing price popup');
                    const marketInfo = marketData.get(itemId);

                    console.log(`Item ID: ${itemId}, Name: ${itemName}, Market Info:`, marketInfo);

                    if (marketInfo && marketInfo.listings) {
                        showPricePopup(mouseEvent, marketInfo.listings, itemName, itemId);
                    } else {
                        console.log('No market info or listings available for this item');
                    }
                }, 300);
            });

            chart.addEventListener('mouseleave', () => {
                console.log('Price chart hover ended');
                // Clear show timeout if still pending
                if (showTimeout) {
                    clearTimeout(showTimeout);
                    showTimeout = null;
                }

                // Hide popup after 2 seconds delay (increased to allow easier access to popup)
                globalHideTimeout = setTimeout(() => {
                    hidePricePopup();
                }, 2000);
            });
        });
    }

    // Update existing popup with new chart content
    function updatePricePopup(listings, itemName, zoomMin = null, zoomMax = null) {
        const popup = document.getElementById('price-popup');
        if (!popup) return;

        // Update the content with new chart
        popup.innerHTML = createDetailedPriceChart(listings, itemName, zoomMin, zoomMax);

        // Re-add chart interaction handlers after content update
        addChartInteraction(listings, itemName);

        // Re-add panel handlers after content update
        addPanelHandlers();

        // Keep popup in same general position but adjust if needed
        const rect = popup.getBoundingClientRect();
        let left = rect.left;
        let top = rect.top;

        // Adjust if popup would go off screen after content change
        const newRect = popup.getBoundingClientRect();
        if (left + newRect.width > window.innerWidth) {
            left = window.innerWidth - newRect.width - 10;
        }
        if (top + newRect.height > window.innerHeight) {
            top = window.innerHeight - newRect.height - 10;
        }
        if (left < 0) left = 10;
        if (top < 0) top = 10;

        popup.style.left = left + 'px';
        popup.style.top = top + 'px';
    }

    // Show detailed price popup
    function showPricePopup(event, listings, itemName, itemId, zoomMin = null, zoomMax = null) {
        // Load stored price targets for this item
        loadPriceTargets(itemId);

        // Remove existing popup
        hidePricePopup();

        const popup = document.createElement('div');
        popup.id = 'price-popup';
        popup.innerHTML = createDetailedPriceChart(listings, itemName, zoomMin, zoomMax);
        popup.style.position = 'fixed';
        popup.style.zIndex = '1000000';
        popup.style.maxWidth = '350px';
        popup.style.pointerEvents = 'auto';

        // Make popup interactive
        popup.addEventListener('mouseenter', () => {
            // Cancel any pending hide timeouts when hovering over popup
            if (globalHideTimeout) {
                clearTimeout(globalHideTimeout);
                globalHideTimeout = null;
            }
        });

        popup.addEventListener('mouseleave', () => {
            // Hide popup when leaving popup area
            globalHideTimeout = setTimeout(() => {
                hidePricePopup();
            }, 500);
        });

        document.body.appendChild(popup);

        // Position popup near mouse but keep it in viewport
        const rect = popup.getBoundingClientRect();
        let left = event.clientX + 10;
        let top = event.clientY + 10;

        // Adjust if popup would go off screen
        if (left + rect.width > window.innerWidth) {
            left = event.clientX - rect.width - 10;
        }
        if (top + rect.height > window.innerHeight) {
            top = event.clientY - rect.height - 10;
        }
        if (left < 0) left = 10;
        if (top < 0) top = 10;

        popup.style.left = left + 'px';
        popup.style.top = top + 'px';

        // Add chart interaction handlers after popup is added to DOM
        addChartInteraction(listings, itemName);

        // Add panel expansion handlers
        addPanelHandlers();
    }

    // Add panel expansion and interaction handlers
    function addPanelHandlers() {
        const expandBtn = document.getElementById('expand-listings');
        const collapseBtn = document.getElementById('collapse-listings');
        const panel = document.getElementById('listings-panel');
        const clearBtn = document.getElementById('clear-targets-btn');
        const autoSelectBtn = document.getElementById('auto-select-btn');

        if (expandBtn && panel) {
            expandBtn.addEventListener('click', () => {
                panel.style.display = 'block';
                expandBtn.style.display = 'none';
            });
        }

        if (collapseBtn && panel) {
            collapseBtn.addEventListener('click', () => {
                panel.style.display = 'none';
                if (expandBtn) expandBtn.style.display = 'flex';
            });
        }

        // Add event listeners for price target checkboxes
        const checkboxes = document.querySelectorAll('.price-target-checkbox');
        checkboxes.forEach(checkbox => {
            const originalPrice = parseFloat(checkbox.getAttribute('data-original-price'));

            // Restore checkbox state from stored data
            if (selectedPriceTargets.has(originalPrice)) {
                checkbox.checked = true;
            }

            checkbox.addEventListener('change', (e) => {
                const originalPrice = parseFloat(e.target.getAttribute('data-original-price'));
                const targetPrice = parseFloat(e.target.getAttribute('data-target-price'));
                const isChecked = e.target.checked;

                console.log(`Checkbox changed: ${originalPrice} -> ${targetPrice}, checked: ${isChecked}`);
                togglePriceTarget(originalPrice, targetPrice, isChecked);
            });
        });

        // Update the display with loaded targets
        updateSelectedTargetsDisplay();

        // Add event listeners for control buttons
        if (clearBtn) {
            clearBtn.addEventListener('click', () => {
                console.log('Clear all targets clicked');
                clearAllTargets();
            });
        }
    }

    // Global storage for selected price targets (persistent across sessions)
    let selectedPriceTargets = new Map();
    let currentItemId = null;

    // Load price targets from localStorage
    function loadPriceTargets(itemId) {
        currentItemId = itemId;
        const storageKey = `scrap2mark_price_targets_${itemId}`;
        const stored = localStorage.getItem(storageKey);

        if (stored) {
            try {
                const parsed = JSON.parse(stored);
                selectedPriceTargets = new Map(Object.entries(parsed).map(([k, v]) => [parseFloat(k), v]));
                console.log(`Loaded ${selectedPriceTargets.size} price targets for item ${itemId}`);
            } catch (e) {
                console.log('Error loading price targets:', e);
                selectedPriceTargets = new Map();
            }
        } else {
            selectedPriceTargets = new Map();
        }
    }

    // Save price targets to localStorage
    function savePriceTargets() {
        if (!currentItemId) return;

        const storageKey = `scrap2mark_price_targets_${currentItemId}`;
        const toStore = Object.fromEntries(selectedPriceTargets.entries());
        localStorage.setItem(storageKey, JSON.stringify(toStore));
        console.log(`Saved ${selectedPriceTargets.size} price targets for item ${currentItemId}`);
    }

    // Toggle price target selection
    function togglePriceTarget(originalPrice, targetPrice, isSelected) {
        if (isSelected) {
            selectedPriceTargets.set(originalPrice, targetPrice);
        } else {
            selectedPriceTargets.delete(originalPrice);
        }
        updateSelectedTargetsDisplay();
        savePriceTargets();
    }

    // Update the display of selected targets
    function updateSelectedTargetsDisplay() {
        const targetDiv = document.getElementById('selected-targets');
        if (!targetDiv) return;

        if (selectedPriceTargets.size === 0) {
            targetDiv.innerHTML = 'None selected';
            targetDiv.style.color = '#666';
        } else {
            const targets = Array.from(selectedPriceTargets.entries())
                .sort((a, b) => a[1] - b[1]) // Sort by target price
                .map(([original, target]) => `$${target.toLocaleString()}`)
                .join(', ');
            targetDiv.innerHTML = targets;
            targetDiv.style.color = '#28a745';
        }
    }

    // Clear all selected targets
    function clearAllTargets() {
        console.log('Clearing all targets');
        selectedPriceTargets.clear();
        // Uncheck all checkboxes
        const checkboxes = document.querySelectorAll('.price-target-checkbox');
        console.log(`Found ${checkboxes.length} checkboxes to clear`);
        checkboxes.forEach(cb => {
            cb.checked = false;
        });
        updateSelectedTargetsDisplay();
        savePriceTargets();
    }

    // Make functions global so they can be called from HTML
    window.togglePriceTarget = togglePriceTarget;
    window.clearAllTargets = clearAllTargets;

    // Add chart interaction functionality
    function addChartInteraction(listings, itemName) {
        const svg = document.getElementById('price-chart-svg');
        const selectionRect = document.getElementById('selection-rect');
        const resetZoomBtn = document.getElementById('reset-zoom');

        if (!svg || !selectionRect) return;

        let isSelecting = false;
        let startX = 0;
        let currentX = 0;

        // Get chart parameters
        const padding = parseInt(svg.getAttribute('data-padding'));
        const chartWidth = parseInt(svg.getAttribute('data-chart-width'));
        const chartHeight = parseInt(svg.getAttribute('data-chart-height'));
        const displayMin = parseFloat(svg.getAttribute('data-display-min'));
        const displayMax = parseFloat(svg.getAttribute('data-display-max'));

        // Add bar hover effects
        const bars = svg.querySelectorAll('.chart-bar');
        bars.forEach(bar => {
            bar.addEventListener('mouseenter', (e) => {
                const priceMin = parseFloat(e.target.getAttribute('data-price-min'));
                const priceMax = parseFloat(e.target.getAttribute('data-price-max'));
                const amount = parseInt(e.target.getAttribute('data-amount'));

                // Show tooltip for individual bar
                e.target.setAttribute('opacity', '1');
                e.target.style.cursor = 'pointer';

                // Create temporary tooltip
                const tooltip = document.createElement('div');
                tooltip.id = 'bar-tooltip';
                tooltip.innerHTML = `$${Math.round(priceMin).toLocaleString()}-$${Math.round(priceMax).toLocaleString()}<br>Quantity: ${amount.toLocaleString()}`;
                tooltip.style.position = 'absolute';
                tooltip.style.background = 'rgba(0,0,0,0.8)';
                tooltip.style.color = 'white';
                tooltip.style.padding = '4px 8px';
                tooltip.style.borderRadius = '4px';
                tooltip.style.fontSize = '11px';
                tooltip.style.pointerEvents = 'none';
                tooltip.style.zIndex = '1000001';

                const rect = e.target.getBoundingClientRect();
                tooltip.style.left = (rect.left + rect.width / 2 + window.pageXOffset) + 'px';
                tooltip.style.top = (rect.top - 40 + window.pageYOffset) + 'px';
                tooltip.style.transform = 'translateX(-50%)';

                document.body.appendChild(tooltip);
            });

            bar.addEventListener('mouseleave', (e) => {
                e.target.setAttribute('opacity', '0.7');
                const tooltip = document.getElementById('bar-tooltip');
                if (tooltip) tooltip.remove();
            });
        });

        // Selection functionality
        svg.addEventListener('mousedown', (e) => {
            const rect = svg.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;

            // Only start selection if within chart area
            if (x >= padding && x <= padding + chartWidth && y >= padding && y <= padding + chartHeight) {
                isSelecting = true;
                startX = x;
                currentX = x;

                selectionRect.style.display = 'block';
                selectionRect.setAttribute('x', startX);
                selectionRect.setAttribute('y', padding);
                selectionRect.setAttribute('width', 0);
                selectionRect.setAttribute('height', chartHeight);

                e.preventDefault();
            }
        });

        svg.addEventListener('mousemove', (e) => {
            if (!isSelecting) return;

            const rect = svg.getBoundingClientRect();
            currentX = Math.max(padding, Math.min(padding + chartWidth, e.clientX - rect.left));

            const width = Math.abs(currentX - startX);
            const x = Math.min(startX, currentX);

            selectionRect.setAttribute('x', x);
            selectionRect.setAttribute('width', width);
        });

        svg.addEventListener('mouseup', (e) => {
            if (!isSelecting) return;

            isSelecting = false;
            selectionRect.style.display = 'none';

            // Calculate price range for zoom
            const minX = Math.min(startX, currentX);
            const maxX = Math.max(startX, currentX);

            // Only zoom if selection is wide enough
            if (maxX - minX > 10) {
                const priceRange = displayMax - displayMin;
                const zoomMin = displayMin + ((minX - padding) / chartWidth) * priceRange;
                const zoomMax = displayMin + ((maxX - padding) / chartWidth) * priceRange;

                // Update popup with zoomed chart (keep existing popup)
                updatePricePopup(listings, itemName, zoomMin, zoomMax);
            }
        });

        // Reset zoom functionality
        if (resetZoomBtn) {
            resetZoomBtn.addEventListener('click', () => {
                // Reset zoom (keep existing popup)
                updatePricePopup(listings, itemName);
            });
        }

        // Prevent selection outside chart area
        document.addEventListener('mouseup', () => {
            if (isSelecting) {
                isSelecting = false;
                selectionRect.style.display = 'none';
            }
        });
    }

    // Hide price popup
    function hidePricePopup() {
        const existingPopup = document.getElementById('price-popup');
        if (existingPopup) {
            existingPopup.remove();
        }
    }

    // Clear inventory data
    function clearInventoryData() {
        scannedItems.clear();
        localStorage.removeItem(STORAGE_KEY);
        updateTableDisplay();
        log('Inventory data cleared');
    }

    // Clear all price targets
    function clearAllPriceTargets() {
        // Get all localStorage keys that start with our price target prefix
        const keysToRemove = [];
        for (let i = 0; i < localStorage.length; i++) {
            const key = localStorage.key(i);
            if (key && key.startsWith('scrap2mark_price_targets_')) {
                keysToRemove.push(key);
            }
        }

        // Remove all price target keys
        keysToRemove.forEach(key => localStorage.removeItem(key));

        // Update the display to reflect changes
        updateTableDisplay();
        log(`Cleared ${keysToRemove.length} price targets`);
    }

    // Export inventory data
    // Get selected price targets for an item
    function getSelectedPriceTargets(itemId) {
        const storageKey = `scrap2mark_price_targets_${itemId}`;
        const stored = localStorage.getItem(storageKey);

        if (stored) {
            try {
                const parsed = JSON.parse(stored);
                return Object.entries(parsed).map(([price, data]) => ({
                    price: parseFloat(price),
                    seller: data.seller,
                    amount: data.amount
                }));
            } catch (e) {
                return [];
            }
        }
        return [];
    }

    // Export inventory data with enhanced information
    function exportInventoryData() {
        const data = Array.from(scannedItems.values()).map(item => {
            const itemData = {
                id: item.id,
                name: item.name,
                category: item.category,
                quantity: item.quantity,
                equipped: item.equipped,
                armoryId: item.armoryId,
                tradeable: item.tradeable
            };

            // Add market data if available
            const marketInfo = marketData.get(item.id.toString());
            if (marketInfo) {
                const stats = calculatePriceStats(marketInfo.listings);
                if (stats) {
                    // Calculate the low-end price used in the Price column
                    const lowPrice = Math.min(stats.p25, stats.min * 1.2);
                    const displayPrice = Math.round(lowPrice);

                    // Calculate estimated value
                    const estimatedValue = calculateEstimatedValue(item.id, item.quantity);

                    itemData.marketData = {
                        displayPrice: displayPrice,  // The low-end price shown in Price column
                        averagePrice: Math.round(stats.average),
                        minPrice: stats.min,
                        maxPrice: stats.max,
                        medianPrice: stats.median,
                        q1Price: stats.p25,
                        q3Price: stats.p75,
                        estimatedTotalValue: estimatedValue,
                        totalListings: stats.totalListings,
                        filteredListings: stats.listings,
                        uniqueSellers: stats.uniqueSellers,
                        totalUniqueSellers: stats.totalUniqueSellers,
                        outliersFiltered: stats.outliersFiltered,
                        sellersFiltered: stats.sellersFiltered,
                        priceCapFiltered: stats.priceCapFiltered,
                        statFiltered: stats.statFiltered,
                        totalQuantity: stats.totalQuantity,
                        fetchedAt: new Date(marketInfo.timestamp).toISOString(),
                        ageInMinutes: Math.round((Date.now() - marketInfo.timestamp) / (1000 * 60))
                    };
                }
            }

            // Add selected price targets if any
            const selectedTargets = getSelectedPriceTargets(item.id);
            if (selectedTargets.length > 0) {
                itemData.selectedPriceTargets = selectedTargets;

                // Calculate total value of selected targets
                const targetValue = selectedTargets.reduce((sum, target) =>
                    sum + (target.price * target.amount), 0);
                itemData.selectedTargetsTotalValue = targetValue;

                // Calculate how many of this item's quantity could be sold at target prices
                const targetQuantity = selectedTargets.reduce((sum, target) =>
                    sum + target.amount, 0);
                itemData.selectedTargetsQuantity = targetQuantity;

                if (targetQuantity > 0) {
                    itemData.averageTargetPrice = Math.round(targetValue / targetQuantity);
                }
            }

            return itemData;
        });

        // Save to localStorage
        localStorage.setItem(STORAGE_KEY, JSON.stringify(data));

        // Create export summary
        const totalItems = data.length;
        const itemsWithMarketData = data.filter(item => item.marketData).length;
        const itemsWithTargets = data.filter(item => item.selectedPriceTargets).length;
        const totalEstimatedValue = data.reduce((sum, item) => sum + (item.marketData?.estimatedTotalValue || 0), 0);
        const totalTargetValue = data.reduce((sum, item) => sum + (item.selectedTargetsTotalValue || 0), 0);

        // Add summary to export data
        const exportData = {
            exportInfo: {
                exportedAt: new Date().toISOString(),
                scriptVersion: "Scrap2Mark v2.0",
                totalItems: totalItems,
                itemsWithMarketData: itemsWithMarketData,
                itemsWithPriceTargets: itemsWithTargets,
                totalEstimatedValue: totalEstimatedValue,
                totalSelectedTargetValue: totalTargetValue,
                priceCalculationMethod: "Low-end pricing (min of P25 or 20% above minimum)"
            },
            items: data
        };

        // Create downloadable file
        const dataStr = JSON.stringify(exportData, null, 2);
        const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);

        const exportFileDefaultName = `scrap2mark_export_${new Date().toISOString().split('T')[0]}_${totalItems}items.json`;

        const linkElement = document.createElement('a');
        linkElement.setAttribute('href', dataUri);
        linkElement.setAttribute('download', exportFileDefaultName);
        linkElement.click();

        log(`Exported ${totalItems} items (${itemsWithMarketData} with market data, ${itemsWithTargets} with price targets) to ${exportFileDefaultName}. Total estimated value: $${totalEstimatedValue.toLocaleString()}`);
    }

    // Main scanning function (now only scans visible items)
    async function startInventoryScan() {
        if (isScanning) {
            log('Scan already in progress');
            return;
        }

        isScanning = true;

        const startBtn = document.getElementById('start-scan');
        const statusDiv = document.getElementById('scan-status');

        if (startBtn) startBtn.disabled = true;
        if (statusDiv) statusDiv.textContent = 'Scanning visible items...';

        try {
            log('Starting inventory scan of visible items');

            // Wait for items to be loaded in current category
            await waitForElement('#all-items li[data-item], li[data-item]', 5000);

            // Scan currently visible items only
            const newItemsFound = checkForNewItems();
            updateTableDisplay();

            // Save scanned items after scanning
            saveScannedItems();

            log(`Scan completed. Found ${newItemsFound} new items (Total: ${scannedItems.size})`);
            if (statusDiv) {
                // Show scan result temporarily, then update with full breakdown
                statusDiv.textContent = `Found ${newItemsFound} new items. Total: ${scannedItems.size} items`;
                setTimeout(() => {
                    updateTableDisplay(); // This will restore the detailed breakdown
                }, 2000);
            }

        } catch (error) {
            log(`Scan failed: ${error.message}`);
            if (statusDiv) statusDiv.textContent = `Scan failed: ${error.message}`;
        } finally {
            isScanning = false;
            if (startBtn) startBtn.disabled = false;
        }
    }

    // Load previously saved data
    function loadSavedData() {
        try {
            const savedData = localStorage.getItem(STORAGE_KEY);
            if (savedData) {
                const items = JSON.parse(savedData);
                items.forEach(item => {
                    const key = `${item.id}_${item.armoryId || 'base'}`;
                    scannedItems.set(key, item);
                });
                log(`Loaded ${items.length} saved items`);

                // Update table display after loading data
                setTimeout(() => {
                    updateTableDisplay();
                }, 100);
            } else {
                log('No saved data found');
            }
        } catch (error) {
            log(`Failed to load saved data: ${error.message}`);
        }
    }

    // Initialize script
    function init() {
        log('Initializing Scrap2Mark');

        // Check if we're on a supported page
        const isInventoryPage = window.location.href.includes('/item.php');
        const isMarketPage = window.location.href.includes('/page.php?sid=ItemMarket');

        if (!isInventoryPage && !isMarketPage) {
            log('Not on supported page, script will not run');
            return;
        }

        // Load API key and items data
        loadApiKey();
        loadApiItemsData();
        loadMarketData();

        // Load saved data and ignored items
        loadSavedData();
        loadIgnoredItems();

        // Update page-specific functionality
        if (isInventoryPage) {
            log('Running on inventory page - full scanning functionality available');
        } else if (isMarketPage) {
            log('Running on market page - posting functionality available');
            // Hide inventory-specific controls on market page
            setTimeout(() => {
                const scanControls = document.getElementById('scan-controls');
                if (scanControls) {
                    // Hide scan button but keep other controls
                    const startScanBtn = document.getElementById('start-scan');
                    if (startScanBtn) {
                        startScanBtn.style.display = 'none';
                    }

                    // Update the manual instruction text
                    const manualText = floatingTable.querySelector('div[style*="font-size: 11px"]');
                    if (manualText && manualText.textContent.includes('Scroll down')) {
                        manualText.textContent = 'Market page: Use data from inventory scanning and price targets to post items.';
                    }
                }
            }, 1000);
        }

        // Create floating table (always available for data management)
        createFloatingTable();

        // Update API status display
        updateApiStatusDisplay();

        // Update button visibility
        updateApiButtonVisibility();

        // Add CSS for better styling
        const style = document.createElement('style');
        style.textContent = `
            #inventory-scanner-table button:hover {
                opacity: 0.8;
            }
            #inventory-scanner-table table th {
                position: sticky;
                top: 0;
                background: #f8f9fa;
            }
            #inventory-scanner-table table tr:hover {
                background-color: #f8f9fa;
            }
            #api-key-input {
                font-family: monospace;
            }
            #inventory-scanner-table {
                resize: both;
                overflow: hidden;
            }
            #inventory-scanner-table table {
                table-layout: fixed;
                width: 100%;
            }
            #inventory-scanner-table table td {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }
            #inventory-scanner-table table td:nth-child(2) {
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
            .price-chart-hover {
                transition: transform 0.2s ease;
            }
            .price-chart-hover:hover {
                transform: scale(1.1);
            }
            #price-popup {
                background: white;
                border-radius: 8px;
                box-shadow: 0 4px 20px rgba(0,0,0,0.15);
                border: 1px solid #ddd;
                animation: fadeIn 0.2s ease-out;
            }
            @keyframes fadeIn {
                from { opacity: 0; transform: scale(0.9); }
                to { opacity: 1; transform: scale(1); }
            }
            .ignore-btn {
                font-size: 10px;
                padding: 2px 6px;
                border: 1px solid #ccc;
                border-radius: 3px;
                cursor: pointer;
                transition: all 0.2s;
                font-weight: bold;
                min-width: 60px;
            }
            .ignore-btn.ignore {
                background: #6c757d;
                color: white;
                border-color: #6c757d;
            }
            .ignore-btn.ignore:hover {
                background: #5a6268;
                border-color: #545b62;
                transform: scale(1.05);
            }
            .ignore-btn.unignore {
                background: #dc3545;
                color: white;
                border-color: #dc3545;
                animation: pulse 2s infinite;
            }
            .ignore-btn.unignore:hover {
                background: #c82333;
                border-color: #bd2130;
                transform: scale(1.05);
                animation: none;
            }
            @keyframes pulse {
                0% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.4); }
                70% { box-shadow: 0 0 0 6px rgba(220, 53, 69, 0); }
                100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); }
            }
        `;
        document.head.appendChild(style);

        log('Torn Inventory Scanner initialized');
    }

    // Show toast notification
    function showToast(message, type = 'info') {
        const toast = document.createElement('div');
        const colors = {
            success: '#28a745',
            error: '#dc3545',
            warning: '#ffc107',
            info: '#17a2b8'
        };

        toast.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${colors[type] || colors.info};
            color: ${type === 'warning' ? '#212529' : 'white'};
            padding: 12px 16px;
            border-radius: 5px;
            z-index: 100002;
            font-size: 14px;
            font-weight: bold;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            max-width: 400px;
            word-wrap: break-word;
            animation: slideIn 0.3s ease-out;
        `;
        toast.textContent = message;

        // Add animation keyframes if not already added
        if (!document.querySelector('#toast-animations')) {
            const animationStyle = document.createElement('style');
            animationStyle.id = 'toast-animations';
            animationStyle.textContent = `
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
                @keyframes slideOut {
                    from { transform: translateX(0); opacity: 1; }
                    to { transform: translateX(100%); opacity: 0; }
                }
            `;
            document.head.appendChild(animationStyle);
        }

        document.body.appendChild(toast);

        // Remove toast after 4 seconds
        setTimeout(() => {
            toast.style.animation = 'slideOut 0.3s ease-out';
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 300);
        }, 4000);
    }

    // Load all price targets by item name
    function loadAllPriceTargets() {
        const allTargets = {};

        // Go through all scanned items and check for saved price targets
        for (const [key, item] of scannedItems) {
            // Extract item ID from the key (format: "itemId_armoryId")
            const itemId = key.split('_')[0];
            const storageKey = `scrap2mark_price_targets_${itemId}`;
            const stored = localStorage.getItem(storageKey);

            if (stored) {
                try {
                    const parsed = JSON.parse(stored);
                    const targets = new Map(Object.entries(parsed).map(([k, v]) => [parseFloat(k), v]));

                    // Find the selected price target
                    for (const [price, isSelected] of targets) {
                        if (isSelected) {
                            allTargets[item.name] = {
                                selectedPrice: price,
                                itemId: itemId
                            };
                            break;
                        }
                    }
                } catch (e) {
                    console.log(`Error loading price targets for ${item.name}:`, e);
                }
            }
        }

        console.log('Debug: loadAllPriceTargets result:', allTargets);
        return allTargets;
    }

    // Get filtered items based on current filter settings
    function getFilteredItems() {
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');

        const showNonTradeable = showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false;
        const showIgnored = showIgnoredCheckbox ? showIgnoredCheckbox.checked : false;
        const hideLowValue = hideLowValueCheckbox ? hideLowValueCheckbox.checked : false;
        const minValue = minValueInput ? parseFloat(minValueInput.value.replace(/[,$]/g, '')) || 0 : 0;

        // Get all items and apply filters
        const allItems = Array.from(scannedItems.values());

        return allItems.filter(item => {
            const itemId = item.id || 'N/A';
            const itemTradeable = item.tradeable;
            const itemIgnored = isItemIgnored(itemId);

            // Filter non-tradeable items
            if (!showNonTradeable && itemTradeable === false) {
                return false;
            }

            // Filter ignored items
            if (!showIgnored && itemIgnored) {
                return false;
            }

            // Filter low value items
            if (hideLowValue && itemTradeable === true) {
                const estimatedValue = calculateEstimatedValue(itemId, item.quantity);
                if (estimatedValue < minValue) {
                    return false;
                }
            }

            return true;
        });
    }

    // Market posting functionality
    async function handlePostToMarket() {
        const filteredItems = getFilteredItems();
        const priceTargets = loadAllPriceTargets();

        // Debug logging
        console.log('Debug: Filtered items count:', filteredItems.length);
        console.log('Debug: Price targets:', priceTargets);
        console.log('Debug: Sample filtered items:', filteredItems.slice(0, 3).map(item => ({
            name: item.name,
            id: item.id
        })));

        const targetedItems = filteredItems.filter(item => {
            const hasTarget = priceTargets[item.name] && priceTargets[item.name].selectedPrice;
            if (!hasTarget) {
                console.log(`Debug: Item "${item.name}" - has target: ${!!priceTargets[item.name]}, has selected price: ${!!(priceTargets[item.name] && priceTargets[item.name].selectedPrice)}`);
            }
            return hasTarget;
        });

        if (targetedItems.length === 0) {
            const totalItems = filteredItems.length;
            const priceTargetCount = Object.keys(priceTargets).length;
            console.log('Debug: All price target keys:', Object.keys(priceTargets));
            console.log('Debug: All filtered item names:', filteredItems.slice(0, 10).map(item => item.name));
            showToast(`No items ready for posting. Found ${totalItems} filtered items, ${priceTargetCount} with price targets, but none match both criteria.`, 'warning');
            return;
        }

        // Check if we're on the market page
        if (!window.location.href.includes('/page.php?sid=ItemMarket')) {
            const result = confirm(`You need to be on the Item Market page to post items. Would you like to navigate there now?\n\nFound ${targetedItems.length} items ready to post.`);
            if (result) {
                window.location.href = '/page.php?sid=ItemMarket#/addListing';
            }
            return;
        }

        showPostToMarketDialog(targetedItems);
    }

    function showPostToMarketDialog(items) {
        // Create modal dialog
        const modalOverlay = document.createElement('div');
        modalOverlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 10001;
            display: flex;
            align-items: center;
            justify-content: center;
        `;

        const modal = document.createElement('div');
        modal.style.cssText = `
            background: #2b2b2b;
            border-radius: 8px;
            max-width: 800px;
            max-height: 80vh;
            overflow-y: auto;
            padding: 20px;
            color: #e0e0e0;
            border: 1px solid #555;
        `;

        const priceTargets = loadAllPriceTargets();

        modal.innerHTML = `
            <h3 style="margin-top: 0; color: #17a2b8;">Fill Market Posting Forms</h3>
            <p>This will automatically fill the quantity and price fields for ${items.length} items with price targets.</p>
            <p style="font-size: 12px; color: #ffc107; background: #333; padding: 8px; border-radius: 4px; margin: 10px 0;">
                ⚠️ <strong>Note:</strong> This tool fills the forms but does NOT automatically submit them.
                You will need to manually review and submit each listing for compliance with Torn.com's terms.
            </p>

            <div style="max-height: 400px; overflow-y: auto; margin: 20px 0;">
                ${items.map(item => {
                    const target = priceTargets[item.name];
                    const itemQty = item.quantity || item.qty || 1;
                    return `
                        <div style="display: flex; align-items: center; padding: 10px; border: 1px solid #444; margin-bottom: 5px; border-radius: 4px;">
                            <div style="flex: 1;">
                                <strong>${item.name}</strong><br>
                                <small>Available: ${itemQty} | Target Price: $${target.selectedPrice?.toLocaleString()}</small>
                            </div>
                            <div style="margin-left: 10px; min-width: 140px;">
                                <label style="font-size: 12px; display: block; margin-bottom: 5px;">Quantity to post:</label>
                                <div style="display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: 5px;">
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="1" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+1</button>
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="10" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+10</button>
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="100" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+100</button>
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="1000" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+1k</button>
                                    <button class="qty-btn" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="${itemQty}" style="padding: 4px 8px; font-size: 11px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;">Max</button>
                                    <button class="qty-btn-reset" data-item="${item.name.replace(/"/g, '&quot;')}" style="padding: 4px 8px; font-size: 11px; background: #dc3545; color: white; border: none; border-radius: 3px; cursor: pointer;">✗</button>
                                </div>
                                <input type="number" id="qty-${item.name.replace(/"/g, '&quot;').replace(/[^a-zA-Z0-9]/g, '_')}" min="0" max="${itemQty}" value="0" style="width: 100%; padding: 4px; background: #444; color: #e0e0e0; border: 1px solid #666; border-radius: 3px; font-size: 12px;">
                            </div>
                        </div>
                    `;
                }).join('')}
            </div>

            <div style="text-align: center; margin-top: 20px;">
                <button id="confirm-post" style="background: #28a745; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Fill Forms</button>
                <button id="cancel-post" style="background: #6c757d; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;">Cancel</button>
            </div>
        `;

        modalOverlay.appendChild(modal);
        document.body.appendChild(modalOverlay);

        // Add event listeners for quantity buttons
        modal.querySelectorAll('.qty-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                const itemName = btn.dataset.item;
                const qty = parseInt(btn.dataset.qty);
                const sanitizedName = itemName.replace(/[^a-zA-Z0-9]/g, '_');
                const input = document.getElementById(`qty-${sanitizedName}`);
                if (input) {
                    input.value = qty;
                    // Add visual feedback
                    input.style.background = '#28a745';
                    setTimeout(() => {
                        input.style.background = '#444';
                    }, 200);
                } else {
                    console.warn(`Could not find input for item: ${itemName} (ID: qty-${sanitizedName})`);
                }
            });
        });

        // Add event listeners for quantity increment buttons
        modal.querySelectorAll('.qty-btn-add').forEach(btn => {
            btn.addEventListener('click', () => {
                const itemName = btn.dataset.item;
                const addQty = parseInt(btn.dataset.qty);
                const sanitizedName = itemName.replace(/[^a-zA-Z0-9]/g, '_');
                const input = document.getElementById(`qty-${sanitizedName}`);
                if (input) {
                    const currentQty = parseInt(input.value) || 0;
                    const maxQty = parseInt(input.max);
                    const newQty = Math.min(currentQty + addQty, maxQty);
                    input.value = newQty;
                    // Add visual feedback
                    input.style.background = '#007bff';
                    setTimeout(() => {
                        input.style.background = '#444';
                    }, 200);
                } else {
                    console.warn(`Could not find input for item: ${itemName} (ID: qty-${sanitizedName})`);
                }
            });
        });

        // Add event listeners for quantity reset buttons
        modal.querySelectorAll('.qty-btn-reset').forEach(btn => {
            btn.addEventListener('click', () => {
                const itemName = btn.dataset.item;
                const sanitizedName = itemName.replace(/[^a-zA-Z0-9]/g, '_');
                const input = document.getElementById(`qty-${sanitizedName}`);
                if (input) {
                    input.value = 0;
                    // Add visual feedback
                    input.style.background = '#dc3545';
                    setTimeout(() => {
                        input.style.background = '#444';
                    }, 200);
                } else {
                    console.warn(`Could not find input for item: ${itemName} (ID: qty-${sanitizedName})`);
                }
            });
        });

        // Handle confirm posting
        document.getElementById('confirm-post').addEventListener('click', () => {
            const postingData = items.map(item => {
                const sanitizedName = item.name.replace(/[^a-zA-Z0-9]/g, '_');
                const qtyInput = document.getElementById(`qty-${sanitizedName}`);
                const quantity = qtyInput ? parseInt(qtyInput.value) : 1;

                return {
                    name: item.name,
                    itemId: item.id,
                    quantity: quantity,
                    price: priceTargets[item.name].selectedPrice
                };
            }).filter(data => data.quantity > 0);

            modalOverlay.remove();
            executeMarketPosting(postingData);
        });

        document.getElementById('cancel-post').addEventListener('click', () => {
            modalOverlay.remove();
        });

        // Close on overlay click
        modalOverlay.addEventListener('click', (e) => {
            if (e.target === modalOverlay) {
                modalOverlay.remove();
            }
        });
    }

    async function executeMarketPosting(postingData) {
        if (postingData.length === 0) {
            showToast('No items to post', 'warning');
            return;
        }

        showToast(`Starting to fill forms for ${postingData.length} items...`, 'info');

        let successCount = 0;
        let errorCount = 0;

        for (const [index, item] of postingData.entries()) {
            try {
                showToast(`Filling form for ${item.name} (${index + 1}/${postingData.length})...`, 'info');

                const success = await postSingleItem(item);

                if (success) {
                    successCount++;
                } else {
                    errorCount++;
                    showToast(`✗ Failed to fill form for ${item.name}`, 'error');
                }

                // Wait between posts to avoid overwhelming the interface
                if (index < postingData.length - 1) {
                    await new Promise(resolve => setTimeout(resolve, 1500));
                }

            } catch (error) {
                errorCount++;
                console.error(`Error filling form for ${item.name}:`, error);
                showToast(`✗ Error filling form for ${item.name}: ${error.message}`, 'error');
            }
        }

        // Final summary with instructions
        const summary = `Form filling complete! Filled: ${successCount}, Errors: ${errorCount}`;
        showToast(summary, successCount > 0 ? 'success' : 'error');

        if (successCount > 0) {
            // Show manual submission instruction
            setTimeout(() => {
                showToast('📝 Forms filled! Please review and manually submit each listing.', 'info');
            }, 2000);
        }
    }

    async function postSingleItem(item) {
        // Wait for the page to be ready
        await waitForMarketPageReady();

        // Find the item row in the market interface
        const itemRow = findMarketItemRow(item.itemId, item.name);
        if (!itemRow) {
            throw new Error(`Could not find item ${item.name} in market interface`);
        }

        // Fill in quantity using the visible input (not the hidden one)
        const qtyInputs = itemRow.querySelectorAll('input[placeholder="Qty"]');
        const visibleQtyInput = Array.from(qtyInputs).find(input => input.type !== 'hidden');
        if (visibleQtyInput) {
            // Clear the field first
            visibleQtyInput.value = '';
            visibleQtyInput.focus();

            // Set the value and trigger events
            visibleQtyInput.value = item.quantity;
            visibleQtyInput.dispatchEvent(new Event('input', { bubbles: true }));
            visibleQtyInput.dispatchEvent(new Event('change', { bubbles: true }));

            // Simulate typing for better compatibility
            const inputEvent = new InputEvent('input', {
                bubbles: true,
                cancelable: true,
                data: item.quantity.toString()
            });
            visibleQtyInput.dispatchEvent(inputEvent);
        }

        // Fill in price using the visible input (not the hidden one)
        const priceInputs = itemRow.querySelectorAll('input[placeholder="Price"]');
        const visiblePriceInput = Array.from(priceInputs).find(input => input.type !== 'hidden');
        if (visiblePriceInput) {
            // Clear the field first
            visiblePriceInput.value = '';
            visiblePriceInput.focus();

            // Set the value and trigger events
            visiblePriceInput.value = item.price;
            visiblePriceInput.dispatchEvent(new Event('input', { bubbles: true }));
            visiblePriceInput.dispatchEvent(new Event('change', { bubbles: true }));

            // Simulate typing for better compatibility
            const inputEvent = new InputEvent('input', {
                bubbles: true,
                cancelable: true,
                data: item.price.toString()
            });
            visiblePriceInput.dispatchEvent(inputEvent);
        }

        // Show a confirmation message
        showToast(`✓ Filled ${item.name}: Qty ${item.quantity}, Price $${item.price.toLocaleString()}`, 'success');

        // Note: Manual submission required - the script fills the forms but doesn't auto-submit
        // This is safer and follows Torn.com's terms of service better

        return true;
    }

    async function waitForMarketPageReady() {
        return new Promise((resolve) => {
            const checkReady = () => {
                const itemRows = document.querySelectorAll('.itemRowWrapper___cFs4O');
                if (itemRows.length > 0) {
                    resolve();
                } else {
                    setTimeout(checkReady, 500);
                }
            };
            checkReady();
        });
    }

    function findMarketItemRow(itemId, itemName) {
        // Look for the item row by item name or ID
        const itemRows = document.querySelectorAll('.itemRowWrapper___cFs4O, .itemRow___Mf7bO');

        for (const row of itemRows) {
            // Check for item name in various elements
            const viewButton = row.querySelector('.viewInfoButton___jOjRg, button[aria-label*="View"]');
            if (viewButton) {
                const ariaLabel = viewButton.getAttribute('aria-label');
                if (ariaLabel) {
                    // Try exact match first
                    if (ariaLabel.includes(itemName)) {
                        return row.closest('.itemRowWrapper___cFs4O') || row;
                    }

                    // Try partial match for items with long names
                    const cleanItemName = itemName.replace(/[^\w\s]/g, '').toLowerCase();
                    const cleanAriaLabel = ariaLabel.replace(/[^\w\s]/g, '').toLowerCase();
                    if (cleanAriaLabel.includes(cleanItemName) || cleanItemName.includes(cleanAriaLabel.split(' ')[0])) {
                        return row.closest('.itemRowWrapper___cFs4O') || row;
                    }
                }
            }

            // Alternative: check for item ID in controls attribute
            const controls = viewButton?.getAttribute('aria-controls');
            if (controls && controls.includes(`itemInfo-${itemId}-`)) {
                return row.closest('.itemRowWrapper___cFs4O') || row;
            }

            // Check for item name in text content as fallback
            const titleElement = row.querySelector('.title___Xo6Pm, .itemName, [class*="title"]');
            if (titleElement && titleElement.textContent.includes(itemName)) {
                return row.closest('.itemRowWrapper___cFs4O') || row;
            }
        }

        // If no exact match, try a broader search by scanning all text content
        for (const row of itemRows) {
            if (row.textContent.includes(itemName)) {
                // Double-check this isn't a false positive by looking for input fields
                const hasInputs = row.querySelectorAll('input[placeholder="Qty"], input[placeholder="Price"]').length >= 2;
                if (hasInputs) {
                    return row.closest('.itemRowWrapper___cFs4O') || row;
                }
            }
        }

        return null;
    }

    // Update API status display
    function updateApiStatusDisplay() {
        const apiStatus = document.getElementById('api-status');

        if (!apiStatus) return;

        if (apiKey) {
            apiStatus.textContent = 'API key loaded';
            apiStatus.style.color = '#28a745';
        } else {
            apiStatus.textContent = 'No API key';
            apiStatus.style.color = '#dc3545';
        }

        if (apiItemsData) {
            const itemCount = Object.keys(apiItemsData).length;
            apiStatus.textContent += ` | ${itemCount} items cached`;
        }
    }

    // Wait for page to be ready and initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();