/**
 * Visible Zone Auto-Scaling Algorithm - STRICT VACUUM-FIT & SHADOW-FREE
 * 
 * This module ensures the chart always occupies maximum vertical space
 * by strictly mapping market extremes (reversal boxes) to the 100px and 50px margins.
 */

class VisibleZoneAlgorithm {
    constructor(chart) {
        this.chart = chart;
        this.isActive = true;

        // Configuration
        this.smoothingFactor = 0.25; // Fast ~0.15s "breathing" shifts
        this.targetLineCount = 7;

        // State for Animated Transitions
        this.currentMin = 0;
        this.currentMax = 0;
        this.lastInterval = null;

        this.isInitialized = false;

        // Vertical Grid State
        this.verticalSpacing = 100; // Pixels between time lines
        this.lastPanOffset = null;
        this.lastFrameTimestamp = 0;

        console.log("✅ Visible Zone: Strict Shadow-Free Vacuum-Fit active.");
    }

    /**
     * Get stable vertical grid points based on panOffset
     */
    getVerticalGrid() {
        const pan = this.chart.panOffset || 0;
        const area = this.chart.getChartArea();

        // Use a fixed spacing that is stable
        const spacing = this.verticalSpacing;

        // Calculate the base index of the first visible line
        // This ensures the ID remains stable even as the offset changes
        const gridBaseIndex = Math.floor(pan / spacing);
        const startX = (pan % spacing);

        const lines = [];

        // Generate lines across the width with stable IDs
        // We go from -1 to width/spacing + 1 to ensure smooth entry/exit
        for (let i = -1; i < (area.width / spacing) + 2; i++) {
            const x = startX + (i * spacing);
            const stableId = gridBaseIndex - i;

            lines.push({
                id: stableId,
                offset: Number(x.toFixed(2))
            });
        }

        return lines;
    }

    /**
     * Get the optimal price range for the currently visible candles
     */
    getOptimalRange() {
        if (!this.chart.candleData || this.chart.candleData.length === 0) return null;

        const visibleCandles = this.chart.getVisibleCandles();
        if (!visibleCandles || visibleCandles.length === 0) {
            return this.getFallbackRange();
        }

        // 1. DYNAMIC EXTREME TRACKING (REVERSAL PRIORITY)
        let high = -Infinity;
        let low = Infinity;

        // Reversal boxes are the primary targets for margins
        if (this.chart.reversalBoxes && this.chart.reversalBoxes.length > 0) {
            this.chart.reversalBoxes.forEach(box => {
                if (box.type === 'top' && box.price > high) high = box.price;
                if (box.type === 'bottom' && box.price < low) low = box.price;
            });
        }

        // Fallback to wicks if no reversal boxes are visible
        if (high === -Infinity || low === Infinity) {
            visibleCandles.forEach(candle => {
                if (!candle) return;
                let cHigh = candle.isComplete ? candle.high : Math.max(candle.open, this.chart.runningHigh || candle.open);
                let cLow = candle.isComplete ? candle.low : Math.min(candle.open, this.chart.runningLow || candle.open);
                if (cHigh > high) high = cHigh;
                if (cLow < low) low = cLow;
            });
        }

        // Always include current live price
        if (this.chart.isRunningCandleVisible && this.chart.isRunningCandleVisible()) {
            if (this.chart.currentPrice > high) high = this.chart.currentPrice;
            if (this.chart.currentPrice < low) low = this.chart.currentPrice;
        }

        if (!isFinite(high) || !isFinite(low)) return null;

        // 2. STRICT MARGIN MAPPING MATH
        // We want 'high' to be at 100px from top and 'low' to be at 50px from bottom.
        const chartHeight = this.chart.canvas.height / (window.devicePixelRatio || 1);
        const topMargin = 100;
        const bottomMargin = 50;
        const effectiveHeight = chartHeight - topMargin - bottomMargin;

        // 🚀 GEOMETRY FAILSAFE: Abort calculation if CSS layout hasn't painted the canvas height yet
        // A negative or zero effective height permanently mathematically inverts the camera bounds!
        if (effectiveHeight <= 0) return this.getFallbackRange();

        // Safety: If high/low are too close, add tiny buffer to avoid division by zero
        if (high - low < 0.0000001) {
            high += 0.01;
            low -= 0.01;
        }

        // Ideal Scale (pixels per price)
        const idealScale = effectiveHeight / (high - low);
        const targetMax = high + (topMargin / idealScale);
        const targetMin = low - (bottomMargin / idealScale);

        // 3. INITIALIZATION
        if (!this.isInitialized) {
            this.currentMin = targetMin;
            this.currentMax = targetMax;
            this.isInitialized = true;
        }

        // 4. ANIMATED BOUNDARY TRANSITION (FRAME-THROTTLED & DECOUPLED)
        // We ensure smoothing only happens once per frame regardless of call count.
        const frameId = Math.floor(performance.now() / 16.6); // Stable ~60fps frame buckets
        if (frameId !== this.lastFrameTimestamp) {
            // DECOUPLED EXPANSION: Only expand if price is pushing the margin
            // This prevents symmetrical expansion when only one side is pushed.

            // TOP EXPANSION (Pushing Max)
            if (targetMax > this.currentMax) {
                this.currentMax += (targetMax - this.currentMax) * this.smoothingFactor;
            } else {
                // Smooth contraction (Breathing in)
                this.currentMax += (targetMax - this.currentMax) * (this.smoothingFactor * 0.5);
            }

            // BOTTOM EXPANSION (Pushing Min)
            if (targetMin < this.currentMin) {
                this.currentMin += (targetMin - this.currentMin) * this.smoothingFactor;
            } else {
                // Smooth contraction (Breathing in)
                this.currentMin += (targetMin - this.currentMin) * (this.smoothingFactor * 0.5);
            }

            this.lastFrameTimestamp = frameId;
        }

        // 5. INTERVAL SELECTION (HYSTERESIS-AWARE)
        const currentRange = this.currentMax - this.currentMin;
        const assetConfig = this.chart.getAssetConfig();
        const availableIntervals = assetConfig?.priceInterval || [0.001, 0.002, 0.005, 0.01, 0.02, 0.04, 0.05, 0.1, 0.2, 0.25, 0.5];

        let selectedInterval = availableIntervals[availableIntervals.length - 1];
        // Aim for roughly 6-7 intervals to fill the screen
        for (const interval of availableIntervals) {
            if (interval * 6.5 >= currentRange) {
                selectedInterval = interval;
                break;
            }
        }

        // Hysteresis: Stay locked to the current interval if it's "OK"
        if (this.lastInterval && this.lastInterval !== selectedInterval) {
            const ratio = selectedInterval / this.lastInterval;
            if (ratio > 0.75 && ratio < 1.35) {
                selectedInterval = this.lastInterval;
            }
        }
        this.lastInterval = selectedInterval;

        // 6. STABLE GRID ANCHORING
        // Anchor the grid center to the cleanest multiple of the interval
        const currentMid = (this.currentMin + this.currentMax) / 2;
        const gridCenterPrice = Math.round(currentMid / selectedInterval) * selectedInterval;

        return {
            min: this.currentMin,
            max: this.currentMax,
            interval: selectedInterval,
            centerPrice: gridCenterPrice,
            lineCount: 7,
            tickSize: selectedInterval / 10
        };
    }

    getFallbackRange() {
        const p = this.chart.currentPrice;
        const interval = 0.020;
        return {
            min: p - (interval * 3.5),
            max: p + (interval * 3.5),
            interval: interval,
            centerPrice: p,
            lineCount: 7,
            tickSize: 0.001
        };
    }
}

// Global Integration
(function injectVisibleZone() {
    const startInjection = () => {
        if (typeof SmoothTradingChart !== 'undefined') {
            const proto = SmoothTradingChart.prototype;
            const originalGetPriceRange = proto.getPriceRange;

            proto.getPriceRange = function () {
                if (!this.visibleZone) {
                    this.visibleZone = new VisibleZoneAlgorithm(this);
                    this.topMargin = 100;
                    this.bottomMargin = 50;
                }

                if (this.yAxisZoom && typeof this.yAxisZoom.isZoomed === 'function') {
                    if (this.yAxisZoom.zoomFactor !== 1.0) {
                        return originalGetPriceRange.call(this);
                    }
                }

                if (this.visibleZone.isActive) {
                    const optimal = this.visibleZone.getOptimalRange();
                    if (optimal) return optimal;
                }

                return originalGetPriceRange.call(this);
            };

            console.log("🚀 [Visible-Zone] Strict Vacuum-Fit Active - Shadow-Free Mode.");
        } else {
            setTimeout(startInjection, 100);
        }
    };

    startInjection();
})();
