/**
 * Y-Axis Zoom Module for Trading Chart
 * Provides TradingView-style vertical zoom functionality
 *
 * Features:
 * - Click and drag on price scale area to zoom Y-axis
 * - Zoom centered at chart middle (like TradingView/Lightweight Charts)
 * - Dynamic grid line adjustment during zoom
 * - Smooth zoom animation
 * - Auto-reset on double-click
 * - Respects asset-specific price intervals
 *
 * Usage: Include this file after app.js and visible-zone.js
 * The module will automatically attach to window.chart
 */

class YAxisZoom {
  constructor(chart) {
    this.chart = chart;
    this.isActive = false;
    this.isDragging = false;
    this.startY = 0;
    this.lastY = 0;

    // Zoom state
    this.zoomFactor = 1.0; // 1.0 = normal, > 1.0 = zoomed out
    // Zoom IN is bounded at normal (1.0), zoom OUT is unlimited
    this.minZoom = 1.0; // Cannot zoom in past normal state
    this.maxZoom = 100000.0; // Virtually unlimited zoom out

    // Price range override
    this.customPriceRange = null;
    this.baseRange = null; // Store original range for reference

    // Original method backup
    this.originalGetPriceRange = null;

    // Price scale zone detection
    this.priceScaleWidth = 55; // Width of price scale area on right

    // Animation state
    this.animating = false;
    this.animationStartTime = 0;
    this.animationDuration = 200; // ms
    this.animationStartRange = null;
    this.animationTargetRange = null;

    // Sensitivity settings - much lower for gradual zoom
    this.zoomSensitivity = 0.007; // Very slow zoom response to drag

    // Smooth interpolation for animated grid lines
    this.targetZoomFactor = 1.0;
    this.smoothingFactor = 0.08; // Higher = faster animation
    this.isInterpolating = false;

    // Fixed price anchoring - grid lines at absolute price multiples
    // This eliminates jumps because lines are anchored to real prices, not view-relative positions
    this.currentInterval = null;

    // Double-click reset
    this.lastClickTime = 0;
    this.doubleClickThreshold = 300; // ms

    console.log("✅ Y-Axis Zoom Module initialized");
  }

  /**
   * Activate the Y-axis zoom functionality
   */
  activate() {
    if (this.isActive) return;
    if (!this.chart || !this.chart.canvas) {
      console.error("❌ Y-Axis Zoom: Chart or canvas not available");
      return;
    }

    this.isActive = true;

    // Store original getPriceRange method
    this.originalGetPriceRange = this.chart.getPriceRange.bind(this.chart);

    // Override getPriceRange to apply zoom
    this.chart.getPriceRange = () => this.getZoomedPriceRange();

    // Setup event listeners for price scale area
    this.setupEventListeners();

    // Add visual cursor indicator styles
    this.addStyles();

    console.log("✅ Y-Axis Zoom activated");
  }

  /**
   * Deactivate the Y-axis zoom functionality
   */
  deactivate() {
    if (!this.isActive) return;

    // Restore original getPriceRange
    if (this.originalGetPriceRange) {
      this.chart.getPriceRange = this.originalGetPriceRange;
    }

    // Remove event listeners
    this.removeEventListeners();

    this.isActive = false;
    this.customPriceRange = null;
    this.zoomFactor = 1.0;

    console.log("✅ Y-Axis Zoom deactivated");
  }

  /**
   * Setup mouse event listeners
   */
  setupEventListeners() {
    this.boundMouseDown = this.onMouseDown.bind(this);
    this.boundMouseMove = this.onMouseMove.bind(this);
    this.boundMouseUp = this.onMouseUp.bind(this);
    this.boundMouseLeave = this.onMouseLeave.bind(this);

    this.chart.canvas.addEventListener("mousedown", this.boundMouseDown);
    this.chart.canvas.addEventListener("mousemove", this.boundMouseMove);
    this.chart.canvas.addEventListener("mouseup", this.boundMouseUp);
    this.chart.canvas.addEventListener("mouseleave", this.boundMouseLeave);

    // Also listen on window for mouseup in case user releases outside canvas
    window.addEventListener("mouseup", this.boundMouseUp);
  }

  /**
   * Remove event listeners
   */
  removeEventListeners() {
    if (this.chart && this.chart.canvas) {
      this.chart.canvas.removeEventListener("mousedown", this.boundMouseDown);
      this.chart.canvas.removeEventListener("mousemove", this.boundMouseMove);
      this.chart.canvas.removeEventListener("mouseup", this.boundMouseUp);
      this.chart.canvas.removeEventListener("mouseleave", this.boundMouseLeave);
    }
    window.removeEventListener("mouseup", this.boundMouseUp);
  }

  /**
   * Add CSS styles for cursor indication
   */
  addStyles() {
    if (document.getElementById("y-axis-zoom-styles")) return;

    const style = document.createElement("style");
    style.id = "y-axis-zoom-styles";
    style.textContent = `
            .y-axis-zoom-active {
                cursor: row-resize !important;
                user-select: none !important;
            }
        `;
    document.head.appendChild(style);
  }

  /**
   * Check if mouse is in the price scale zone (right side of chart)
   */
  isInPriceScaleZone(mouseX) {
    if (!this.chart) return false;

    const chartArea = this.chart.getChartArea();
    const priceScaleLeft = chartArea.left + chartArea.width;
    const priceScaleRight = this.chart.canvas.width;

    return mouseX >= priceScaleLeft && mouseX <= priceScaleRight;
  }

  /**
   * Get chart center Y position
   */
  getChartCenterY() {
    const chartArea = this.chart.getChartArea();
    return chartArea.top + chartArea.height / 2;
  }

  /**
   * Get price at chart center
   */
  getCenterPrice() {
    const range = this.originalGetPriceRange();
    return (range.min + range.max) / 2;
  }

  /**
   * Mouse down handler
   */
  onMouseDown(e) {
    const rect = this.chart.canvas.getBoundingClientRect();
    const mouseX = e.clientX - rect.left;
    const mouseY = e.clientY - rect.top;

    // Check for double-click to reset zoom
    const now = Date.now();
    if (now - this.lastClickTime < this.doubleClickThreshold && this.isInPriceScaleZone(mouseX)) {
      this.resetZoom();
      this.lastClickTime = 0;
      return;
    }
    this.lastClickTime = now;

    // Only activate drag if in price scale zone
    if (this.isInPriceScaleZone(mouseX)) {
      this.isDragging = true;
      this.startY = mouseY;
      this.lastY = mouseY;

      // Store base range for this drag session
      this.baseRange = this.originalGetPriceRange();

      // Add active class to canvas
      this.chart.canvas.classList.add("y-axis-zoom-active");

      e.preventDefault();
      e.stopPropagation();
    }
  }

  /**
   * Mouse move handler
   */
  onMouseMove(e) {
    const rect = this.chart.canvas.getBoundingClientRect();
    const mouseX = e.clientX - rect.left;
    const mouseY = e.clientY - rect.top;

    // No cursor change on hover - only when dragging

    // Handle dragging for zoom - INSTANT response like mouse wheel
    if (this.isDragging) {
      const deltaY = mouseY - this.lastY;
      this.lastY = mouseY;

      // Calculate zoom change
      const zoomChange = deltaY * this.zoomSensitivity;

      // Update zoom factor instantly (no interpolation)
      this.zoomFactor = Math.max(this.minZoom, Math.min(this.maxZoom, this.zoomFactor + zoomChange));
      this.targetZoomFactor = this.zoomFactor; // Keep in sync

      // Update immediately without interpolation
      this.updateZoomedRange();

      e.preventDefault();
      e.stopPropagation();
    }
  }

  /**
   * Mouse up handler
   */
  onMouseUp(e) {
    if (this.isDragging) {
      this.isDragging = false;
      this.chart.canvas.classList.remove("y-axis-zoom-active");
    }
  }

  /**
   * Mouse leave handler
   */
  onMouseLeave(e) {
    if (this.isDragging) {
      this.isDragging = false;
      this.chart.canvas.classList.remove("y-axis-zoom-active");
    }
  }

  /**
   * Get grid line limits directly from main chart configuration
   * Reads priceLineCount (min/target) and maxPriceLines (max) from chart
   */
  getGridLineLimits() {
    // Read limits directly from main chart object
    const minLines = this.chart.priceLineCount || 7; // Default 7 from app.js
    const maxLines = this.chart.maxPriceLines || 10; // Default 10 from app.js

    return { minLines, maxLines };
  }

  /**
   * Update the custom price range based on current zoom factor
   * Uses Fixed Price Anchoring - grid lines are always at absolute price multiples
   * This eliminates jumps because lines are anchored to real prices, not view-relative positions
   */
  updateZoomedRange() {
    if (!this.baseRange) {
      this.baseRange = this.originalGetPriceRange();
    }

    const originalRange = this.baseRange.max - this.baseRange.min;
    const centerPrice = (this.baseRange.min + this.baseRange.max) / 2;

    // Get asset-specific intervals
    const assetConfig = this.getAssetConfig();
    const availableIntervals = assetConfig?.priceInterval || [this.baseRange.interval];

    // Get grid line limits from main chart configuration
    const { minLines, maxLines } = this.getGridLineLimits();

    // Calculate new range based on zoom factor (unlimited zoom out)
    const newRange = originalRange * this.zoomFactor;
    const halfNewRange = newRange / 2;

    // Calculate range bounds FIRST (before interval selection)
    const newMin = centerPrice - halfNewRange;
    const newMax = centerPrice + halfNewRange;

    // Find best interval that keeps lines within limits
    let targetInterval = this.baseRange.interval;

    // First try available intervals
    let foundInterval = false;
    for (const testInterval of availableIntervals) {
      const lineCount = Math.floor(newRange / testInterval) + 1;
      if (lineCount >= minLines && lineCount <= maxLines) {
        targetInterval = testInterval;
        foundInterval = true;
        break;
      }
    }

    // If no predefined interval works (zoomed out too far), calculate dynamic interval
    if (!foundInterval) {
      const largestInterval = availableIntervals[availableIntervals.length - 1];
      const targetLines = Math.floor((minLines + maxLines) / 2);
      targetInterval = newRange / (targetLines - 1);
      const multiplier = Math.ceil(targetInterval / largestInterval);
      targetInterval = largestInterval * multiplier;
    }

    this.currentInterval = targetInterval;

    // FIXED PRICE ANCHORING: Calculate the first grid line position as nearest price multiple
    // This ensures grid lines are ALWAYS at exact price multiples (e.g., 100.00, 100.02, 100.04)
    // regardless of the view range, eliminating position jumps when interval changes
    const anchoredMin = Math.floor(newMin / targetInterval) * targetInterval;
    const anchoredMax = Math.ceil(newMax / targetInterval) * targetInterval;
    
    // Calculate line count based on anchored positions
    const finalLineCount = Math.round((anchoredMax - anchoredMin) / targetInterval) + 1;

    this.customPriceRange = {
      ...this.baseRange,
      min: newMin,
      max: newMax,
      interval: targetInterval,
      lineCount: finalLineCount,
      // Pass anchored values for grid line calculation
      anchoredMin: anchoredMin,
      anchoredMax: anchoredMax,
    };
  }

  /**
   * Get anchored grid line positions for rendering
   * Returns an array of exact price values where grid lines should be drawn
   * These are always at fixed price multiples, eliminating position jumps
   */
  getAnchoredGridLines() {
    if (!this.customPriceRange) return [];
    
    const { anchoredMin, anchoredMax, interval } = this.customPriceRange;
    if (anchoredMin === undefined || anchoredMax === undefined) return [];
    
    const lines = [];
    for (let price = anchoredMin; price <= anchoredMax; price += interval) {
      lines.push(price);
    }
    return lines;
  }

  /**
   * Get asset configuration (matches chart's method)
   */
  getAssetConfig() {
    const assetName = this.chart.assetName || document.querySelector(".asset-name")?.textContent;
    if (typeof getAssetConfig === "function") {
      return getAssetConfig(assetName);
    }
    return null;
  }

  /**
   * Get the zoomed price range (called by overridden getPriceRange)
   */
  getZoomedPriceRange() {
    // If we have a custom zoomed range, use it
    if (this.customPriceRange && this.zoomFactor !== 1.0) {
      // Get current visible prices to ensure they're still in range
      const originalRange = this.originalGetPriceRange();

      // Ensure current price is within view
      if (this.chart.currentPrice < this.customPriceRange.min || this.chart.currentPrice > this.customPriceRange.max) {
        // Price has moved outside zoomed view - allow visible-zone to handle
        // but still apply zoom factor
        this.baseRange = originalRange;
        this.updateZoomedRange();
      }

      return this.customPriceRange;
    }

    // Otherwise use original range
    return this.originalGetPriceRange();
  }

  /**
   * Start smooth zoom interpolation
   */
  startSmoothZoom() {
    if (this.isInterpolating) return;

    this.isInterpolating = true;
    this.smoothZoomLoop();
  }

  /**
   * Smooth zoom interpolation loop
   */
  smoothZoomLoop() {
    // Interpolate current zoom towards target
    const diff = this.targetZoomFactor - this.zoomFactor;

    // If close enough, snap to target
    if (Math.abs(diff) < 0.001) {
      this.zoomFactor = this.targetZoomFactor;
      this.isInterpolating = false;
      this.updateZoomedRange();
      return;
    }

    // Apply smooth interpolation
    this.zoomFactor += diff * this.smoothingFactor;
    this.updateZoomedRange();

    // Continue animation
    requestAnimationFrame(() => this.smoothZoomLoop());
  }

  /**
   * Reset zoom to default
   */
  resetZoom() {
    this.targetZoomFactor = 1.0;
    this.animateZoomReset();
  }

  /**
   * Animate zoom reset
   */
  animateZoomReset() {
    const startZoom = this.zoomFactor;
    const targetZoom = 1.0;
    const duration = 400; // ms - longer for smoother reset
    const startTime = performance.now();

    const animate = (currentTime) => {
      const elapsed = currentTime - startTime;
      const progress = Math.min(elapsed / duration, 1);

      // Ease out cubic for smooth deceleration
      const eased = 1 - Math.pow(1 - progress, 3);

      this.zoomFactor = startZoom + (targetZoom - startZoom) * eased;
      this.targetZoomFactor = this.zoomFactor;

      if (progress >= 1) {
        this.zoomFactor = 1.0;
        this.targetZoomFactor = 1.0;
        this.customPriceRange = null;
        this.baseRange = null;
        console.log("✅ Y-Axis Zoom reset complete");
      } else {
        this.updateZoomedRange();
        requestAnimationFrame(animate);
      }
    };

    requestAnimationFrame(animate);
  }

  /**
   * Set zoom level programmatically
   * @param {number} factor - Zoom factor (1.0 = default, < 1.0 = zoomed in, > 1.0 = zoomed out)
   */
  setZoom(factor) {
    this.targetZoomFactor = Math.max(this.minZoom, Math.min(this.maxZoom, factor));
    this.baseRange = this.originalGetPriceRange();
    this.startSmoothZoom();
  }

  /**
   * Get current zoom level
   */
  getZoom() {
    return this.zoomFactor;
  }

  /**
   * Check if currently zoomed
   */
  isZoomed() {
    return this.zoomFactor !== 1.0;
  }
}

// Auto-initialization when chart is ready
(function initYAxisZoom() {
  const checkChart = () => {
    if (window.chart) {
      // Create and activate Y-axis zoom
      window.chart.yAxisZoom = new YAxisZoom(window.chart);
      window.chart.yAxisZoom.activate();

      // Make class available globally for debugging
      window.YAxisZoom = YAxisZoom;

      console.log("✅ Y-Axis Zoom module loaded and activated");
    } else {
      // Retry after a short delay
      setTimeout(checkChart, 500);
    }
  };

  // Start checking for chart availability
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", () => setTimeout(checkChart, 1000));
  } else {
    setTimeout(checkChart, 1000);
  }
})();
