/**
 * Indicator Definition Object for Hurst Bands (Multi-Timeframe).
 * This object is used by the indicator manager to create and control the indicator.
 * It defines the parameters and the calculation functions.
 */
const HURST_INDICATOR = {
    name: 'Hurst',
    label: 'Hurst Bands (Multi-TF)',
    params: [
        { name: 'cycle', type: 'number', defaultValue: 30, min: 2 },
        { name: 'timeframe_mult', type: 'number', defaultValue: 5, min: 2, step: 1 },
    ],
    // The output is { topBand, bottomBand, topBand_h, bottomBand_h }
    calculateFull: calculateFullHurst,
    createRealtime: createRealtimeHurstCalculator,
};

// --- Helper Functions (private to this file) ---

/**
 * Aggregates candle data into a higher timeframe.
 * @param {Array<Object>} data - The original candle data.
 * @param {number} multiplier - The timeframe multiplier (e.g., 5 for 5-minute candles from 1-minute data).
 * @returns {Array<Object>} A new array of aggregated candle objects.
 */
function _aggregateCandles(data, multiplier) {
    if (multiplier <= 1) return data;

    const aggregatedData = [];
    for (let i = 0; i < data.length; i += multiplier) {
        const chunk = data.slice(i, i + multiplier);
        if (chunk.length > 0) {
            const newCandle = {
                open: chunk[0].open,
                high: Math.max(...chunk.map(c => c.high)),
                low: Math.min(...chunk.map(c => c.low)),
                close: chunk[chunk.length - 1].close,
                // The timestamp of the new candle corresponds to the end of the period.
                time: chunk[chunk.length - 1].time,
            };
            aggregatedData.push(newCandle);
        }
    }
    return aggregatedData;
}


/**
 * Calculates RMA (Relative Moving Average), a type of EMA.
 * @param {number[]} series - An array of numbers.
 * @param {number} period - The smoothing period.
 * @returns {number[]} The calculated RMA series.
 */
function _calculateRMA(series, period) {
    if (series.length < period) return [];
    const alpha = 1 / period;
    let rma = [];
    let sum = 0;
    for (let i = 0; i < period; i++) {
        sum += series[i];
    }
    rma.push(sum / period);
    for (let i = period; i < series.length; i++) {
        const val = alpha * series[i] + (1 - alpha) * rma[rma.length - 1];
        rma.push(val);
    }
    return rma;
}

/**
 * Calculates ATR (Average True Range).
 * @param {Array<Object>} data - The full candle data.
 * @param {number} period - The ATR period.
 * @returns {number[]} The calculated ATR series.
 */
function _calculateATR(data, period) {
    if (data.length < period) return [];
    let tr_series = [data[0].high - data[0].low];
    for (let i = 1; i < data.length; i++) {
        const h = data[i].high;
        const l = data[i].low;
        const prev_c = data[i - 1].close;
        const tr = Math.max(h - l, Math.abs(h - prev_c), Math.abs(l - prev_c));
        tr_series.push(tr);
    }
    return _calculateRMA(tr_series, period);
}

/**
 * A generic function to calculate a single set of Hurst Bands.
 * This is the core calculation logic.
 * @param {Array<Object>} data - An array of candle objects for a specific timeframe.
 * @param {number} cycle - The cycle length for this calculation.
 * @param {number} atr_mult - The ATR multiplier for this calculation.
 * @returns {Object} An object containing two arrays: { topBand: [...], bottomBand: [...] }.
 */
function _calculateSingleBandSet(data, cycle, atr_mult) {
    const mcl = Math.floor(cycle / 2);
    const mcl_2 = Math.floor(mcl / 2);

    if (data.length < cycle + mcl_2) {
        return { topBand: [], bottomBand: [] };
    }
    
    const closePrices = data.map(d => d.close);
    const ma_mcl_full = _calculateRMA(closePrices, mcl);
    const atr_full = _calculateATR(data, mcl);

    const topBand = [];
    const bottomBand = [];
    const startIndex = mcl - 1 + mcl_2;

    for (let i = startIndex; i < data.length; i++) {
        const rma_atr_base_index = i - (mcl - 1);
        const center_ma_index = rma_atr_base_index - mcl_2;
        
        if (center_ma_index >= 0 && rma_atr_base_index >= 0) {
            const center = ma_mcl_full[center_ma_index];
            const offset = atr_full[rma_atr_base_index] * atr_mult;

            if (center !== undefined && offset !== undefined) {
                topBand.push({ time: data[i].time, value: center + offset });
                bottomBand.push({ time: data[i].time, value: center - offset });
            }
        }
    }
    return { topBand, bottomBand };
}

// --- Main Calculation Functions ---

/**
 * Calculates both primary and higher-timeframe Hurst Bands for an entire dataset.
 * @param {Array<Object>} data - An array of candle objects.
 * @param {Object} params - An object with { cycle, timeframe_mult }.
 * @returns {Object} An object containing four arrays: { topBand, bottomBand, topBand_h, bottomBand_h }.
 */
function calculateFullHurst(data, params) {
    const { cycle, timeframe_mult } = params;

    // 1. Calculate Primary Bands (e.g., 1-minute)
    const primaryBands = _calculateSingleBandSet(data, cycle, 1.8);

    // 2. Aggregate candles to higher timeframe (e.g., 5-minute)
    const higherTfData = _aggregateCandles(data, timeframe_mult);

    // 3. Calculate Higher Timeframe Bands
    const higherTFBandsRaw = _calculateSingleBandSet(higherTfData, cycle, 1.9);

    // 4. Align higher timeframe results back to the primary timeframe for plotting
    const higherTfResults = new Map(higherTFBandsRaw.topBand.map((p, i) => [
        p.time,
        { top: p.value, bottom: higherTFBandsRaw.bottomBand[i].value }
    ]));
    
    const topBand_h = [];
    const bottomBand_h = [];
    let lastKnownTop = null;
    let lastKnownBottom = null;

    for (const candle of data) {
        if (higherTfResults.has(candle.time)) {
            const bands = higherTfResults.get(candle.time);
            lastKnownTop = bands.top;
            lastKnownBottom = bands.bottom;
        }
        // Carry forward the last known value until a new one is calculated
        if (lastKnownTop !== null) {
            topBand_h.push({ time: candle.time, value: lastKnownTop });
            bottomBand_h.push({ time: candle.time, value: lastKnownBottom });
        }
    }

    return {
        topBand: primaryBands.topBand,
        bottomBand: primaryBands.bottomBand,
        topBand_h,
        bottomBand_h,
    };
}

/**
 * Creates a stateful Hurst calculator for real-time updates.
 * @param {Object} params - An object with { cycle, timeframe_mult }.
 * @returns {Object} A calculator object with `update` and `prime` methods.
 */
function createRealtimeHurstCalculator(params) {
    const { cycle, timeframe_mult } = params;
    // Buffer needs to be large enough to contain enough aggregated candles for a valid calculation.
    const minHigherTfCandles = cycle + Math.floor(Math.floor(cycle / 2) / 2);
    const bufferSize = minHigherTfCandles * timeframe_mult * 2; // Use a safe buffer size
    let buffer = [];

    return {
        update: function(candle) {
            buffer.push(candle);
            if (buffer.length > bufferSize) {
                buffer.shift();
            }
            
            // Check if there's enough data for at least one calculation on the higher timeframe.
            const requiredLength = minHigherTfCandles * timeframe_mult;
            if (buffer.length < requiredLength) {
                return null;
            }

            const result = calculateFullHurst(buffer, params);
            
            if (result.topBand.length > 0 && result.topBand_h.length > 0) {
                return { 
                    topBand: result.topBand[result.topBand.length - 1], 
                    bottomBand: result.bottomBand[result.bottomBand.length - 1],
                    topBand_h: result.topBand_h[result.topBand_h.length - 1],
                    bottomBand_h: result.bottomBand_h[result.bottomBand_h.length - 1],
                };
            }
            return null;
        },
        prime: function(historicalCandles) {
            buffer = historicalCandles.slice(-bufferSize);
        }
    };
}
