/**
 * Escapes HTML special characters to prevent XSS attacks.
 * @param {string} str - The string to escape.
 * @returns {string} - The escaped string.
 */
function escapeHtml(str) {
    if (str == null) return '';
    return String(str)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
}

/**
 * Escapes a string for safe embedding inside a single-quoted JS string literal.
 * @param {string} str - Raw string value.
 * @returns {string} - JS-escaped string.
 */
function escapeJsString(str) {
    if (str == null) return '';
    return String(str)
        .replace(/\\/g, '\\\\')
        .replace(/'/g, "\\'")
        .replace(/\r/g, '\\r')
        .replace(/\n/g, '\\n');
}

class StratUIManager {
    constructor(workspaceManager) {
        this.workspaceManager = workspaceManager;
        this.targetEl = null;
        this.formElement = null;
    }

    /**
     * Initializes the UI elements with provided IDs.
     * @param {string} targetId - The ID of the HTML element where strategies will be displayed.
     * @param {string} formElId - The ID of the HTML element for the strategy creation form.
     */
    initUI(targetId, formElId) {
        // Get the target element for displaying strategies
        this.targetEl = document.getElementById(targetId);
        if (!this.targetEl) {
            throw new Error(`Element for displaying strategies "${targetId}" not found.`);
        }

        // Get the form element for strategy creation
        this.formElement = document.getElementById(formElId);
        if (!this.formElement) {
            throw new Error(`Strategies form element "${formElId}" not found.`);
        }
    }

    /**
     * Displays the form for creating or editing a strategy.
     * @param {string} action - The action to perform ('new' or 'edit').
     * @param {object|null} strategyData - The data of the strategy to edit (only applicable for 'edit' action).
     */
    async displayForm(action, strategyData = null) {
        console.log(`Opening form for action: ${action}, strategy: ${strategyData?.name}`);
        if (this.formElement) {
            const headerTitle = this.formElement.querySelector("#draggable_header h1");
            const submitCreateBtn = this.formElement.querySelector("#submit-create");
            const submitEditBtn = this.formElement.querySelector("#submit-edit");
            const nameBox = this.formElement.querySelector('#name_box');
            const publicCheckbox = this.formElement.querySelector('#public_checkbox');
            const feeBox = this.formElement.querySelector('#fee_box');
            const exchangeSelect = this.formElement.querySelector('#strategy_exchange');
            const symbolInput = this.formElement.querySelector('#strategy_symbol');
            const timeframeSelect = this.formElement.querySelector('#strategy_timeframe');

            if (!headerTitle || !submitCreateBtn || !submitEditBtn || !nameBox || !publicCheckbox || !feeBox) {
                console.error('One or more form elements were not found.');
                return;
            }

            // Remove any existing warning banner
            const existingWarning = this.formElement.querySelector('.running-strategy-warning');
            if (existingWarning) {
                existingWarning.remove();
            }

            // Remove any existing source change warning
            const existingSourceWarning = this.formElement.querySelector('.source-change-warning');
            if (existingSourceWarning) {
                existingSourceWarning.remove();
            }

            // Track the strategy being edited (for restart prompt after save)
            this._editingStrategyId = null;

            // Get current chart view for default source
            const chartView = window.UI?.data?.getChartView?.() || { exchange: 'binance', market: 'BTC/USDT', timeframe: '5m' };
            console.log('Current chart view for default source:', chartView);

            // Set up exchange change listener to update symbols
            if (exchangeSelect && symbolInput) {
                // Remove old listener if exists
                exchangeSelect.removeEventListener('change', this._onExchangeChange);
                this._onExchangeChange = async () => {
                    const selectedExchange = exchangeSelect.value;
                    await this._populateSymbolDropdown(symbolInput, selectedExchange, null);
                };
                exchangeSelect.addEventListener('change', this._onExchangeChange);
            }

            // Update form based on action
            if (action === 'new') {
                headerTitle.textContent = "Create New Strategy";
                submitCreateBtn.style.display = "inline-block";
                submitEditBtn.style.display = "none";
                nameBox.value = '';
                publicCheckbox.checked = false;
                feeBox.value = 0;

                // Set default source from current chart view
                const defaultExchange = (chartView.exchange || 'binance').toLowerCase();
                if (exchangeSelect) exchangeSelect.value = defaultExchange;
                if (timeframeSelect) timeframeSelect.value = chartView.timeframe || '5m';
                // Populate symbols for the default exchange
                if (symbolInput) {
                    await this._populateSymbolDropdown(symbolInput, defaultExchange, chartView.market || 'BTC/USDT');
                }

            } else if (action === 'edit' && strategyData) {
                headerTitle.textContent = "Edit Strategy";
                submitCreateBtn.style.display = "none";
                submitEditBtn.style.display = "inline-block";
                nameBox.value = strategyData.name;
                publicCheckbox.checked = strategyData.public === 1;
                feeBox.value = strategyData.fee || 0;

                // Store the strategy ID for later use
                this._editingStrategyId = strategyData.tbl_key;

                // Load saved source values (if available) or fall back to chart view
                const savedSource = strategyData.default_source || {};
                const savedExchange = (savedSource.exchange || chartView.exchange || 'binance').toLowerCase();
                const savedSymbol = savedSource.market || savedSource.symbol || chartView.market || 'BTC/USDT';
                const savedTimeframe = savedSource.timeframe || chartView.timeframe || '5m';

                if (exchangeSelect) exchangeSelect.value = savedExchange;
                if (timeframeSelect) timeframeSelect.value = savedTimeframe;
                // Populate symbols for the saved exchange and select the saved symbol
                if (symbolInput) {
                    await this._populateSymbolDropdown(symbolInput, savedExchange, savedSymbol);
                }

                // Show warning if current chart view differs from saved source
                const currentExchange = (chartView.exchange || 'binance').toLowerCase();
                const currentSymbol = chartView.market || 'BTC/USDT';
                const currentTimeframe = chartView.timeframe || '5m';

                if (savedSource.exchange && (
                    savedExchange !== currentExchange ||
                    savedSymbol !== currentSymbol ||
                    savedTimeframe !== currentTimeframe
                )) {
                    const warningBanner = document.createElement('div');
                    warningBanner.className = 'source-change-warning';
                    warningBanner.innerHTML = `
                        <span style="margin-right: 8px;">📊</span>
                        <span>Strategy was saved with <strong>${savedExchange.toUpperCase()} ${savedSymbol} (${savedTimeframe})</strong>.
                        You're viewing <strong>${currentExchange.toUpperCase()} ${currentSymbol} (${currentTimeframe})</strong>.
                        Update below if you want to change the trading source.</span>
                    `;
                    warningBanner.style.cssText = `
                        background: #e7f3ff;
                        border: 1px solid #007bff;
                        border-radius: 5px;
                        padding: 10px;
                        margin: 10px;
                        color: #004085;
                        font-size: 12px;
                        display: flex;
                        align-items: center;
                    `;

                    // Insert after header
                    const header = this.formElement.querySelector('#draggable_header');
                    if (header && header.nextSibling) {
                        header.parentNode.insertBefore(warningBanner, header.nextSibling);
                    }
                }

                // Check if strategy is currently running and show warning
                if (UI.strats && UI.strats.isStrategyRunning(strategyData.tbl_key)) {
                    const runningInfo = UI.strats.getRunningInfo(strategyData.tbl_key);
                    const modeText = runningInfo ? runningInfo.mode : 'unknown';

                    // Create warning banner
                    const warningBanner = document.createElement('div');
                    warningBanner.className = 'running-strategy-warning';
                    warningBanner.innerHTML = `
                        <span style="margin-right: 8px;">⚠️</span>
                        <span>This strategy is currently running in <strong>${modeText}</strong> mode.
                        Changes will not take effect until you restart it.</span>
                    `;
                    warningBanner.style.cssText = `
                        background: #fff3cd;
                        border: 1px solid #ffc107;
                        border-radius: 5px;
                        padding: 10px;
                        margin: 10px;
                        color: #856404;
                        font-size: 13px;
                        display: flex;
                        align-items: center;
                    `;

                    // Insert after header
                    const header = this.formElement.querySelector('#draggable_header');
                    if (header && header.nextSibling) {
                        header.parentNode.insertBefore(warningBanner, header.nextSibling);
                    }
                }
            }

            // Display the form
            this.formElement.style.display = "grid";

            // Initialize Blockly workspace after the form becomes visible
            if (UI.strats && this.workspaceManager) {
                try {
                    await this.workspaceManager.initWorkspace();
                    console.log("Blockly workspace initialized.");
                    // Restore workspace from XML if editing
                    if (action === 'edit' && strategyData && strategyData.workspace) {
                        this.workspaceManager.loadWorkspaceFromXml(strategyData.workspace);
                        console.log("Workspace restored from XML.");
                    }
                } catch (error) {
                    console.error("Failed to initialize Blockly workspace:", error);
                }
            } else {
                console.error("Workspace manager is not initialized or is unavailable.");
            }
        } else {
            console.error(`Form element "${this.formElement.id}" not found.`);
        }
    }

    /**
     * Hides the "Create New Strategy" form by adding a 'hidden' class.
     */
    hideForm() {
        if (this.formElement) {
            this.formElement.style.display = 'none'; // Hide the form
        }
    }

    /**
     * Populates the symbol dropdown with symbols for the given exchange.
     * Fetches from EDM API, falls back to common symbols on error.
     * @param {HTMLSelectElement} selectElement - The symbol dropdown element.
     * @param {string} exchange - The exchange name.
     * @param {string|null} selectedSymbol - The symbol to select after populating.
     */
    async _populateSymbolDropdown(selectElement, exchange, selectedSymbol) {
        if (!selectElement) return;

        // Popular base currencies to prioritize (most traded first)
        const popularBases = ['BTC', 'ETH', 'SOL', 'XRP', 'ADA', 'DOGE', 'AVAX', 'DOT', 'MATIC', 'LINK',
                             'LTC', 'UNI', 'ATOM', 'XLM', 'ALGO', 'FIL', 'NEAR', 'APT', 'ARB', 'OP'];

        // Common symbols as fallback
        const commonSymbols = ['BTC/USDT', 'ETH/USDT', 'BTC/USD', 'ETH/USD', 'SOL/USDT', 'XRP/USDT', 'ADA/USDT', 'DOGE/USDT'];

        let symbols = [];
        let allExchangeSymbols = [];

        // Try to fetch symbols from EDM
        try {
            const edm_url = window.bt_data?.edm_url || 'http://localhost:8080';
            const response = await fetch(`${edm_url}/exchanges/${exchange.toLowerCase()}/symbols`);
            if (response.ok) {
                const data = await response.json();
                if (data.symbols && data.symbols.length > 0) {
                    allExchangeSymbols = data.symbols;
                    console.log(`Fetched ${allExchangeSymbols.length} symbols for ${exchange}`);

                    // Prioritize: First add popular USDT pairs in order of popularity
                    for (const base of popularBases) {
                        const usdtPair = `${base}/USDT`;
                        if (allExchangeSymbols.includes(usdtPair) && !symbols.includes(usdtPair)) {
                            symbols.push(usdtPair);
                        }
                    }

                    // Then add popular USD pairs
                    for (const base of popularBases) {
                        const usdPair = `${base}/USD`;
                        if (allExchangeSymbols.includes(usdPair) && !symbols.includes(usdPair)) {
                            symbols.push(usdPair);
                        }
                    }

                    // Then add remaining USDT pairs (sorted)
                    const remainingUsdtPairs = allExchangeSymbols
                        .filter(s => s.endsWith('/USDT') && !symbols.includes(s))
                        .sort();
                    symbols.push(...remainingUsdtPairs);

                    // Then add remaining USD pairs
                    const remainingUsdPairs = allExchangeSymbols
                        .filter(s => s.endsWith('/USD') && !symbols.includes(s))
                        .sort();
                    symbols.push(...remainingUsdPairs);

                    // Finally add other pairs (BTC pairs, etc.)
                    const otherPairs = allExchangeSymbols
                        .filter(s => !symbols.includes(s))
                        .sort();
                    symbols.push(...otherPairs);
                }
            }
        } catch (error) {
            console.warn(`Failed to fetch symbols for ${exchange}, using defaults:`, error);
        }

        // Fall back to common symbols if nothing fetched
        if (symbols.length === 0) {
            symbols = [...commonSymbols];
        }

        // Ensure selected symbol is at the top of the list
        if (selectedSymbol) {
            // Remove it if it exists elsewhere in the list
            symbols = symbols.filter(s => s !== selectedSymbol);
            // Add it to the front
            symbols.unshift(selectedSymbol);
        }

        // Populate the dropdown (no arbitrary limit - show all available)
        selectElement.innerHTML = '';
        for (const symbol of symbols) {
            const option = document.createElement('option');
            option.value = symbol;
            option.textContent = symbol;
            selectElement.appendChild(option);
        }

        // Set the selected value
        if (selectedSymbol) {
            selectElement.value = selectedSymbol;
        }
    }


    /**
     * Updates the HTML representation of the strategies.
     * @param {Object[]} strategies - The list of strategies to display.
     */
    updateStrategiesHtml(strategies) {
        if (this.targetEl) {
            // Clear existing content
            while (this.targetEl.firstChild) {
                this.targetEl.removeChild(this.targetEl.firstChild);
            }

            // Create and append new elements for all strategies
            for (let i = 0; i < strategies.length; i++) {
                const strat = strategies[i];
                console.log(`Processing strategy ${i + 1}/${strategies.length}:`, strat);

                try {
                    const strategyItem = document.createElement('div');
                    strategyItem.className = 'strategy-item';
                    strategyItem.setAttribute('data-strategy-id', strat.tbl_key);

                    // Check if this is a subscribed strategy (not owned)
                    const isSubscribed = strat.is_subscribed && !strat.is_owner;
                    const isOwner = strat.is_owner !== false; // Default to owner if not specified

                    // Check if strategy is running
                    const isRunning = UI.strats && UI.strats.isStrategyRunning(strat.tbl_key);
                    const runningInfo = isRunning ? UI.strats.getRunningInfo(strat.tbl_key) : null;

                    // Add subscribed class if applicable
                    if (isSubscribed) {
                        strategyItem.classList.add('subscribed');
                    }

                    // Delete/Unsubscribe button
                    if (isSubscribed) {
                        // Show unsubscribe button for subscribed strategies
                        const unsubscribeButton = document.createElement('button');
                        unsubscribeButton.className = 'unsubscribe-button';
                        unsubscribeButton.innerHTML = '&#8722;'; // Minus sign
                        unsubscribeButton.title = 'Unsubscribe from strategy';
                        unsubscribeButton.addEventListener('click', (e) => {
                            e.stopPropagation();
                            if (isRunning) {
                                alert('Cannot unsubscribe while strategy is running. Stop it first.');
                                return;
                            }
                            if (UI.strats && UI.strats.unsubscribeFromStrategy) {
                                UI.strats.unsubscribeFromStrategy(strat.tbl_key);
                            }
                        });
                        strategyItem.appendChild(unsubscribeButton);
                    } else {
                        // Delete button for owned strategies
                        const deleteButton = document.createElement('button');
                        deleteButton.className = 'delete-button';
                        deleteButton.innerHTML = '&#10008;';
                        deleteButton.addEventListener('click', (e) => {
                            e.stopPropagation();
                            if (isRunning) {
                                alert('Cannot delete a running strategy. Stop it first.');
                                return;
                            }
                            console.log(`Delete button clicked for strategy: ${strat.name}`);
                            if (this.onDeleteStrategy) {
                                this.onDeleteStrategy(strat.tbl_key);
                            } else {
                                console.error("Delete strategy callback is not set.");
                            }
                        });
                        strategyItem.appendChild(deleteButton);
                    }

                    // Run/Stop button
                    const runButton = document.createElement('button');
                    runButton.className = isRunning ? 'run-button running' : 'run-button';
                    runButton.innerHTML = isRunning ? '&#9632;' : '&#9654;'; // Stop or Play icon
                    runButton.title = isRunning ? `Stop (${runningInfo.mode})` : 'Run strategy';
                    runButton.addEventListener('click', (e) => {
                        e.stopPropagation();
                        if (isRunning) {
                            UI.strats.stopStrategy(strat.tbl_key);
                        } else {
                            // Use runStrategyWithOptions to honor testnet checkbox and show warnings
                            UI.strats.runStrategyWithOptions(strat.tbl_key);
                        }
                    });
                    strategyItem.appendChild(runButton);

                    // Strategy icon
                    const strategyIcon = document.createElement('div');
                    strategyIcon.className = isRunning ? 'strategy-icon running' : 'strategy-icon';
                    if (isSubscribed) {
                        strategyIcon.classList.add('subscribed');
                    }
                    strategyIcon.addEventListener('click', () => {
                        console.log(`Strategy icon clicked for strategy: ${strat.name}`);
                        if (isSubscribed) {
                            // Show info modal for subscribed strategies (can't edit)
                            this.showSubscribedStrategyInfo(strat);
                        } else {
                            // Normal edit behavior for owned strategies
                            this.displayForm('edit', strat).catch(error => {
                                console.error('Error displaying form:', error);
                            });
                        }
                    });

                    // Strategy name
                    const strategyName = document.createElement('div');
                    strategyName.className = 'strategy-name';
                    strategyName.textContent = strat.name || 'Unnamed Strategy';
                    strategyIcon.appendChild(strategyName);

                    // Creator badge for subscribed strategies
                    if (isSubscribed && strat.creator_name) {
                        const creatorBadge = document.createElement('div');
                        creatorBadge.className = 'creator-badge';
                        creatorBadge.textContent = `by @${strat.creator_name}`;
                        strategyIcon.appendChild(creatorBadge);
                    }

                    strategyItem.appendChild(strategyIcon);

                    // Strategy hover details with run controls
                    const strategyHover = document.createElement('div');
                    strategyHover.className = 'strategy-hover';
                    const strategyKey = String(strat.tbl_key || '');
                    const strategyKeyHtml = escapeHtml(strategyKey);
                    const strategyKeyJs = escapeHtml(escapeJsString(strategyKey));

                    // Build hover content (escape user-controlled values)
                    let hoverHtml = `<strong>${escapeHtml(strat.name || 'Unnamed Strategy')}</strong>`;

                    // Show running status if applicable
                    if (isRunning) {
                        let modeDisplay = runningInfo.mode;
                        const safeModeDisplay = escapeHtml(modeDisplay);
                        let modeBadge = '';

                        // Add testnet/production badge for live mode
                        if (runningInfo.mode === 'live') {
                            if (runningInfo.testnet) {
                                modeBadge = '<span style="background: #28a745; color: white; padding: 1px 4px; border-radius: 3px; font-size: 9px; margin-left: 4px;">TESTNET</span>';
                            } else {
                                modeBadge = '<span style="background: #dc3545; color: white; padding: 1px 4px; border-radius: 3px; font-size: 9px; margin-left: 4px;">PRODUCTION</span>';
                            }
                        }

                        let statusHtml = `
                            <div class="strategy-status running">
                                Running in <strong>${safeModeDisplay}</strong> mode ${modeBadge}`;

                        // Show balance if available
                        if (runningInfo.balance !== undefined) {
                            statusHtml += `<br>Balance: $${runningInfo.balance.toFixed(2)}`;
                        }
                        if (runningInfo.trade_count !== undefined) {
                            statusHtml += ` | Trades: ${runningInfo.trade_count}`;
                        }

                        // Show circuit breaker status for live mode
                        if (runningInfo.circuit_breaker && runningInfo.circuit_breaker.tripped) {
                            const safeCircuitReason = escapeHtml(runningInfo.circuit_breaker.reason || 'Unknown');
                            statusHtml += `<br><span style="color: #dc3545;">⚠️ Circuit Breaker TRIPPED: ${safeCircuitReason}</span>`;
                        }

                        statusHtml += `</div>`;
                        hoverHtml += statusHtml;
                    }

                    // Stats
                    if (strat.stats && Object.keys(strat.stats).length > 0) {
                        hoverHtml += `<br><small>Stats: ${escapeHtml(JSON.stringify(strat.stats, null, 2))}</small>`;
                    }

                    // Run controls
                    hoverHtml += `
                        <div class="strategy-controls">
                            <select id="mode-select-${strategyKeyHtml}" ${isRunning ? 'disabled' : ''}
                                    onchange="UI.strats.onModeChange('${strategyKeyJs}', this.value)">
                                <option value="paper" ${runningInfo?.mode === 'paper' ? 'selected' : ''}>Paper Trading</option>
                                <option value="live" ${runningInfo?.mode === 'live' ? 'selected' : ''}>Live Trading</option>
                            </select>
                            <div id="live-options-${strategyKeyHtml}" style="display: none; margin-top: 5px;">
                                <label style="font-size: 10px; display: block;">
                                    <input type="checkbox" id="testnet-${strategyKeyHtml}" checked>
                                    Testnet Mode (Recommended)
                                </label>
                                <small style="color: #ff6600; font-size: 9px; display: block; margin-top: 3px;">
                                    ⚠️ Unchecking uses REAL MONEY
                                </small>
                            </div>
                            <button class="btn-run ${isRunning ? 'running' : ''}"
                                    onclick="event.stopPropagation(); ${isRunning
                                        ? `UI.strats.stopStrategy('${strategyKeyJs}')`
                                        : `UI.strats.runStrategyWithOptions('${strategyKeyJs}')`
                                    }">
                                ${isRunning ? 'Stop Strategy' : 'Run Strategy'}
                            </button>
                        </div>`;

                    strategyHover.innerHTML = hoverHtml;
                    strategyItem.appendChild(strategyHover);

                    // Append to target element
                    this.targetEl.appendChild(strategyItem);
                } catch (error) {
                    console.error(`Error processing strategy ${i + 1}:`, error);
                }
            }

            console.log('All strategies have been processed and appended.');
        } else {
            console.error("Target element for updating strategies is not set.");
        }
    }

    /**
     * Toggles the fee input field based on the state of the public checkbox.
     */
    toggleFeeInput() {
        /** @type {HTMLInputElement} */
        const publicCheckbox = document.getElementById('public_checkbox');
        const feeBox = document.getElementById('fee_box');

        if (publicCheckbox && feeBox) {
            feeBox.disabled = !publicCheckbox.checked;
        }
    }

    /**
     * Sets the callback function for deleting a strategy.
     * @param {Function} callback - The callback function to call when deleting a strategy.
     */
    registerDeleteStrategyCallback(callback) {
        this.onDeleteStrategy = callback;
    }

    // ========== AI Strategy Builder Methods ==========

    /**
     * Opens the AI strategy builder dialog.
     */
    openAIDialog() {
        const dialog = document.getElementById('ai_strategy_form');
        if (dialog) {
            // Reset state
            const descriptionEl = document.getElementById('ai_strategy_description');
            const loadingEl = document.getElementById('ai_strategy_loading');
            const errorEl = document.getElementById('ai_strategy_error');
            const generateBtn = document.getElementById('ai_generate_btn');

            if (descriptionEl) descriptionEl.value = '';
            if (loadingEl) loadingEl.style.display = 'none';
            if (errorEl) errorEl.style.display = 'none';
            if (generateBtn) generateBtn.disabled = false;

            // Show and center the dialog
            dialog.style.display = 'block';
            dialog.style.left = '50%';
            dialog.style.top = '50%';
            dialog.style.transform = 'translate(-50%, -50%)';
        }
    }

    /**
     * Closes the AI strategy builder dialog.
     */
    closeAIDialog() {
        const dialog = document.getElementById('ai_strategy_form');
        if (dialog) {
            dialog.style.display = 'none';
        }
    }

    /**
     * Calls the API to generate a strategy from the natural language description.
     */
    async generateWithAI() {
        const descriptionEl = document.getElementById('ai_strategy_description');
        const description = descriptionEl ? descriptionEl.value.trim() : '';

        if (!description) {
            alert('Please enter a strategy description.');
            return;
        }

        const loadingEl = document.getElementById('ai_strategy_loading');
        const errorEl = document.getElementById('ai_strategy_error');
        const generateBtn = document.getElementById('ai_generate_btn');

        // Gather user's available indicators and signals
        const indicators = this._getAvailableIndicators();
        const signals = this._getAvailableSignals();
        const defaultSource = this._getDefaultSource();

        // Check if description mentions indicators but none are configured
        const indicatorKeywords = ['ema', 'sma', 'rsi', 'macd', 'bollinger', 'bb', 'atr', 'adx', 'stochastic'];
        const descLower = description.toLowerCase();
        const mentionsIndicators = indicatorKeywords.some(kw => descLower.includes(kw));

        if (mentionsIndicators && indicators.length === 0) {
            const proceed = confirm(
                'Your strategy mentions indicators (EMA, RSI, Bollinger Bands, etc.) but you haven\'t configured any indicators yet.\n\n' +
                'Please add the required indicators in the Indicators panel on the right side of the screen first.\n\n' +
                'Click OK to proceed anyway (the AI will use price-based logic only), or Cancel to add indicators first.'
            );
            if (!proceed) {
                return;
            }
        }

        // Show loading state
        if (loadingEl) loadingEl.style.display = 'block';
        if (errorEl) errorEl.style.display = 'none';
        if (generateBtn) generateBtn.disabled = true;

        console.log('Generating strategy with:', { description, indicators, signals, defaultSource });

        try {

            const response = await fetch('/api/generate-strategy', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    description,
                    indicators,
                    signals,
                    default_source: defaultSource
                })
            });

            const data = await response.json();

            if (!response.ok || !data.success) {
                throw new Error(data.error || 'Strategy generation failed');
            }

            // Load the generated Blockly XML into the workspace
            if (this.workspaceManager && data.workspace_xml) {
                this.workspaceManager.loadWorkspaceFromXml(data.workspace_xml);
            }

            // Close the AI dialog
            this.closeAIDialog();

            console.log('Strategy generated successfully with AI');

        } catch (error) {
            console.error('AI generation error:', error);
            if (errorEl) {
                errorEl.textContent = `Error: ${error.message}`;
                errorEl.style.display = 'block';
            }
        } finally {
            if (loadingEl) loadingEl.style.display = 'none';
            if (generateBtn) generateBtn.disabled = false;
        }
    }

    /**
     * Gets the user's available indicators for the AI prompt.
     * @returns {Array} Array of indicator objects with name and outputs.
     * @private
     */
    _getAvailableIndicators() {
        // Use getIndicatorOutputs() which returns {name: outputs[]} from i_objs
        const indicatorOutputs = window.UI?.indicators?.getIndicatorOutputs?.() || {};
        const indicatorObjs = window.UI?.indicators?.i_objs || {};

        return Object.entries(indicatorOutputs).map(([name, outputs]) => ({
            name: name,
            type: indicatorObjs[name]?.constructor?.name || 'unknown',
            outputs: outputs
        }));
    }

    /**
     * Gets the user's available signals for the AI prompt.
     * @returns {Array} Array of signal objects with name.
     * @private
     */
    _getAvailableSignals() {
        // Get from UI.signals if available
        const signals = window.UI?.signals?.signals || [];
        return signals.map(sig => ({
            name: sig.name || sig.id
        }));
    }

    /**
     * Gets the current default trading source from the strategy form.
     * @returns {Object} Object with exchange, market, and timeframe.
     * @private
     */
    _getDefaultSource() {
        const exchangeEl = document.getElementById('strategy_exchange');
        const symbolEl = document.getElementById('strategy_symbol');
        const timeframeEl = document.getElementById('strategy_timeframe');

        return {
            exchange: exchangeEl ? exchangeEl.value : 'binance',
            market: symbolEl ? symbolEl.value : 'BTC/USDT',
            timeframe: timeframeEl ? timeframeEl.value : '5m'
        };
    }

    /**
     * Shows information modal for subscribed strategies (cannot edit).
     * @param {Object} strat - The subscribed strategy object.
     */
    showSubscribedStrategyInfo(strat) {
        const message = `Strategy: ${strat.name}\nCreator: @${strat.creator_name || 'Unknown'}\n\nThis is a subscribed strategy and cannot be edited.\n\nYou can run this strategy but the workspace and code are not accessible.`;
        alert(message);
    }

    /**
     * Shows the public strategy browser modal.
     */
    async showPublicStrategyBrowser() {
        // Request public strategies from server
        if (UI.strats && UI.strats.requestPublicStrategies) {
            UI.strats.requestPublicStrategies();
        }
    }

    /**
     * Renders the public strategy browser modal with available strategies.
     * @param {Array} strategies - List of public strategies to display.
     */
    renderPublicStrategyModal(strategies) {
        // Remove existing modal if any
        let existingModal = document.getElementById('public-strategy-modal');
        if (existingModal) {
            existingModal.remove();
        }

        // Create modal using form-popup style (like other dialogs)
        const modal = document.createElement('div');
        modal.id = 'public-strategy-modal';
        modal.className = 'form-popup';
        modal.style.cssText = 'display: block; position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 500px; border-radius: 10px; z-index: 1000; overflow: hidden;';
        modal.innerHTML = `
            <div class="dialog-header" id="public_strategy_header">
                <h1>Public Strategies</h1>
                <button class="dialog-close-btn" onclick="document.getElementById('public-strategy-modal').remove()">&times;</button>
            </div>
            <div class="form-container" style="padding: 15px; max-height: 400px; overflow-y: auto;">
                <div class="public-strategies-list" style="display: flex; flex-direction: column; gap: 10px;">
                    ${strategies.length === 0
                        ? '<p class="no-strategies" style="text-align: center; color: #666; padding: 20px;">No public strategies available.</p>'
                        : strategies.map(s => {
                            const keyRaw = String(s.tbl_key || '');
                            const keyHtml = escapeHtml(keyRaw);
                            const keyJs = escapeHtml(escapeJsString(keyRaw));
                            const nameHtml = escapeHtml(s.name || 'Unnamed Strategy');
                            const creatorHtml = escapeHtml(s.creator_name || 'Unknown');
                            const action = s.is_subscribed ? 'unsubscribeFromStrategy' : 'subscribeToStrategy';
                            const label = s.is_subscribed ? 'Unsubscribe' : 'Subscribe';
                            return `
                            <div class="public-strategy-item ${s.is_subscribed ? 'subscribed' : ''}" data-tbl-key="${keyHtml}"
                                 style="display: flex; justify-content: space-between; align-items: center; padding: 10px; background: #f8f9fa; border-radius: 6px; border: 1px solid #dee2e6;">
                                <div class="strategy-info">
                                    <strong>${nameHtml}</strong>
                                    <span class="creator" style="display: block; font-size: 11px; color: #666;">by @${creatorHtml}</span>
                                </div>
                                <button class="btn ${s.is_subscribed ? 'cancel' : 'submit'}"
                                        style="padding: 6px 12px; font-size: 12px;"
                                        onclick="UI.strats.${action}('${keyJs}')">
                                    ${label}
                                </button>
                            </div>
                        `;
                        }).join('')
                    }
                </div>
            </div>
        `;

        document.body.appendChild(modal);

        // Make it draggable
        if (window.UI && window.UI.dragElement) {
            window.UI.dragElement(modal, 'public_strategy_header');
        }
    }
}

class StratDataManager {
    constructor() {
        this.strategies = [];
    }

    /**
     * Fetches the saved strategies from the server.
     * @param {Object} comms - The communications instance to interact with the server.
     * @param {Object} data - An object containing user data.
     */
    fetchSavedStrategies(comms, data) {
        if (comms) {
            try {
                const requestData = {
                    request: 'strategies',
                    user_name: data?.user_name
                };
                comms.sendToApp('request', requestData);
            } catch (error) {
                console.error("Error fetching saved strategies:", error.message);
                alert('Unable to connect to the server. Please check your connection or try reinitializing the application.');
            }
        } else {
            throw new Error('Communications instance not available.');
        }
    }

    /**
     * Handles the creation of a new strategy.
     * @param {Object} data - The data for the newly created strategy.
     */
    addNewStrategy(data) {
        console.log("Adding new strategy. Data:", data);
        if (!data.name) {
            console.error("Strategy data missing 'name' field:", data);
        }
        this.strategies.push(data);
    }

    /**
     * Retrieves a strategy by its tbl_key.
     * @param {string} tbl_key - The tbl_key of the strategy to find.
     * @returns {Object|null} - The strategy object or null if not found.
     */
    getStrategyById(tbl_key) {
        return this.strategies.find(strategy => strategy.tbl_key === tbl_key) || null;
    }



    /**
     * Handles updates to the strategy itself (e.g., configuration changes).
     * @param {Object} data - The updated strategy data.
     */
    updateStrategyData(data) {
        // Ignore runtime execution events; only apply persisted strategy records.
        if (!data || typeof data !== 'object') {
            return;
        }
        const strategyKey = data.tbl_key || data.id;
        if (!strategyKey) {
            return;
        }

        console.log("Strategy updated:", data);
        const index = this.strategies.findIndex(
            strategy => (strategy.tbl_key || strategy.id) === strategyKey
        );
        if (index !== -1) {
            this.strategies[index] = { ...this.strategies[index], ...data };
        } else {
            this.strategies.push(data); // Add if not found
        }
    }

    /**
     * Handles the deletion of a strategy.
     * @param {string} tbl_key - The tbl_key for the deleted strategy.
     */
    removeStrategy(tbl_key) {
        try {
            console.log(`Removing strategy with tbl_key: ${tbl_key}`);
            // Filter out the strategy with the matching tbl_key
            this.strategies = this.strategies.filter(strat => strat.tbl_key !== tbl_key);
            console.log("Remaining strategies:", this.strategies);
        } catch (error) {
            console.error("Error handling strategy deletion:", error.message);
        }
    }


    /**
     * Handles batch updates for strategies, such as multiple configuration or performance updates.
     * @param {Object} data - The data containing batch updates for strategies.
     */
    applyBatchUpdates(data) {
        const { stg_updts } = data;
        if (Array.isArray(stg_updts)) {
            stg_updts.forEach(strategy => {
                if (strategy && (strategy.tbl_key || strategy.id)) {
                    this.updateStrategyData(strategy);
                }
            });
        }
    }

    /**
     * Returns all available strategies.
     * @returns {Object[]} - The list of available strategies.
     */
    getAllStrategies() {
        return this.strategies;
    }
}

class StratWorkspaceManager {
    constructor() {
        this.workspace = null;
        this.blocksDefined = false;
        this.MAX_TOP_LEVEL_BLOCKS = 10; // Set your desired limit
        this.MAX_DEPTH = 10; // Set your desired limit
    }

    /**
     * Initializes the Blockly workspace with custom blocks and generators.
     * Ensures required elements are present in the DOM and initializes the workspace.
     * @async
     * @throws {Error} If required elements ('blocklyDiv' or 'toolbox_advanced') are not found.
     */
    async initWorkspace() {
        if (!document.getElementById('blocklyDiv')) {
            console.error("blocklyDiv is not loaded.");
            return;
        }

        if (this.workspace) {
            this.workspace.dispose();
        }

        // Initialize custom blocks and Blockly workspace
        await this._loadModulesAndInitWorkspace();

        // Set the maximum allowed nesting depth
        Blockly.JSON.maxDepth = this.MAX_DEPTH; // or any desired value
    }


    async _loadModulesAndInitWorkspace() {
        if (!this.blocksDefined) {
            try {
                // Separate json_base_generator as it needs to be loaded first
                const jsonBaseModule = await import('./blocks/generators/json_base_generator.js');
                jsonBaseModule.defineJsonBaseGenerator();
                console.log('Defined defineJsonBaseGenerator from json_base_generator.js');

                // Map generator and block files to the functions that define them.
                const generatorModules = [
                    { file: 'balances_generators.js', defineFunc: 'defineBalancesGenerators' },
                    { file: 'order_metrics_generators.js', defineFunc: 'defineOrderMetricsGenerators' },
                    { file: 'trade_metrics_generators.js', defineFunc: 'defineTradeMetricsGenerators' },
                    { file: 'time_metrics_generators.js', defineFunc: 'defineTimeMetricsGenerators' },
                    { file: 'market_data_generators.js', defineFunc: 'defineMarketDataGenerators' },
                    { file: 'logical_generators.js', defineFunc: 'defineLogicalGenerators' },
                    { file: 'trade_order_generators.js', defineFunc: 'defineTradeOrderGenerators' },
                    { file: 'control_generators.js', defineFunc: 'defineControlGenerators' },
                    { file: 'values_and_flags_generators.js', defineFunc: 'defineVAFGenerators' },
                    { file: 'risk_management_generators.js', defineFunc: 'defineRiskManagementGenerators' },
                    { file: 'advanced_math_generators.js', defineFunc: 'defineAdvancedMathGenerators' }
                ];

                const blockModules = [
                    { file: 'balances_blocks.js', defineFunc: 'defineBalanceBlocks' },
                    { file: 'order_metrics_blocks.js', defineFunc: 'defineOrderMetricsBlocks' },
                    { file: 'trade_metrics_blocks.js', defineFunc: 'defineTradeMetricsBlocks' },
                    { file: 'time_metrics_blocks.js', defineFunc: 'defineTimeMetricsBlocks' },
                    { file: 'market_data_blocks.js', defineFunc: 'defineMarketDataBlocks' },
                    { file: 'logical_blocks.js', defineFunc: 'defineLogicalBlocks' },
                    { file: 'trade_order_blocks.js', defineFunc: 'defineTradeOrderBlocks' },
                    { file: 'control_blocks.js', defineFunc: 'defineControlBlocks' },
                    { file: 'values_and_flags.js', defineFunc: 'defineValuesAndFlags' },
                    { file: 'risk_management_blocks.js', defineFunc: 'defineRiskManagementBlocks' },
                    { file: 'advanced_math_blocks.js', defineFunc: 'defineAdvancedMathBlocks' }
                ];

                // Function to import and define modules in parallel
                const importAndDefineParallel = async (modules, basePath) => {
                    await Promise.all(modules.map(async moduleInfo => {
                        try {
                            const module = await import(`${basePath}/${moduleInfo.file}`);
                            if (typeof module[moduleInfo.defineFunc] === 'function') {
                                module[moduleInfo.defineFunc]();
                                console.log(`Defined ${moduleInfo.defineFunc} from ${moduleInfo.file}`);
                            } else {
                                console.error(`Define function ${moduleInfo.defineFunc} not found in ${moduleInfo.file}`);
                            }
                        } catch (importError) {
                            console.error(`Error importing ${moduleInfo.file}:`, importError);
                        }
                    }));
                };

                // Import and define all generator modules
                await importAndDefineParallel(generatorModules, './blocks/generators');

                // Import and define all block modules
                await importAndDefineParallel(blockModules, './blocks/blocks');

                // Load and define indicator blocks
                const indicatorBlocksModule = await import('./blocks/indicator_blocks.js');
                indicatorBlocksModule.defineIndicatorBlocks();

                // Load and define signal blocks
                const signalBlocksModule = await import('./blocks/signal_blocks.js');
                signalBlocksModule.defineSignalBlocks();

                // Load and define formation blocks
                const formationBlocksModule = await import('./blocks/formation_blocks.js');
                formationBlocksModule.defineFormationBlocks();

            } catch (error) {
                console.error("Error loading Blockly modules: ", error);
                return;
            }
            this.blocksDefined = true;
        }

        const toolboxElement = document.getElementById('toolbox_advanced');
        if (!toolboxElement) {
            console.error("toolbox is not loaded.");
            return;
        }
        /**
         * @override the toolbox zoom
         */
        Blockly.VerticalFlyout.prototype.getFlyoutScale = function() {
          return 1;
        };
        this.workspace = Blockly.inject('blocklyDiv', {
            toolbox: toolboxElement,
            scrollbars: true,
            trashcan: true,
            grid: {
                spacing: 20,
                length: 3,
                colour: '#ccc',
                snap: true
            },
            zoom: {
                controls: true,
                wheel: true,
                startScale: 1.0,
                maxScale: 3,
                minScale: 0.3,
                scaleSpeed: 1.2
            }
        });

        // Add tooltips to toolbox categories
        this._setupCategoryTooltips();

        // Note: Blockly has built-in copy/paste support (Ctrl+C, Ctrl+V, Ctrl+X, Delete)

        console.log('Blockly workspace initialized and modules loaded.');
    }

    /**
     * Adjusts the Blockly workspace dimensions to fit within the container.
     */
    adjustWorkspace() {
        const blocklyDiv = document.getElementById('blocklyDiv');
        if (blocklyDiv && this.workspace) {
            Blockly.svgResize(this.workspace);
        } else {
            console.error("Cannot resize workspace: Blockly or blocklyDiv is not loaded.");
        }
    }

    /**
     * Generates the strategy data including, JSON representation, and workspace XML.
     * @returns {string|null} - A JSON string containing the strategy data or null if failed.
     */
    compileStrategyJson() {
        if (!this.workspace) {
            console.error("Workspace is not available.");
            return null;
        }
        /** @type {HTMLInputElement} */
        const nameElement = document.getElementById('name_box');
        if (!nameElement) {
            console.error("Name input element (name_box) is not available.");
            return null;
        }
        const strategyName = nameElement.value;

        // Generate code and data representations
        const strategyJson = this._generateStrategyJsonFromWorkspace();

        if (!strategyJson) {
            console.error("Failed to generate strategy JSON.");
            return null;
        }

        // Generate workspace XML for restoration when editing
        const workspaceXml = Blockly.Xml.workspaceToDom(this.workspace);
        const workspaceXmlText = Blockly.Xml.domToText(workspaceXml);

        return JSON.stringify({
            name: strategyName,
            strategy_json: strategyJson,
            workspace: workspaceXmlText
        });
    }


    /**
     * Generates a JSON representation of the strategy from the workspace.
     * @private
     * @returns {Object} - An array of JSON objects representing the top-level blocks.
     */
    _generateStrategyJsonFromWorkspace() {
      const { initializationBlocks, actionBlocks } = this._categorizeOrphanedBlocks(this.workspace);

      const totalTopLevelBlocks = initializationBlocks.length + actionBlocks.length;

      if (totalTopLevelBlocks > this.MAX_TOP_LEVEL_BLOCKS) {
        console.error(`Too many top-level blocks. Maximum allowed is ${this.MAX_TOP_LEVEL_BLOCKS}.`);
        alert(`Your strategy has too many top-level blocks. Please reduce the number of top-level blocks to ${this.MAX_TOP_LEVEL_BLOCKS} or fewer.`);
        return null;
      }

      // Proceed with strategy JSON creation
        return this._createStrategyJson(initializationBlocks, actionBlocks);
    }

     /**
     * Identify all orphaned blocks on the workspace. Categorize them into initialization blocks and action blocks.
     * @private
     * @returns {Object} - an object containing an array of initializationBlocks and actionBlocks.
     */
    _categorizeOrphanedBlocks(workspace) {
      const blocks = workspace.getTopBlocks(true);
      const orphanedBlocks = blocks.filter(block => !block.getParent());

      const initializationBlocks = [];
      const actionBlocks = [];

      orphanedBlocks.forEach(block => {
        // Only consider specified block types
        switch (block.type) {
          // Initialization block types (run first)
          case 'set_available_strategy_balance':
          case 'set_variable':
          case 'set_flag':
          case 'set_leverage':
          case 'max_position_size':
          case 'pause_strategy':
          case 'strategy_resume':
          case 'strategy_exit':
          case 'notify_user':
            initializationBlocks.push(block);
            break;

          // Action block types (run last)
          case 'trade_action':
          case 'schedule_action':
          case 'execute_if':
            actionBlocks.push(block);
            break;

          // Ignore other blocks or log a warning
          default:
            console.warn(`Block type '${block.type}' is not allowed as a top-level block and will be ignored.`);
            break;
        }
      });

      return { initializationBlocks, actionBlocks };
    }

    _createStrategyJson(initializationBlocks, actionBlocks) {
      const statements = [];

      // Process initialization blocks (run first)
      initializationBlocks.forEach(block => {
        const blockJson = Blockly.JSON._blockToJson(block);
        if (blockJson) {
          statements.push(blockJson);
        }
      });

      // Process action blocks (run last)
      actionBlocks.forEach(block => {
        const blockJson = Blockly.JSON._blockToJson(block);
        if (blockJson) {
          statements.push(blockJson);
        }
      });

      // Create the root strategy block
        return {
          type: 'strategy',
          statements: statements
      };
    }

    /**
     * Adds tooltips to toolbox category labels.
     * @private
     */
    _setupCategoryTooltips() {
        // Category tooltips mapping
        const categoryTooltips = {
            'Indicators': 'Use your configured technical indicators in strategy logic',
            'Balances': 'Access strategy and account balance information',
            'Order Metrics': 'Monitor order status, volume, and fill rates',
            'Trade Metrics': 'Monitor active trades, P&L, and trade history',
            'Time Metrics': 'Time-based conditions for your strategy',
            'Market Data': 'Access real-time prices and candle data',
            'Logical': 'Build conditions with comparisons and logic operators',
            'Trade Order': 'Execute trades with stop-loss, take-profit, and limits',
            'Control': 'Control strategy flow: pause, resume, exit, and schedule',
            'Values and flags': 'Store values, set flags, and send notifications',
            'Risk Management': 'Control leverage, margin, and position limits',
            'Math': 'Arithmetic, statistics, and mathematical functions'
        };

        // Wait a moment for Blockly to render the toolbox
        setTimeout(() => {
            // Find all category labels in the toolbox
            const toolboxDiv = document.querySelector('.blocklyToolboxDiv');
            if (!toolboxDiv) return;

            const categoryRows = toolboxDiv.querySelectorAll('.blocklyTreeRow');
            categoryRows.forEach(row => {
                const labelSpan = row.querySelector('.blocklyTreeLabel');
                if (labelSpan) {
                    const categoryName = labelSpan.textContent;
                    if (categoryTooltips[categoryName]) {
                        row.setAttribute('title', categoryTooltips[categoryName]);
                        row.style.cursor = 'help';
                    }
                }
            });
            console.log('Category tooltips added');
        }, 100);
    }

    /**
     * Restores the Blockly workspace from an XML string.
     * @param {string} workspaceXmlText - The XML text representing the workspace.
     */
    loadWorkspaceFromXml(workspaceXmlText) {
        try {
            if (!this.workspace) {
                console.error("Cannot restore workspace: Blockly workspace is not initialized.");
                return;
            }

            const workspaceXml = Blockly.utils.xml.textToDom(workspaceXmlText);
            if (!workspaceXml || !workspaceXml.hasChildNodes()) {
                console.error('Invalid workspace XML provided.');
                alert('The provided workspace data is invalid and cannot be loaded.');
                return;
            }

            this.workspace.clear();
            Blockly.Xml.domToWorkspace(workspaceXml, this.workspace);
        } catch (error) {
            // Save the failed XML for debugging
            const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
            const debugKey = `failed_strategy_xml_${timestamp}`;
            try {
                localStorage.setItem(debugKey, workspaceXmlText);
                console.error(`Failed XML saved to localStorage as "${debugKey}"`);
                console.error('To retrieve: localStorage.getItem("' + debugKey + '")');
            } catch (e) {
                // If localStorage fails, log to console
                console.error('Failed workspace XML (copy for debugging):', workspaceXmlText);
            }

            if (error instanceof SyntaxError) {
                console.error('Syntax error in workspace XML:', error.message);
                alert('There was a syntax error in the workspace data. Please check the data and try again.\n\nDebug XML saved to localStorage as: ' + debugKey);
            } else {
                console.error('Unexpected error restoring workspace:', error);
                alert('An unexpected error occurred while restoring the workspace.\n\nDebug XML saved to localStorage as: ' + debugKey);
            }
        }
    }
}

class Strategies {
    constructor() {
        this.dataManager = new StratDataManager();
        this.workspaceManager = new StratWorkspaceManager();
        this.uiManager = new StratUIManager(this.workspaceManager);
        this.comms = null;
        this.data = null;
        this._initialized = false;

        // Track running strategies: key = "strategy_id:mode", value = { mode, instance_id, ... }
        // Using composite key to support same strategy in different modes
        this.runningStrategies = new Map();

        // Set the delete callback
        this.uiManager.registerDeleteStrategyCallback(this.deleteStrategy.bind(this));

        // Bind methods to ensure correct 'this' context
        this.submitStrategy = this.submitStrategy.bind(this);
        this.runStrategy = this.runStrategy.bind(this);
        this.stopStrategy = this.stopStrategy.bind(this);
    }

    /**
     * Initializes the Strategies instance with necessary dependencies.
     * @param {string} targetId - The ID of the HTML element where strategies will be displayed.
     * @param {string} formElId - The ID of the HTML element for the strategy creation form.
     * @param {Object} data - An object containing user data and communication instances.
     */
    initialize(targetId, formElId, data) {
        try {
            // Initialize UI Manager
            this.uiManager.initUI(targetId, formElId);

            if (!data || typeof data !== 'object') {
                console.error("Invalid data provided for initialization.");
                return;
            }
            this.data = data;

            if (!this.data.user_name || typeof this.data.user_name !== 'string') {
                console.error("Invalid user_name provided in data object.");
                return;
            }

            this.comms = this.data?.comms;
            if (!this.comms) {
                console.error("Communications instance not provided in data.");
                return;
            }

            // Register handlers with Comms for specific message types
            this.comms.on('strategy_created', this.handleStrategyCreated.bind(this));
            this.comms.on('strategy_updated', this.handleStrategyUpdated.bind(this));
            this.comms.on('strategy_deleted', this.handleStrategyDeleted.bind(this));
            this.comms.on('updates', this.handleUpdates.bind(this));
            this.comms.on('strategies', this.handleStrategies.bind(this));
            // Register the handler for 'strategy_error' reply
            this.comms.on('strategy_error', this.handleStrategyError.bind(this));
            // Register handlers for run/stop strategy
            this.comms.on('strategy_started', this.handleStrategyStarted.bind(this));
            this.comms.on('strategy_stopped', this.handleStrategyStopped.bind(this));
            this.comms.on('strategy_run_error', this.handleStrategyRunError.bind(this));
            this.comms.on('strategy_stop_error', this.handleStrategyStopError.bind(this));
            this.comms.on('strategy_status', this.handleStrategyStatus.bind(this));
            this.comms.on('strategy_events', this.handleStrategyEvents.bind(this));

            // Register handlers for subscription events
            this.comms.on('public_strategies', this.handlePublicStrategies.bind(this));
            this.comms.on('strategy_subscribed', this.handleStrategySubscribed.bind(this));
            this.comms.on('strategy_unsubscribed', this.handleStrategyUnsubscribed.bind(this));
            this.comms.on('subscription_error', this.handleSubscriptionError.bind(this));

            // Fetch saved strategies using DataManager
            this.dataManager.fetchSavedStrategies(this.comms, this.data);

            // Request status of any running strategies (handles page reload)
            this.requestStrategyStatus();

            this._initialized = true;
        } catch (error) {
            console.error("Error initializing Strategies instance:", error.message);
        }
    }

    /**
     * Handles strategy-related errors sent from the server.
     * @param {Object} errorData - The error message and additional details.
     */
    handleStrategyError(errorData) {
        console.error("Strategy Error:", errorData.message);

        // Display a user-friendly error message
        if (errorData.message) {
            alert(`Error: ${errorData.message}`);
        } else {
            alert("An unknown error occurred while processing the strategy.");
        }
    }

    /**
     * Handles the reception of existing strategies from the server.
     * @param {Array} data - The data containing the list of existing strategies.
     */
    handleStrategies(data) {
        console.log("Received strategies data:", data);

        if (Array.isArray(data)) {
            console.log(`Number of strategies received: ${data.length}`);
            // Update the DataManager with the received strategies
            this.dataManager.strategies = data;

            // Update the UI to display the strategies
            this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
            console.log("Successfully loaded strategies.");
        } else {
            console.error("Failed to load strategies: Invalid data format");
            alert("Failed to load strategies: Invalid data format");
        }
    }



    /**
     * Handles the creation of a new strategy.
     * @param {Object} data - The data for the newly created strategy.
     */
    handleStrategyCreated(data) {
        console.log("handleStrategyCreated received data:", data);

        if (data.success && data.strategy) {
            this.dataManager.addNewStrategy(data.strategy);
            this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
            // Close the dialog only on success
            this.uiManager.hideForm();
        } else {
            console.error("Failed to create strategy:", data.message);
            // Keep dialog open and show error - user can fix the issue
            const errorMsg = data.message || 'Unknown error';
            if (errorMsg.toLowerCase().includes('name') || errorMsg.toLowerCase().includes('exists')) {
                alert(`Strategy name already exists. Please choose a different name.`);
            } else {
                alert(`Strategy creation failed: ${errorMsg}`);
            }
        }
    }

    /**
     * Handles updates to the strategy itself (e.g., configuration changes).
     * @param {Object} data - The server response containing strategy update metadata.
     */
    handleStrategyUpdated(data) {
        if (data.success) {
            console.log("Strategy updated successfully:", data);

            // Close the dialog on success
            this.uiManager.hideForm();

            // Locate the strategy in the local state by its tbl_key
            const updatedStrategyKey = data.strategy.tbl_key;
            const updatedAt = data.updated_at;

            const strategy = this.dataManager.getStrategyById(updatedStrategyKey);
            if (strategy) {
                // Update the relevant strategy data
                Object.assign(strategy, data.strategy);

                // Update the `updated_at` field
                strategy.updated_at = updatedAt;

                // Refresh the UI to reflect the updated metadata
                this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
            } else {
                console.warn("Updated strategy not found in local records:", updatedStrategyKey);
            }

            // Check if the strategy was running and prompt for restart
            if (this.isStrategyRunning(updatedStrategyKey)) {
                const runningInfo = this.getRunningInfo(updatedStrategyKey);
                const strategyName = data.strategy.name || 'Strategy';
                const modeText = runningInfo ? runningInfo.mode : 'current';

                const shouldRestart = confirm(
                    `"${strategyName}" was updated successfully!\n\n` +
                    `This strategy is currently running in ${modeText} mode.\n` +
                    `The changes will NOT take effect until you restart.\n\n` +
                    `Would you like to restart the strategy now to apply changes?`
                );

                if (shouldRestart) {
                    // Stop and restart the strategy
                    const mode = runningInfo.mode;
                    const testnet = runningInfo.testnet !== undefined ? runningInfo.testnet : true;
                    const initialBalance = runningInfo.initial_balance || 10000;

                    // Stop first
                    this.stopStrategy(updatedStrategyKey, mode);

                    // Restart after a brief delay to allow stop to complete
                    setTimeout(() => {
                        this.runStrategy(updatedStrategyKey, mode, initialBalance, testnet);
                    }, 1000);
                }
            }
        } else {
            console.error("Failed to update strategy:", data.message);
            // Keep dialog open and show error - user can fix the issue
            const errorMsg = data.message || 'Unknown error';
            if (errorMsg.toLowerCase().includes('name') || errorMsg.toLowerCase().includes('exists')) {
                alert(`Strategy name already exists. Please choose a different name.`);
            } else {
                alert(`Strategy update failed: ${errorMsg}`);
            }
        }
    }



    /**
     * Handles the deletion of a strategy.
     * @param {Object} response - The full response object from the server.
     */
    handleStrategyDeleted(response) {
        // Extract the message and tbl_key from the response
        const success = response.message === "Strategy deleted successfully."; // Use the message to confirm success
        const tbl_key = response.tbl_key; // Extract tbl_key directly

        if (success) {
            if (tbl_key) {
                console.log(`Successfully deleted strategy with tbl_key: ${tbl_key}`);
                // Remove the strategy using tbl_key
                this.dataManager.removeStrategy(tbl_key);

                // Update the UI to reflect the deletion
                this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
            } else {
                console.warn("tbl_key is missing in the server response, unable to remove strategy from UI.");
            }
        } else {
            // Handle failure
            console.error("Failed to delete strategy:", response.message || "Unknown error");
            alert(`Failed to delete strategy: ${response.message || "Unknown error"}`);
        }
    }



    /**
     * Handles batch updates for strategies, such as multiple configuration or performance updates.
     * @param {Object} data - The data containing batch updates for strategies.
     */
    handleUpdates(data) {
        const strategyEvents = Array.isArray(data?.stg_updts) ? data.stg_updts : [];
        for (const event of strategyEvents) {
            if (!event || typeof event !== 'object') {
                continue;
            }

            if (event.type === 'strategy_exited' && event.strategy_id && event.mode) {
                this.runningStrategies.delete(this._makeRunningKey(event.strategy_id, event.mode));
            }

            if (event.type === 'tick_complete' && event.strategy_id && event.mode) {
                const key = this._makeRunningKey(event.strategy_id, event.mode);
                const running = this.runningStrategies.get(key);
                if (running) {
                    if (typeof event.balance === 'number') {
                        running.balance = event.balance;
                    }
                    if (typeof event.trades === 'number') {
                        running.trade_count = event.trades;
                    }
                    this.runningStrategies.set(key, running);
                }
            }

            if (event.type === 'error') {
                console.warn("Strategy runtime error:", event.message, event);
            }
        }

        this.dataManager.applyBatchUpdates(data);
        this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
    }

    /**
     * Resizes the Blockly workspace using StratWorkspaceManager.
     */
    resizeWorkspace() {
        this.workspaceManager.adjustWorkspace();
    }

    /**
     * Generates the strategy data including Python code, JSON representation, and workspace XML.
     * @returns {string} - A JSON string containing the strategy data.
     */
    generateStrategyJson() {
        return this.workspaceManager.compileStrategyJson();
    }

    /**
     * Restores the Blockly workspace from an XML string using StratWorkspaceManager.
     * @param {string} workspaceXmlText - The XML text representing the workspace.
     */
    restoreWorkspaceFromXml(workspaceXmlText) {
        this.workspaceManager.loadWorkspaceFromXml(workspaceXmlText);
    }

    /**
     * Submits or edits a strategy based on the provided action.
     * @param {string} action - Action type, either 'new' or 'edit'.
     */
    submitStrategy(action) {
        /** @type {HTMLInputElement} */
        const feeBox = document.getElementById('fee_box');
        /** @type {HTMLInputElement} */
        const nameBox = document.getElementById('name_box');
        /** @type {HTMLInputElement} */
        const publicCheckbox = document.getElementById('public_checkbox');
        /** @type {HTMLSelectElement} */
        const exchangeSelect = document.getElementById('strategy_exchange');
        /** @type {HTMLInputElement} */
        const symbolInput = document.getElementById('strategy_symbol');
        /** @type {HTMLSelectElement} */
        const timeframeSelect = document.getElementById('strategy_timeframe');

        if (!feeBox || !nameBox || !publicCheckbox) {
            console.error("One or more form elements are missing.");
            alert("An error occurred: Required form elements are missing.");
            return;
        }

        let strategyData;
        try {
            // Compile the strategy JSON (conditions and actions)
            const compiledStrategy = this.generateStrategyJson(); // Returns JSON string
            const parsedStrategy = JSON.parse(compiledStrategy); // Object with 'name', 'strategy_json', 'workspace'

            // Check for an incomplete strategy (no root blocks)
            if (!parsedStrategy.strategy_json.statements || parsedStrategy.strategy_json.statements.length === 0) {
                alert("Your strategy is incomplete. Please add actions or conditions before submitting.");
                return;
            }

            // Get default source values
            const defaultSource = {
                exchange: exchangeSelect ? exchangeSelect.value : 'binance',
                market: symbolInput ? symbolInput.value.trim() : 'BTC/USDT',
                timeframe: timeframeSelect ? timeframeSelect.value : '5m'
            };

            // Prepare the strategy data to send
            strategyData = {
                code: parsedStrategy.strategy_json,      // The compiled strategy JSON string
                workspace: parsedStrategy.workspace,     // Serialized workspace XML
                name: nameBox.value.trim(),
                fee: parseFloat(feeBox.value.trim()),
                public: publicCheckbox.checked ? 1 : 0,
                user_name: this.data.user_name,
                default_source: defaultSource            // Include explicit default source
                // Add 'stats' if necessary
            };

            console.log('Submitting strategy with default source:', defaultSource);

        } catch (error) {
            console.error('Failed to compile strategy JSON:', error);
            alert('An error occurred while processing the strategy data.');
            return;
        }

        // Basic client-side validation
        if (isNaN(strategyData.fee) || strategyData.fee < 0) {
            alert("Please enter a valid, non-negative number for the fee.");
            return;
        }

        // Validate fee is 1-100 if public
        if (strategyData.public === 1 && (strategyData.fee < 1 || strategyData.fee > 100)) {
            alert("Fee must be between 1 and 100 (percentage of exchange commission).");
            return;
        }

        if (!strategyData.name) {
            alert("Please provide a name for the strategy.");
            return;
        }

        // Send the strategy data to the server
        if (this.comms) {
            // Determine message type based on action
            const messageType = action === 'new' ? 'new_strategy' : 'edit_strategy';
            this.comms.sendToApp(messageType, strategyData);
            // Don't hide form here - wait for server response
            // Form will be hidden in handleStrategyCreated/handleStrategyUpdated on success
        } else {
            console.error("Comms instance not available or invalid action type.");
        }
    }

    /**
     * Deletes a strategy by its name.
     * @param {string} tbl_key - The name of the strategy to be deleted.
     */
    deleteStrategy(tbl_key) {
        console.log(`Deleting strategy: ${tbl_key}`);

        // Prepare data for server request
        const deleteData = {
            tbl_key: tbl_key
        };

        // Send delete request to the server
        if (this.comms) {
            this.comms.sendToApp('delete_strategy', deleteData);
        } else {
            console.error("Comms instance not available.");
        }
    }

    /**
     * Creates a composite key for running strategies map.
     * @param {string} strategyId - Strategy tbl_key.
     * @param {string} mode - Trading mode.
     * @returns {string} - Composite key.
     */
    _makeRunningKey(strategyId, mode) {
        return `${strategyId}:${mode}`;
    }

    /**
     * Requests current status of running strategies from server.
     * Called on init to sync state after page reload.
     */
    requestStrategyStatus() {
        if (!this.comms) {
            console.warn("Comms not available, skipping status request.");
            return;
        }

        this.comms.sendToApp('get_strategy_status', {
            user_name: this.data.user_name
        });
    }

    /**
     * Handles mode change in the dropdown to show/hide live options.
     * @param {string} strategyId - The strategy tbl_key.
     * @param {string} mode - Selected mode.
     */
    onModeChange(strategyId, mode) {
        const liveOptions = document.getElementById(`live-options-${strategyId}`);
        if (liveOptions) {
            liveOptions.style.display = mode === 'live' ? 'block' : 'none';
        }
    }

    /**
     * Runs a strategy with options from the UI.
     * @param {string} strategyId - The strategy tbl_key.
     */
    runStrategyWithOptions(strategyId) {
        console.log(`runStrategyWithOptions called for strategy: ${strategyId}`);

        const modeSelect = document.getElementById(`mode-select-${strategyId}`);
        const testnetCheckbox = document.getElementById(`testnet-${strategyId}`);

        const mode = modeSelect ? modeSelect.value : 'paper';
        const testnet = testnetCheckbox ? testnetCheckbox.checked : true;

        // Show immediate visual feedback on the button
        const btn = document.querySelector(`.strategy-item[data-strategy-id="${strategyId}"] .btn-run`);
        if (btn) {
            btn.disabled = true;
            btn.textContent = 'Starting...';
            btn.style.backgroundColor = '#6c757d';
        }

        // For live mode with production (non-testnet), show extra warning
        if (mode === 'live' && !testnet) {
            const proceed = confirm(
                "⚠️ WARNING: PRODUCTION MODE ⚠️\n\n" +
                "You are about to start LIVE trading with REAL MONEY.\n\n" +
                "• Real trades will be executed on your exchange account\n" +
                "• Financial losses are possible\n" +
                "• The circuit breaker will halt at -10% drawdown\n\n" +
                "Are you absolutely sure you want to continue?"
            );
            if (!proceed) {
                // Reset button state
                if (btn) {
                    btn.disabled = false;
                    btn.textContent = 'Run Strategy';
                    btn.style.backgroundColor = '#28a745';
                }
                return;
            }
        }

        this.runStrategy(strategyId, mode, 10000, testnet);
    }

    /**
     * Runs a strategy in the specified mode.
     * @param {string} strategyId - The strategy tbl_key.
     * @param {string} mode - Trading mode ('paper' or 'live').
     * @param {number} initialBalance - Starting balance (default 10000).
     * @param {boolean} testnet - Use testnet for live trading (default true).
     */
    runStrategy(strategyId, mode = 'paper', initialBalance = 10000, testnet = true) {
        console.log(`Running strategy ${strategyId} in ${mode} mode (testnet: ${testnet})`);

        if (!this.comms) {
            console.error("Comms instance not available.");
            return;
        }

        // Check if already running in this mode
        const runKey = this._makeRunningKey(strategyId, mode);
        if (this.runningStrategies.has(runKey)) {
            alert(`Strategy is already running in ${mode} mode.`);
            return;
        }

        // Show loading state on the button
        const btnSelector = `.strategy-item[data-strategy-id="${strategyId}"] .btn-run`;
        const btn = document.querySelector(btnSelector);
        if (btn) {
            btn.disabled = true;
            btn.textContent = 'Starting...';
            btn.style.opacity = '0.7';
        }

        const runData = {
            strategy_id: strategyId,
            mode: mode,
            initial_balance: initialBalance,
            commission: 0.001,
            testnet: testnet,
            max_position_pct: 0.5,
            circuit_breaker_pct: -0.10,
            user_name: this.data.user_name
        };

        this.comms.sendToApp('run_strategy', runData);

        // Re-enable button after a short delay (server response will update state)
        setTimeout(() => {
            if (btn) {
                btn.disabled = false;
                btn.style.opacity = '1';
                // If not yet marked as running, reset the text
                if (!this.runningStrategies.has(runKey)) {
                    btn.textContent = 'Run Strategy';
                }
            }
        }, 3000);
    }

    /**
     * Stops a running strategy.
     * @param {string} strategyId - The strategy tbl_key.
     * @param {string} mode - Trading mode (optional, will find first running mode).
     */
    stopStrategy(strategyId, mode = null) {
        console.log(`Stopping strategy ${strategyId}`);

        if (!this.comms) {
            console.error("Comms instance not available.");
            return;
        }

        // If mode not specified, find any running instance of this strategy
        let runKey;
        let running;

        if (mode) {
            runKey = this._makeRunningKey(strategyId, mode);
            running = this.runningStrategies.get(runKey);
        } else {
            // Find first matching strategy regardless of mode
            for (const [key, value] of this.runningStrategies) {
                if (key.startsWith(strategyId + ':')) {
                    runKey = key;
                    running = value;
                    break;
                }
            }
        }

        if (!running) {
            console.warn(`Strategy ${strategyId} is not running.`);
            return;
        }

        const stopData = {
            strategy_id: strategyId,
            mode: running.mode,
            user_name: this.data.user_name
        };

        this.comms.sendToApp('stop_strategy', stopData);
    }

    /**
     * Handles successful strategy start.
     * @param {Object} data - Response data from server.
     */
    handleStrategyStarted(data) {
        console.log("Strategy started:", data);

        if (data.strategy_id) {
            // Use actual_mode if provided (for live fallback), otherwise use requested mode
            const actualMode = data.actual_mode || data.mode;
            const runKey = this._makeRunningKey(data.strategy_id, actualMode);

            this.runningStrategies.set(runKey, {
                mode: actualMode,
                requested_mode: data.mode,
                instance_id: data.instance_id,
                strategy_name: data.strategy_name,
                initial_balance: data.initial_balance,
                testnet: data.testnet,
                exchange: data.exchange,
                max_position_pct: data.max_position_pct,
                circuit_breaker_pct: data.circuit_breaker_pct,
                start_time: new Date().toISOString()
            });

            // Notify statistics module
            if (UI.statistics) {
                UI.statistics.registerRunningStrategy(data.strategy_id, {
                    name: data.strategy_name,
                    mode: actualMode,
                    testnet: data.testnet,
                    initial_balance: data.initial_balance,
                    start_time: new Date().toISOString()
                });
            }

            // Update the UI to reflect running state
            this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());

            // Show success notification
            const modeInfo = data.testnet !== undefined
                ? `${actualMode} (${data.testnet ? 'testnet' : 'production'})`
                : actualMode;
            const successMsg = `Strategy '${data.strategy_name}' started successfully in ${modeInfo} mode!`;

            // Show warning first if present, then show success confirmation
            if (data.warning) {
                alert(`${data.warning}\n\n${successMsg}`);
            } else {
                alert(successMsg);
            }
        }

        const modeInfo = data.testnet !== undefined
            ? `${data.actual_mode || data.mode} (${data.testnet ? 'testnet' : 'production'})`
            : (data.actual_mode || data.mode);
        console.log(`Strategy '${data.strategy_name}' started in ${modeInfo} mode.`);
    }

    /**
     * Handles successful strategy stop.
     * @param {Object} data - Response data from server.
     */
    handleStrategyStopped(data) {
        console.log("Strategy stopped:", data);

        if (data.strategy_id) {
            const stopMode = data.actual_mode || data.mode;
            const runKey = this._makeRunningKey(data.strategy_id, stopMode);
            this.runningStrategies.delete(runKey);

            // Notify statistics module
            if (UI.statistics) {
                UI.statistics.unregisterStrategy(data.strategy_id);
            }

            // Update the UI to reflect stopped state
            this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
        }

        // Show final stats if available
        if (data.final_stats) {
            const stats = data.final_stats;
            console.log(`Final balance: ${stats.final_balance}, Trades: ${stats.total_trades}`);

            // Optionally show summary to user
            if (stats.final_balance !== undefined) {
                const pnl = stats.final_balance - (stats.initial_balance || 10000);
                const pnlPercent = ((pnl / (stats.initial_balance || 10000)) * 100).toFixed(2);
                console.log(`P&L: ${pnl.toFixed(2)} (${pnlPercent}%)`);
            }
        }
    }

    /**
     * Handles strategy run errors.
     * @param {Object} data - Error data from server.
     */
    handleStrategyRunError(data) {
        console.error("Strategy run error:", data.message, data);

        const errorCode = data.error_code;
        const missing = data.missing_exchanges;

        // Handle exchange requirement errors with detailed messages
        if (missing && missing.length > 0) {
            const exchanges = missing.join(', ');
            let message;

            switch (errorCode) {
                case 'missing_edm_data':
                    message = `Historical data not available for these exchanges:\n\n${exchanges}\n\n` +
                              `These exchanges may not be supported by the Exchange Data Manager.`;
                    break;
                case 'missing_config':
                    message = `Please configure API keys for:\n\n${exchanges}\n\n` +
                              `Go to Exchange Settings to add your credentials.`;
                    break;
                case 'invalid_exchange':
                    message = `Unknown or unsupported exchanges:\n\n${exchanges}`;
                    break;
                case 'edm_unreachable':
                    message = `Cannot validate exchange availability - data service unreachable.`;
                    break;
                default:
                    message = `This strategy requires: ${exchanges}`;
            }
            alert(message);
        } else {
            alert(`Failed to start strategy: ${data.message || data.error || 'Unknown error'}`);
        }
    }

    /**
     * Handles strategy stop errors.
     * @param {Object} data - Error data from server.
     */
    handleStrategyStopError(data) {
        console.error("Strategy stop error:", data.message);
        alert(`Failed to stop strategy: ${data.message}`);
    }

    /**
     * Handles strategy status response.
     * @param {Object} data - Status data from server.
     */
    handleStrategyStatus(data) {
        console.log("Strategy status:", data);

        if (data.running_strategies) {
            // Update running strategies map using composite keys
            this.runningStrategies.clear();
            data.running_strategies.forEach(strat => {
                const runKey = this._makeRunningKey(strat.strategy_id, strat.mode);
                this.runningStrategies.set(runKey, {
                    mode: strat.mode,
                    instance_id: strat.instance_id,
                    strategy_name: strat.strategy_name,
                    balance: strat.balance,
                    positions: strat.positions || [],
                    trade_count: strat.trade_count || 0,
                    testnet: strat.testnet,
                    circuit_breaker: strat.circuit_breaker
                });
            });

            // Update UI
            this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
        }
    }

    /**
     * Handles real-time strategy execution events from the server.
     * @param {Object} data - The event data containing strategy_id, mode, and events array.
     */
    handleStrategyEvents(data) {
        console.log("Strategy events received:", data);

        if (!data || !data.strategy_id || !data.events) {
            return;
        }

        const { strategy_id, mode, events } = data;
        const runKey = this._makeRunningKey(strategy_id, mode);
        const running = this.runningStrategies.get(runKey);

        if (!running) {
            console.warn(`Received events for strategy ${strategy_id} but it's not in runningStrategies`);
            return;
        }

        // Process each event
        let needsUIUpdate = false;
        for (const event of events) {
            switch (event.type) {
                case 'tick_complete':
                    if (typeof event.balance === 'number') {
                        running.balance = event.balance;
                        needsUIUpdate = true;
                    }
                    if (typeof event.trades === 'number') {
                        running.trade_count = event.trades;
                        needsUIUpdate = true;
                    }
                    if (typeof event.profit_loss === 'number') {
                        running.profit_loss = event.profit_loss;
                        needsUIUpdate = true;
                    }
                    break;

                case 'trade_executed':
                    console.log(`Trade executed: ${event.side} ${event.amount} @ ${event.price}`);
                    running.trade_count = (running.trade_count || 0) + 1;
                    needsUIUpdate = true;
                    // TODO: Add to trade history display
                    break;

                case 'signal_triggered':
                    console.log(`Signal triggered: ${event.signal}`);
                    // TODO: Add to activity feed
                    break;

                case 'error':
                    console.error(`Strategy error: ${event.message}`);
                    alert(`Strategy ${running.strategy_name || strategy_id} error: ${event.message}`);
                    break;

                case 'strategy_exited':
                    this.runningStrategies.delete(runKey);
                    needsUIUpdate = true;
                    break;

                default:
                    console.log(`Unknown event type: ${event.type}`, event);
            }
        }

        // Update the map and refresh UI if needed
        if (needsUIUpdate) {
            this.runningStrategies.set(runKey, running);
            this.uiManager.updateStrategiesHtml(this.dataManager.getAllStrategies());
        }
    }

    /**
     * Checks if a strategy is currently running (in any mode).
     * @param {string} strategyId - The strategy tbl_key.
     * @param {string} mode - Optional mode to check specifically.
     * @returns {boolean} - True if running.
     */
    isStrategyRunning(strategyId, mode = null) {
        if (mode) {
            return this.runningStrategies.has(this._makeRunningKey(strategyId, mode));
        }
        // Check if running in any mode
        for (const key of this.runningStrategies.keys()) {
            if (key.startsWith(strategyId + ':')) {
                return true;
            }
        }
        return false;
    }

    /**
     * Gets the running info for a strategy.
     * @param {string} strategyId - The strategy tbl_key.
     * @param {string} mode - Optional mode to get specifically.
     * @returns {Object|null} - Running info or null.
     */
    getRunningInfo(strategyId, mode = null) {
        if (mode) {
            return this.runningStrategies.get(this._makeRunningKey(strategyId, mode)) || null;
        }
        // Return first matching strategy regardless of mode
        for (const [key, value] of this.runningStrategies) {
            if (key.startsWith(strategyId + ':')) {
                return value;
            }
        }
        return null;
    }

    /**
     * Gets all running modes for a strategy.
     * @param {string} strategyId - The strategy tbl_key.
     * @returns {string[]} - Array of modes the strategy is running in.
     */
    getRunningModes(strategyId) {
        const modes = [];
        for (const [key, value] of this.runningStrategies) {
            if (key.startsWith(strategyId + ':')) {
                modes.push(value.mode);
            }
        }
        return modes;
    }

    // ========== AI Strategy Builder Wrappers ==========

    /**
     * Opens the AI strategy builder dialog.
     */
    openAIDialog() {
        this.uiManager.openAIDialog();
    }

    /**
     * Closes the AI strategy builder dialog.
     */
    closeAIDialog() {
        this.uiManager.closeAIDialog();
    }

    /**
     * Generates a strategy from natural language using AI.
     */
    async generateWithAI() {
        await this.uiManager.generateWithAI();
    }

    // ========== Public Strategy Subscription Methods ==========

    /**
     * Requests list of public strategies from the server.
     */
    requestPublicStrategies() {
        if (this.comms) {
            this.comms.sendToApp('get_public_strategies', {});
        }
    }

    /**
     * Subscribes to a public strategy.
     * @param {string} tbl_key - The strategy's tbl_key.
     */
    subscribeToStrategy(tbl_key) {
        if (!tbl_key) {
            console.error('subscribeToStrategy: No tbl_key provided');
            return;
        }
        if (this.comms) {
            this.comms.sendToApp('subscribe_strategy', { strategy_tbl_key: tbl_key });
        }
    }

    /**
     * Unsubscribes from a strategy.
     * @param {string} tbl_key - The strategy's tbl_key.
     */
    unsubscribeFromStrategy(tbl_key) {
        if (!tbl_key) {
            console.error('unsubscribeFromStrategy: No tbl_key provided');
            return;
        }

        // Check if strategy is running (in any mode)
        if (this.isStrategyRunning(tbl_key)) {
            alert('Cannot unsubscribe while strategy is running. Stop it first.');
            return;
        }

        if (!confirm('Unsubscribe from this strategy?')) {
            return;
        }

        if (this.comms) {
            this.comms.sendToApp('unsubscribe_strategy', { strategy_tbl_key: tbl_key });
        }
    }

    /**
     * Handles public strategies list from server.
     * @param {Object} data - Response containing strategies array.
     */
    handlePublicStrategies(data) {
        console.log('Received public strategies:', data);
        if (data && data.strategies) {
            this.uiManager.renderPublicStrategyModal(data.strategies);
        }
    }

    /**
     * Handles successful subscription response.
     * @param {Object} data - Response containing success info.
     */
    handleStrategySubscribed(data) {
        console.log('Strategy subscribed:', data);
        if (data && data.success) {
            // Refresh strategy list to include new subscription
            this.dataManager.fetchSavedStrategies(this.comms, this.data);

            // Close the public strategy browser modal if open
            const modal = document.getElementById('public-strategy-modal');
            if (modal) {
                modal.remove();
            }

            // Show success feedback
            if (data.strategy_name) {
                alert(`Successfully subscribed to "${data.strategy_name}"`);
            }
        }
    }

    /**
     * Handles successful unsubscription response.
     * @param {Object} data - Response containing success info.
     */
    handleStrategyUnsubscribed(data) {
        console.log('Strategy unsubscribed:', data);
        if (data && data.success) {
            // Refresh strategy list to remove unsubscribed strategy
            this.dataManager.fetchSavedStrategies(this.comms, this.data);

            // Update public strategy browser if open
            const modal = document.getElementById('public-strategy-modal');
            if (modal) {
                // Refresh the modal content
                this.requestPublicStrategies();
            }
        }
    }

    /**
     * Handles subscription error response.
     * @param {Object} data - Response containing error info.
     */
    handleSubscriptionError(data) {
        console.error('Subscription error:', data);
        const message = data && data.message ? data.message : 'Subscription operation failed';
        alert(message);
    }
}
