import { useCallback, useEffect, useRef } from 'react';

import { WAVEFORM_CONSTANTS } from './WaveformConstants';

interface RecordLiveWaveformProps {
  audioData?: Float32Array;
  isRecording?: boolean;
  barColor?: string;
  glowColor?: string;
}

export const RecordLiveWaveform: React.FC<RecordLiveWaveformProps> = ({
  audioData,
  isRecording = false,
  barColor = WAVEFORM_CONSTANTS.BAR_COLOR,
  glowColor = WAVEFORM_CONSTANTS.GLOW_COLOR,
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const animationRef = useRef<number | undefined>(undefined);
  const resizeObserverRef = useRef<ResizeObserver | null>(null);
  const barHeights = useRef<number[]>([]);
  const barTargetHeights = useRef<number[]>([]);
  const barPhases = useRef<number[]>([]);
  const startTime = useRef<number>(Date.now());
  const currentBarCount = useRef<number>(0);

  // Calculate responsive waveform dimensions based on container width
  const calculateWaveformDimensions = useCallback((containerWidth: number) => {
    const availableWidth = containerWidth - 40; // Leave some padding
    const minBarWidth = 1;
    const maxBarWidth = 3;
    const barGap = 3;

    // Calculate optimal bar count and width
    const maxBarsWithMinWidth = Math.floor(
      availableWidth / (minBarWidth + barGap)
    );
    const barCount = Math.min(maxBarsWithMinWidth, 160); // Cap at 160 for performance

    // Calculate actual bar width to fill available space
    const totalGaps = (barCount - 1) * barGap;
    const barWidth = Math.min(
      maxBarWidth,
      Math.max(minBarWidth, (availableWidth - totalGaps) / barCount)
    );

    return {
      barCount,
      barWidth,
      barGap,
      totalWidth: barCount * barWidth + (barCount - 1) * barGap,
    };
  }, []);

  // Initialize or resize bar arrays when bar count changes
  const initializeBars = useCallback((barCount: number) => {
    if (currentBarCount.current !== barCount) {
      currentBarCount.current = barCount;
      barHeights.current = new Array(barCount).fill(0);
      barTargetHeights.current = new Array(barCount).fill(0);
      barPhases.current = new Array(barCount)
        .fill(0)
        .map(() => Math.random() * Math.PI * 2);
    }
  }, []);

  const setupCanvas = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return null;

    const ctx = canvas.getContext('2d');
    if (!ctx) return null;

    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();

    // Set canvas size
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    ctx.scale(dpr, dpr);

    // Calculate responsive dimensions
    const dimensions = calculateWaveformDimensions(rect.width);
    initializeBars(dimensions.barCount);

    const startX = (rect.width - dimensions.totalWidth) / 2;

    return { ctx, rect, dimensions, startX };
  }, [calculateWaveformDimensions, initializeBars]);

  useEffect(() => {
    const animate = () => {
      const setup = setupCanvas();
      if (!setup) return;

      const { ctx, rect, dimensions, startX } = setup;
      ctx.clearRect(0, 0, rect.width, rect.height);

      const currentTime = Date.now();
      const elapsed = (currentTime - startTime.current) / 1000;

      for (let i = 0; i < dimensions.barCount; i++) {
        const x = startX + i * (dimensions.barWidth + dimensions.barGap);

        // Determine base height based on position with responsive edge calculation
        const edgeRatio = 0.1; // 10% of bars on each edge
        const smallRatio = 0.2; // 20% of bars for small height
        const mediumRatio = 0.3; // 30% of bars for medium height

        const edgeCount = Math.floor(dimensions.barCount * edgeRatio);
        const smallCount = Math.floor(dimensions.barCount * smallRatio);
        const mediumCount = Math.floor(dimensions.barCount * mediumRatio);

        let baseHeight;
        if (i <= edgeCount || i >= dimensions.barCount - edgeCount) {
          baseHeight =
            WAVEFORM_CONSTANTS.MIN_HEIGHT +
            (WAVEFORM_CONSTANTS.MAX_HEIGHT - WAVEFORM_CONSTANTS.MIN_HEIGHT) *
              0.2; // Very small edges
        } else if (i <= smallCount || i >= dimensions.barCount - smallCount) {
          baseHeight =
            WAVEFORM_CONSTANTS.MIN_HEIGHT +
            (WAVEFORM_CONSTANTS.MAX_HEIGHT - WAVEFORM_CONSTANTS.MIN_HEIGHT) *
              0.5; // Small
        } else if (i <= mediumCount || i >= dimensions.barCount - mediumCount) {
          baseHeight =
            WAVEFORM_CONSTANTS.MIN_HEIGHT +
            (WAVEFORM_CONSTANTS.MAX_HEIGHT - WAVEFORM_CONSTANTS.MIN_HEIGHT) *
              0.7; // Medium
        } else {
          baseHeight =
            WAVEFORM_CONSTANTS.MIN_HEIGHT +
            (WAVEFORM_CONSTANTS.MAX_HEIGHT - WAVEFORM_CONSTANTS.MIN_HEIGHT) *
              1.0; // Large center
        }

        // Multiple wave layers for more complex animation
        const waveSpeed1 = 1.5 + Math.sin(barPhases.current[i]) * 0.8;
        const waveSpeed2 = 0.8 + Math.cos(barPhases.current[i] * 1.3) * 0.4;
        const waveSpeed3 = 2.2 + Math.sin(barPhases.current[i] * 0.7) * 0.6;

        const wave1 =
          Math.sin(elapsed * waveSpeed1 + barPhases.current[i]) * 0.5 + 0.5;
        const wave2 =
          Math.cos(elapsed * waveSpeed2 + barPhases.current[i] * 1.7) * 0.3 +
          0.7;
        const wave3 =
          Math.sin(elapsed * waveSpeed3 + barPhases.current[i] * 2.1) * 0.4 +
          0.6;

        // Combine waves for more complex motion
        const combinedWave = wave1 * 0.5 + wave2 * 0.3 + wave3 * 0.2;

        // Apply audio data with more dramatic scaling
        let audioMultiplier = 1;
        if (audioData && audioData.length > 0) {
          const audioIndex = Math.floor(
            (i / dimensions.barCount) * audioData.length
          );
          const audioLevel = Math.abs(audioData[audioIndex] || 0);
          // Much more dramatic audio influence
          audioMultiplier =
            0.2 + audioLevel * 3.0 + (audioLevel > 0.1 ? audioLevel * 2.0 : 0);
        }

        // Add random bursts for more liveliness
        const randomBurst = Math.sin(elapsed * 3 + i * 0.1) > 0.85 ? 1.5 : 1.0;

        // Calculate target height with all effects
        const targetHeight =
          baseHeight * combinedWave * audioMultiplier * randomBurst;
        barTargetHeights.current[i] = targetHeight;

        // Faster, more responsive interpolation
        const lerpSpeed = 0.2 + (audioMultiplier > 1.5 ? 0.3 : 0);
        barHeights.current[i] +=
          (barTargetHeights.current[i] - barHeights.current[i]) * lerpSpeed;

        const height = Math.max(
          WAVEFORM_CONSTANTS.MIN_HEIGHT,
          barHeights.current[i]
        );
        const y = (rect.height - height) / 2;

        // Dynamic opacity and glow effect
        const heightRatio = height / WAVEFORM_CONSTANTS.MAX_HEIGHT;
        const opacity = 0.3 + heightRatio * 0.7;
        const glowIntensity = heightRatio * (audioMultiplier > 1.5 ? 1.0 : 0.5);

        // Add glow effect for higher bars
        if (glowIntensity > 0.3) {
          ctx.shadowColor = glowColor;
          ctx.shadowBlur = glowIntensity * 8;
          ctx.shadowOffsetX = 0;
          ctx.shadowOffsetY = 0;
        } else {
          ctx.shadowBlur = 0;
        }

        ctx.fillStyle = barColor;
        ctx.globalAlpha = opacity;
        ctx.fillRect(x, y, dimensions.barWidth, height);

        // Reset shadow for next iteration
        ctx.shadowBlur = 0;
      }

      ctx.globalAlpha = 1;
      animationRef.current = requestAnimationFrame(animate);
    };

    animate();

    // Set up ResizeObserver to handle container size changes
    const canvas = canvasRef.current;
    if (canvas && !resizeObserverRef.current) {
      resizeObserverRef.current = new ResizeObserver(() => {
        // Canvas will be reconfigured on next animation frame
      });
      resizeObserverRef.current.observe(canvas);
    }

    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
      if (resizeObserverRef.current) {
        resizeObserverRef.current.disconnect();
        resizeObserverRef.current = null;
      }
    };
  }, [audioData, isRecording, setupCanvas]);

  return (
    <canvas
      ref={canvasRef}
      className='h-8 w-full'
      style={{ imageRendering: 'pixelated' }}
    />
  );
};
