import {
  DeesElement,
  customElement,
  property,
  state,
  type TemplateResult,
} from '@design.estate/dees-element';

import * as domtools from '@design.estate/dees-domtools';
import { demoFunc } from './demo.js';
import { chartAreaStyles } from './styles.js';
import { renderChartArea } from './template.js';

import type ApexCharts from 'apexcharts';
import { DeesServiceLibLoader } from '../../../services/index.js';

declare global {
  interface HTMLElementTagNameMap {
    'dees-chart-area': DeesChartArea;
  }
}

@customElement('dees-chart-area')
export class DeesChartArea extends DeesElement {
  public static demo = demoFunc;
  public static demoGroup = 'Chart';

  // instance
  @state()
  accessor chart: ApexCharts;

  @property()
  accessor label: string = 'Untitled Chart';

  @property({ type: Array })
  accessor series: ApexAxisChartSeries = [];

  // Override getter to return internal chart data
  get chartSeries(): ApexAxisChartSeries {
    return this.internalChartData.length > 0 ? this.internalChartData : this.series;
  }

  @property({ attribute: false })
  accessor yAxisFormatter: (value: number) => string = (val) => `${val} Mbps`;

  @property({ type: Number })
  accessor rollingWindow: number = 0; // 0 means no rolling window

  @property({ type: Boolean })
  accessor realtimeMode: boolean = false;

  @property({ type: String })
  accessor yAxisScaling: 'fixed' | 'dynamic' | 'percentage' = 'dynamic';

  @property({ type: Number })
  accessor yAxisMax: number = 100; // Used when yAxisScaling is 'fixed' or 'percentage'

  @property({ type: Number })
  accessor autoScrollInterval: number = 1000; // Auto-scroll interval in milliseconds (0 to disable)

  private resizeObserver: ResizeObserver;
  private resizeTimeout: number;
  private internalChartData: ApexAxisChartSeries = [];
  private autoScrollTimer: number | null = null;
  private readonly DEBUG_RESIZE = false; // Set to true to enable resize debugging
  
  // Chart color schemes
  private readonly CHART_COLORS = {
    dark: [
      'hsl(217.2 91.2% 59.8%)', // Blue
      'hsl(173.4 80.4% 40%)',   // Teal
      'hsl(280.3 87.4% 66.7%)', // Purple
      'hsl(24.6 95% 53.1%)',    // Orange
    ],
    light: [
      'hsl(222.2 47.4% 51.2%)', // Blue (shadcn primary)
      'hsl(142.1 76.2% 36.3%)', // Green (shadcn success)
      'hsl(280.3 47.7% 50.2%)', // Purple (muted)
      'hsl(20.5 90.2% 48.2%)',  // Orange (shadcn destructive variant)
    ]
  };

  constructor() {
    super();
    domtools.elementBasic.setup();

    this.resizeObserver = new ResizeObserver((entries) => {
      // Debounce resize calls to prevent excessive updates
      if (this.resizeTimeout) {
        clearTimeout(this.resizeTimeout);
      }
      
      this.resizeTimeout = window.setTimeout(() => {
        // Simply resize if we have a chart, since we're only observing the mainbox
        if (this.chart) {
          // Log resize event for debugging
          if (this.DEBUG_RESIZE && entries.length > 0) {
            const entry = entries[0];
            console.log('DeesChartArea - Resize detected:', {
              width: entry.contentRect.width,
              height: entry.contentRect.height
            });
          }
          this.resizeChart();
        }
      }, 100); // 100ms debounce
    });
    
    // Note: ResizeObserver is now set up after chart initialization in firstUpdated()
    // to ensure proper timing and avoid race conditions
    
    this.registerGarbageFunction(async () => {
      if (this.resizeTimeout) {
        clearTimeout(this.resizeTimeout);
      }
      if (this.resizeObserver) {
        this.resizeObserver.disconnect();
      }
      this.stopAutoScroll();
      
      // Critical: Destroy chart instance to prevent memory leak
      if (this.chart) {
        try {
          this.chart.destroy();
          this.chart = null;
        } catch (error) {
          console.error('Error destroying chart:', error);
        }
      }
    });
  }
  
  public async connectedCallback() {
    super.connectedCallback();
    
    // Trigger resize when element is connected to DOM
    // This helps with dynamically added charts
    if (this.chart) {
      // Wait a frame for layout to settle
      await new Promise(resolve => requestAnimationFrame(resolve));
      await this.resizeChart();
    }
  }

  public static styles = chartAreaStyles;

  public render(): TemplateResult {
    return renderChartArea(this);
  }



  public async firstUpdated() {
    await this.domtoolsPromise;

    // Load ApexCharts from CDN
    const ApexChartsLib = await DeesServiceLibLoader.getInstance().loadApexCharts();

    // Wait for next animation frame to ensure layout is complete
    await new Promise(resolve => requestAnimationFrame(resolve));
    
    // Get actual dimensions of the container
    const mainbox: HTMLDivElement = this.shadowRoot.querySelector('.mainbox');
    const chartContainer: HTMLDivElement = this.shadowRoot.querySelector('.chartContainer');
    
    if (!mainbox || !chartContainer) {
      console.error('Chart containers not found');
      return;
    }
    
    // Calculate initial dimensions
    const styleChartContainer = window.getComputedStyle(chartContainer);
    const paddingTop = parseInt(styleChartContainer.paddingTop, 10);
    const paddingBottom = parseInt(styleChartContainer.paddingBottom, 10);
    const paddingLeft = parseInt(styleChartContainer.paddingLeft, 10);
    const paddingRight = parseInt(styleChartContainer.paddingRight, 10);
    
    const initialWidth = mainbox.clientWidth - paddingLeft - paddingRight;
    const initialHeight = mainbox.offsetHeight - paddingTop - paddingBottom;
    
    // Use provided series data or default demo data
    const chartSeries = this.series.length > 0 ? this.series : [
      {
        name: 'cpu',
        data: [
          { x: '2025-01-15T03:00:00', y: 25 },
          { x: '2025-01-15T07:00:00', y: 30 },
          { x: '2025-01-15T11:00:00', y: 20 },
          { x: '2025-01-15T15:00:00', y: 35 },
          { x: '2025-01-15T19:00:00', y: 25 },
        ],
      },
      {
        name: 'memory',
        data: [
          { x: '2025-01-15T03:00:00', y: 10 },
          { x: '2025-01-15T07:00:00', y: 12 },
          { x: '2025-01-15T11:00:00', y: 10 },
          { x: '2025-01-15T15:00:00', y: 30 },
          { x: '2025-01-15T19:00:00', y: 40 },
        ],
      },
    ];
    
    // Store internal data
    this.internalChartData = chartSeries;
    
    // Get current theme
    const isDark = !this.goBright;
    const theme = isDark ? 'dark' : 'light';
    
    var options: ApexCharts.ApexOptions = {
      series: chartSeries,
      chart: {
        width: initialWidth || 100, // Use actual width or fallback
        height: initialHeight || 100, // Use actual height or fallback
        type: 'area',
        background: 'transparent', // Transparent background to inherit from container
        toolbar: {
          show: false, // This line disables the toolbar
        },
        animations: {
          enabled: !this.realtimeMode, // Disable animations in realtime mode
          speed: 400,
          animateGradually: {
            enabled: false, // Disable gradual animation for cleaner updates
            delay: 0
          },
          dynamicAnimation: {
            enabled: !this.realtimeMode,
            speed: 350
          }
        },
        zoom: {
          enabled: false, // Disable zoom for cleaner interaction
        },
        selection: {
          enabled: false, // Disable selection
        },
      },
      dataLabels: {
        enabled: false,
      },
      stroke: {
        width: 2,
        curve: 'smooth',
      },
      xaxis: {
        type: 'datetime', // Time-series data
        labels: {
          format: 'HH:mm:ss', // Time formatting with seconds
          datetimeUTC: false,
          style: {
            colors: [isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)'], // Label color
            fontSize: '12px',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
            fontWeight: '400',
          },
        },
        axisBorder: {
          show: false, // Hide x-axis border
        },
        axisTicks: {
          show: false, // Hide x-axis ticks
        },
      },
      yaxis: {
        min: 0,
        max: this.yAxisScaling === 'dynamic' ? undefined : this.yAxisMax,
        labels: {
          formatter: this.yAxisFormatter,
          style: {
            colors: [isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)'], // Label color
            fontSize: '12px',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
            fontWeight: '400',
          },
        },
        axisBorder: {
          show: false, // Hide y-axis border
        },
        axisTicks: {
          show: false, // Hide y-axis ticks
        },
      },
      tooltip: {
        shared: true, // Enables the tooltip to display across series
        intersect: false, // Allows hovering anywhere on the chart
        followCursor: true, // Makes tooltip follow mouse even between points
        x: {
          format: 'dd/MM/yy HH:mm',
        },
        custom: ({ series, dataPointIndex, w }: any) => {
          // Iterate through each series and get its value
          // Note: We can't access component instance here, so we'll use w.config.theme.mode
          const currentTheme = w.config.theme.mode;
          const isDarkMode = currentTheme === 'dark';
          const bgColor = isDarkMode ? 'hsl(0 0% 9%)' : 'hsl(0 0% 100%)';
          const textColor = isDarkMode ? 'hsl(0 0% 95%)' : 'hsl(0 0% 9%)';
          const borderColor = isDarkMode ? 'hsl(0 0% 14.9%)' : 'hsl(0 0% 89.8%)';
          
          // Get formatter from chart config
          const formatter = w.config.yaxis[0]?.labels?.formatter || ((val: number) => val.toString());
          
          let tooltipContent = `<div style="padding: 12px; background: ${bgColor}; color: ${textColor}; border-radius: 6px; box-shadow: 0 2px 8px 0 hsl(0 0% 0% / ${isDarkMode ? '0.2' : '0.1'}); border: 1px solid ${borderColor};font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 12px;">`;

          series.forEach((s: number[], index: number) => {
            const label = w.globals.seriesNames[index]; // Get series label
            const value = s[dataPointIndex]; // Get value at data point
            const color = w.globals.colors[index];
            const formattedValue = formatter(value);
            tooltipContent += `<div style="display: flex; align-items: center; gap: 8px; margin: ${index > 0 ? '6px' : '0'} 0;">
              <span style="display: inline-block; width: 10px; height: 10px; background: ${color}; border-radius: 2px;"></span>
              <span style="font-weight: 500;">${label}:</span>
              <span style="margin-left: auto; font-weight: 600;">${formattedValue}</span>
            </div>`;
          });

          tooltipContent += `</div>`;
          return tooltipContent;
        },
      },
      grid: {
        xaxis: {
          lines: {
            show: false, // Hide vertical grid lines for cleaner look
          },
        },
        yaxis: {
          lines: {
            show: true,
          },
        },
        borderColor: isDark ? 'hsl(0 0% 14.9%)' : 'hsl(0 0% 94%)', // Very subtle grid lines
        strokeDashArray: 0, // Solid line
        padding: {
          top: 10,
          right: 20,
          bottom: 10,
          left: 20,
        },
      },
      fill: {
        type: 'gradient', // Gradient fill for the area
        gradient: {
          shade: isDark ? 'dark' : 'light',
          type: 'vertical',
          shadeIntensity: 0.1,
          opacityFrom: isDark ? 0.2 : 0.3,
          opacityTo: 0,
          stops: [0, 100],
        },
      },
      colors: isDark ? this.CHART_COLORS.dark : this.CHART_COLORS.light,
      theme: {
        mode: theme,
      },
    };
    
    try {
      this.chart = new ApexChartsLib(this.shadowRoot.querySelector('.chartContainer'), options);
      await this.chart.render();
      
      // Give the chart a moment to fully initialize before resizing
      await new Promise(resolve => setTimeout(resolve, 100));
      await this.resizeChart();
      
      // Ensure resize observer is watching the mainbox
      const mainbox = this.shadowRoot.querySelector('.mainbox');
      if (mainbox && this.resizeObserver) {
        // Disconnect any previous observations
        this.resizeObserver.disconnect();
        // Start observing the mainbox
        this.resizeObserver.observe(mainbox);
        if (this.DEBUG_RESIZE) {
          console.log('DeesChartArea - ResizeObserver attached to mainbox');
        }
      }
    } catch (error) {
      console.error('Failed to initialize chart:', error);
      // Optionally, you could set an error state here
      // this.chartState = 'error';
      // this.errorMessage = 'Failed to initialize chart';
    }
  }

  public async updated(changedProperties: Map<string, any>) {
    super.updated(changedProperties);
    
    // Update chart theme when goBright changes
    if (changedProperties.has('goBright') && this.chart) {
      await this.updateChartTheme();
    }
    
    // Update chart if series data changes
    if (changedProperties.has('series') && this.chart && this.series.length > 0) {
      await this.updateSeries(this.series);
    }
    
    // Update y-axis formatter if it changes
    if (changedProperties.has('yAxisFormatter') && this.chart) {
      await this.chart.updateOptions({
        yaxis: {
          labels: {
            formatter: this.yAxisFormatter,
          },
        },
      });
    }
    
    // Handle realtime mode changes
    if (changedProperties.has('realtimeMode') && this.chart) {
      await this.chart.updateOptions({
        chart: {
          animations: {
            enabled: !this.realtimeMode,
            speed: 400,
            animateGradually: {
              enabled: false,
              delay: 0
            },
            dynamicAnimation: {
              enabled: !this.realtimeMode,
              speed: 350
            }
          }
        }
      });
      
      // Start/stop auto-scroll based on realtime mode
      if (this.realtimeMode && this.rollingWindow > 0 && this.autoScrollInterval > 0) {
        this.startAutoScroll();
      } else {
        this.stopAutoScroll();
      }
    }
    
    // Handle auto-scroll interval changes
    if (changedProperties.has('autoScrollInterval') && this.chart) {
      this.stopAutoScroll();
      if (this.realtimeMode && this.rollingWindow > 0 && this.autoScrollInterval > 0) {
        this.startAutoScroll();
      }
    }
    
    // Handle y-axis scaling changes
    if ((changedProperties.has('yAxisScaling') || changedProperties.has('yAxisMax')) && this.chart) {
      await this.chart.updateOptions({
        yaxis: {
          min: 0,
          max: this.yAxisScaling === 'dynamic' ? undefined : this.yAxisMax
        }
      });
    }
  }

  public async updateSeries(newSeries: ApexAxisChartSeries, animate: boolean = true) {
    if (!this.chart) {
      return;
    }
    
    try {
      // Store the new data first
      this.internalChartData = newSeries;
      
      // Handle rolling window if enabled
      if (this.rollingWindow > 0 && this.realtimeMode) {
        const now = Date.now();
        const cutoffTime = now - this.rollingWindow;
        
        // Filter data to only include points within the rolling window
        const filteredSeries = newSeries.map(series => ({
          name: series.name,
          data: (series.data as any[]).filter(point => {
            if (typeof point === 'object' && point !== null && 'x' in point) {
              return new Date(point.x).getTime() > cutoffTime;
            }
            return false;
          })
        }));
        
        // Only update if we have data
        if (filteredSeries.some(s => s.data.length > 0)) {
          // Handle y-axis scaling first
          if (this.yAxisScaling === 'dynamic') {
            const allValues = filteredSeries.flatMap(s => (s.data as any[]).map(d => d.y));
            if (allValues.length > 0) {
              const maxValue = Math.max(...allValues);
              const dynamicMax = Math.ceil(maxValue * 1.1);
              await this.chart.updateOptions({
                yaxis: {
                  min: 0,
                  max: dynamicMax
                }
              }, false, false);
            }
          }
          
          await this.chart.updateSeries(filteredSeries, false);
        }
      } else {
        await this.chart.updateSeries(newSeries, animate);
      }
    } catch (error) {
      console.error('Failed to update chart series:', error);
    }
  }
  
  // Update just the x-axis for smooth scrolling in realtime mode
  // Public for advanced usage in demos, but typically handled automatically
  public async updateTimeWindow() {
    if (!this.chart || this.rollingWindow <= 0) {
      return;
    }
    
    const now = Date.now();
    const cutoffTime = now - this.rollingWindow;
    
    await this.chart.updateOptions({
      xaxis: {
        min: cutoffTime,
        max: now,
        labels: {
          format: 'HH:mm:ss',
          datetimeUTC: false,
          style: {
            colors: [!this.goBright ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)'],
            fontSize: '12px',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
            fontWeight: '400',
          },
        },
        tickAmount: 6,
      }
    }, false, false);
  }

  public async appendData(newData: { data: any[] }[]) {
    if (!this.chart) {
      return;
    }
    
    // Use ApexCharts' appendData method for smoother real-time updates
    this.chart.appendData(newData);
  }
  
  public async updateOptions(options: ApexCharts.ApexOptions, redrawPaths?: boolean, animate?: boolean) {
    if (!this.chart) {
      return;
    }
    
    return this.chart.updateOptions(options, redrawPaths, animate);
  }

  public async resizeChart() {
    if (!this.chart) {
      return;
    }
    
    if (this.DEBUG_RESIZE) {
      console.log('DeesChartArea - resizeChart called');
    }
    
    try {
      const mainbox: HTMLDivElement = this.shadowRoot.querySelector('.mainbox');
      const chartContainer: HTMLDivElement = this.shadowRoot.querySelector('.chartContainer');
      
      if (!mainbox || !chartContainer) {
        return;
      }

      // Force layout recalculation
      void mainbox.offsetHeight;

      // Get computed style of the element
      const styleChartContainer = window.getComputedStyle(chartContainer);

      // Extract padding values
      const paddingTop = parseInt(styleChartContainer.paddingTop, 10);
      const paddingBottom = parseInt(styleChartContainer.paddingBottom, 10);
      const paddingLeft = parseInt(styleChartContainer.paddingLeft, 10);
      const paddingRight = parseInt(styleChartContainer.paddingRight, 10);

      // Calculate the actual width and height to use, subtracting padding
      const actualWidth = mainbox.clientWidth - paddingLeft - paddingRight;
      const actualHeight = mainbox.offsetHeight - paddingTop - paddingBottom;
      
      // Validate dimensions
      if (actualWidth > 0 && actualHeight > 0) {
        if (this.DEBUG_RESIZE) {
          console.log('DeesChartArea - Updating chart dimensions:', {
            width: actualWidth,
            height: actualHeight
          });
        }
        
        await this.chart.updateOptions({
          chart: {
            width: actualWidth,
            height: actualHeight,
          },
        }, true, false); // Redraw paths but don't animate
      }
    } catch (error) {
      console.error('Failed to resize chart:', error);
    }
  }
  
  /**
   * Manually trigger a chart resize. Useful when automatic detection doesn't work.
   * This is a convenience method that can be called from outside the component.
   */
  public async forceResize() {
    await this.resizeChart();
  }
  
  private startAutoScroll() {
    if (this.autoScrollTimer) {
      return; // Already running
    }
    
    this.autoScrollTimer = window.setInterval(() => {
      this.updateTimeWindow();
    }, this.autoScrollInterval);
  }
  
  private stopAutoScroll() {
    if (this.autoScrollTimer) {
      window.clearInterval(this.autoScrollTimer);
      this.autoScrollTimer = null;
    }
  }
  
  private async updateChartTheme() {
    if (!this.chart) {
      return;
    }
    
    const isDark = !this.goBright;
    const theme = isDark ? 'dark' : 'light';
    
    await this.chart.updateOptions({
      theme: {
        mode: theme,
      },
      colors: isDark ? this.CHART_COLORS.dark : this.CHART_COLORS.light,
      xaxis: {
        labels: {
          style: {
            colors: [isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)'],
          },
        },
      },
      yaxis: {
        labels: {
          style: {
            colors: [isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)'],
          },
        },
      },
      grid: {
        borderColor: isDark ? 'hsl(0 0% 14.9%)' : 'hsl(0 0% 94%)',
      },
      fill: {
        gradient: {
          shade: isDark ? 'dark' : 'light',
          opacityFrom: isDark ? 0.2 : 0.3,
        },
      },
    });
  }
}
