/**
 * Indicator Definition Object for Bollinger Bands (BB).
 * This object is used by the indicator manager to create and control the indicator.
 */
const BB_INDICATOR = {
    name: 'BB',
    label: 'Bollinger Bands (3 Sets)',
    usesBaseData: true, // This indicator needs the raw 1m data for its own aggregation
    params: [
        { name: 'timeframe', type: 'number', defaultValue: 1440, min: 1, label: 'Timeframe (min)' },
    ],
    // Hardcoded internal parameters, no longer exposed to the user.
    internalParams: {
        bb1_len_upper: 20, bb1_std_upper: 1.6, bb1_len_lower: 20, bb1_std_lower: 1.6,
        bb2_len_upper: 20, bb2_std_upper: 2.4, bb2_len_lower: 20, bb2_std_lower: 2.4,
        bb3_len_upper: 20, bb3_std_upper: 3.3, bb3_len_lower: 20, bb3_std_lower: 3.3,
    },
    calculateFull: calculateFullBollingerBands,
};

/**
 * Calculates all three sets of Bollinger Bands for the entire dataset.
 * @param {Array<Object>} data An array of candle objects with 'close' and 'time' properties.
 * @param {Object} params An object containing the indicator's parameters (only timeframe from UI).
 * @returns {Object} An object containing arrays of data points for each band.
 */
function calculateFullBollingerBands(data, params) {
    const { timeframe } = params;
    const internal = BB_INDICATOR.internalParams;

    // First, aggregate the base data into the desired timeframe for the BB calculation.
    const aggregatedData = aggregateCandles(data, timeframe);
    if (aggregatedData.length === 0) {
        return { bb1_upper: [], bb1_lower: [], bb2_upper: [], bb2_lower: [], bb3_upper: [], bb3_lower: [] };
    }

    // Calculate each set of bands using the aggregated data
    const bb1 = calculateBands(aggregatedData, internal.bb1_len_upper, internal.bb1_std_upper, internal.bb1_len_lower, internal.bb1_std_lower);
    const bb2 = calculateBands(aggregatedData, internal.bb2_len_upper, internal.bb2_std_upper, internal.bb2_len_lower, internal.bb2_std_lower);
    const bb3 = calculateBands(aggregatedData, internal.bb3_len_upper, internal.bb3_std_upper, internal.bb3_len_lower, internal.bb3_std_lower);

    // Extend the last point of each band to the latest candle time
    const lastCandleTime = data[data.length - 1].time;
    const extendLine = (bandData) => {
        if (bandData.length > 0) {
            const lastPoint = bandData[bandData.length - 1];
            if (lastPoint.time < lastCandleTime) {
                bandData.push({ ...lastPoint, time: lastCandleTime });
            }
        }
        return bandData;
    };

    return {
        bb1_upper: extendLine(bb1.upper), bb1_lower: extendLine(bb1.lower),
        bb2_upper: extendLine(bb2.upper), bb2_lower: extendLine(bb2.lower),
        bb3_upper: extendLine(bb3.upper), bb3_lower: extendLine(bb3.lower),
    };
}

/**
 * Helper function to calculate a single set of upper and lower Bollinger Bands.
 * @returns {{upper: Array<Object>, lower: Array<Object>}}
 */
function calculateBands(data, upperLength, upperStdDev, lowerLength, lowerStdDev) {
    const upperBand = [];
    const lowerBand = [];

    // Calculate Upper Band
    for (let i = upperLength - 1; i < data.length; i++) {
        const slice = data.slice(i - upperLength + 1, i + 1);
        const sma = slice.reduce((sum, candle) => sum + candle.close, 0) / upperLength;
        const stdDev = Math.sqrt(slice.reduce((sum, candle) => sum + Math.pow(candle.close - sma, 2), 0) / upperLength);
        upperBand.push({
            time: slice[slice.length - 1].time,
            value: sma + (stdDev * upperStdDev)
        });
    }

    // Calculate Lower Band
    for (let i = lowerLength - 1; i < data.length; i++) {
        const slice = data.slice(i - lowerLength + 1, i + 1);
        const sma = slice.reduce((sum, candle) => sum + candle.close, 0) / lowerLength;
        const stdDev = Math.sqrt(slice.reduce((sum, candle) => sum + Math.pow(candle.close - sma, 2), 0) / lowerLength);
        lowerBand.push({
            time: slice[slice.length - 1].time,
            value: sma - (stdDev * lowerStdDev)
        });
    }

    return { upper: upperBand, lower: lowerBand };
}
