import { useAnimationFrame } from 'framer-motion';
import { useCallback, useEffect, useRef } from 'react';

import useLiveAudioAnalyzer from '@/hooks/useLiveAudioAnalyzer';

export type WaveformType =
  | 'bars'
  | 'waveform'
  | 'both'
  | 'scrolling-bars'
  | 'playbar-timeline';

interface LiveAudioWaveformProps {
  audioElement: HTMLAudioElement | null;
  width?: number;
  height?: number;
  className?: string;
  type?: WaveformType;
  color?: string;
  backgroundColor?: string;
  barCount?: number;
  lineWidth?: number;
  enabled?: boolean;
  responsive?: boolean;
  centerBars?: boolean; // Center bars vertically instead of bottom-align
}

const LiveAudioWaveform: React.FC<LiveAudioWaveformProps> = ({
  audioElement,
  width = 400,
  height = 100,
  className = '',
  type = 'waveform',
  color = '#ffffff',
  backgroundColor = 'transparent',
  barCount = 64,
  lineWidth = 2,
  enabled = true,
  responsive = true,
  centerBars = false,
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  // For scrolling bars visualization - keep a history of volume levels
  const volumeHistoryRef = useRef<number[]>([]);
  const maxHistoryLength = type === 'scrolling-bars' ? barCount : 200; // Use barCount for scrolling bars

  const { isAnalyzing, getAnalysisData } = useLiveAudioAnalyzer(audioElement, {
    fftSize: 2048,
    smoothingTimeConstant: 0.8,
    enabled,
  });

  // Resize canvas to match container if responsive
  const updateCanvasSize = useCallback(() => {
    const canvas = canvasRef.current;
    const container = containerRef.current;
    if (!canvas || !responsive || !container) return;

    const rect = container.getBoundingClientRect();
    const canvasWidth = rect.width || width;
    const canvasHeight = rect.height || height;

    // Set canvas internal size
    canvas.width = canvasWidth * window.devicePixelRatio;
    canvas.height = canvasHeight * window.devicePixelRatio;

    // Set canvas display size
    canvas.style.width = `${canvasWidth}px`;
    canvas.style.height = `${canvasHeight}px`;

    // Scale context for high DPI displays
    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
    }
  }, [width, height, responsive]);

  // Initialize canvas size on mount and resize
  useEffect(() => {
    updateCanvasSize();

    if (responsive) {
      window.addEventListener('resize', updateCanvasSize);
      return () => window.removeEventListener('resize', updateCanvasSize);
    }
  }, [updateCanvasSize, responsive]);

  // Clear volume history when audio element changes or when disabled
  useEffect(() => {
    if (!enabled || !audioElement) {
      volumeHistoryRef.current = [];
    }
  }, [enabled, audioElement]);

  // Render frequency bars
  const renderBars = useCallback(
    (
      ctx: CanvasRenderingContext2D,
      frequencyData: Uint8Array,
      canvasWidth: number,
      canvasHeight: number
    ) => {
      const barWidth = canvasWidth / barCount;
      const frequencyStep = Math.floor(frequencyData.length / barCount);

      ctx.fillStyle = color;

      for (let i = 0; i < barCount; i++) {
        // Average frequency data for this bar
        let sum = 0;
        const start = i * frequencyStep;
        const end = Math.min(start + frequencyStep, frequencyData.length);

        for (let j = start; j < end; j++) {
          sum += frequencyData[j];
        }

        const average = sum / (end - start);
        const barHeight = (average / 255) * canvasHeight;

        const x = i * barWidth;
        const y = canvasHeight - barHeight;

        ctx.fillRect(x, y, barWidth - 1, barHeight);
      }
    },
    [color, barCount]
  );

  // Render waveform
  const renderWaveform = useCallback(
    (
      ctx: CanvasRenderingContext2D,
      timeData: Uint8Array,
      canvasWidth: number,
      canvasHeight: number
    ) => {
      ctx.strokeStyle = color;
      ctx.lineWidth = lineWidth;
      ctx.beginPath();

      const sliceWidth = canvasWidth / timeData.length;
      let x = 0;

      for (let i = 0; i < timeData.length; i++) {
        const v = timeData[i] / 255; // Normalize to 0-1
        const y = (v * canvasHeight) / 2 + canvasHeight / 2; // Center waveform

        if (i === 0) {
          ctx.moveTo(x, y);
        } else {
          ctx.lineTo(x, y);
        }

        x += sliceWidth;
      }

      ctx.stroke();
    },
    [color, lineWidth]
  );

  // Calculate current volume level from time domain data
  const calculateVolume = useCallback((timeData: Uint8Array): number => {
    let sum = 0;
    for (let i = 0; i < timeData.length; i++) {
      const normalized = (timeData[i] - 128) / 128; // Convert from 0-255 to -1 to 1
      sum += normalized * normalized; // RMS calculation
    }
    return Math.sqrt(sum / timeData.length);
  }, []);

  // Render scrolling volume bars
  const renderScrollingBars = useCallback(
    (
      ctx: CanvasRenderingContext2D,
      timeData: Uint8Array,
      canvasWidth: number,
      canvasHeight: number
    ) => {
      // Calculate current volume and add to history
      const currentVolume = calculateVolume(timeData);
      volumeHistoryRef.current.push(currentVolume);

      // Keep only the last N samples
      if (volumeHistoryRef.current.length > maxHistoryLength) {
        volumeHistoryRef.current.shift();
      }

      // Calculate bar width based on canvas width
      const barWidth = Math.max(1, canvasWidth / maxHistoryLength);

      ctx.fillStyle = color;

      // Draw bars from right to left (newest to oldest)
      for (let i = 0; i < volumeHistoryRef.current.length; i++) {
        const volume = volumeHistoryRef.current[i];
        const barHeight = volume * canvasHeight * 2; // Amplify for visibility

        // Position: rightmost is newest, leftmost is oldest
        const x =
          canvasWidth - (volumeHistoryRef.current.length - i) * barWidth;

        // Center bars vertically or align to bottom based on centerBars prop
        const y = centerBars
          ? (canvasHeight - barHeight) / 2 // Center vertically
          : canvasHeight - barHeight; // Align to bottom

        // Add some transparency to older bars for a fade effect
        const age =
          (volumeHistoryRef.current.length - i) /
          volumeHistoryRef.current.length;
        const alpha = Math.max(0.1, 1 - age * 0.7); // Fade from 100% to 30%

        ctx.globalAlpha = alpha;
        ctx.fillRect(x, y, barWidth - 1, barHeight);
      }

      // Reset alpha
      ctx.globalAlpha = 1;
    },
    [color, calculateVolume, centerBars]
  );

  // Render playbar timeline (central playbar with volume bars on left, uniform bars on right)
  const renderPlaybarTimeline = useCallback(
    (
      ctx: CanvasRenderingContext2D,
      timeData: Uint8Array,
      canvasWidth: number,
      canvasHeight: number
    ) => {
      // Calculate current volume and add to history
      const currentVolume = calculateVolume(timeData);
      volumeHistoryRef.current.push(currentVolume);

      // Keep only the last N samples for the left side
      const leftSideBarCount = Math.floor(barCount / 2);
      if (volumeHistoryRef.current.length > leftSideBarCount) {
        volumeHistoryRef.current.shift();
      }

      // Calculate dimensions
      const playbarX = canvasWidth / 2;
      const playbarWidth = 3;
      const leftSideWidth = playbarX; // No margin, go right up to playbar
      const rightSideWidth = canvasWidth - playbarX; // No margin, start right after playbar
      const leftBarWidth = Math.max(1, leftSideWidth / leftSideBarCount);
      const rightBarWidth = Math.max(
        1,
        rightSideWidth / Math.floor(barCount / 2)
      );

      ctx.fillStyle = color;

      // Draw past audio bars on the left (variable height based on volume)
      for (let i = 0; i < volumeHistoryRef.current.length; i++) {
        const volume = volumeHistoryRef.current[i];
        const barHeight = volume * canvasHeight * 1.5; // Amplify for visibility

        // Position from right to left before the playbar
        const x =
          playbarX - (volumeHistoryRef.current.length - i) * leftBarWidth;
        const y = centerBars
          ? (canvasHeight - barHeight) / 2 // Center vertically
          : canvasHeight - barHeight; // Align to bottom

        // Add fade effect for older bars
        const age =
          (volumeHistoryRef.current.length - i) /
          volumeHistoryRef.current.length;
        const alpha = Math.max(0.3, 1 - age * 0.5); // Fade from 100% to 30%

        ctx.globalAlpha = alpha;
        ctx.fillRect(x, y, leftBarWidth - 1, barHeight);
      }

      // Reset alpha for other elements
      ctx.globalAlpha = 1;

      // Draw future/queued bars on the right (uniform small height)
      const rightBarCount = Math.floor(barCount / 2);
      const uniformBarHeight = canvasHeight * 0.1; // Small uniform height

      for (let i = 0; i < rightBarCount; i++) {
        const x = playbarX + i * rightBarWidth;
        const y = centerBars
          ? (canvasHeight - uniformBarHeight) / 2 // Center vertically
          : canvasHeight - uniformBarHeight; // Align to bottom

        // Gradually fade out future bars
        const fadeAlpha = Math.max(0.1, 1 - (i / rightBarCount) * 0.8);
        ctx.globalAlpha = fadeAlpha;
        ctx.fillRect(x, y, rightBarWidth - 1, uniformBarHeight);
      }

      // Reset alpha
      ctx.globalAlpha = 1;

      // Draw central playbar line
      ctx.fillStyle = color;
      ctx.fillRect(playbarX - playbarWidth / 2, 0, playbarWidth, canvasHeight);
    },
    [color, calculateVolume, centerBars, barCount]
  );

  // Render both bars and waveform
  const renderBoth = useCallback(
    (
      ctx: CanvasRenderingContext2D,
      frequencyData: Uint8Array,
      timeData: Uint8Array,
      canvasWidth: number,
      canvasHeight: number
    ) => {
      const barHeight = canvasHeight * 0.6;
      const waveformHeight = canvasHeight * 0.4;

      // Save context state
      ctx.save();

      // Render bars in top portion
      ctx.save();
      ctx.translate(0, 0);
      ctx.beginPath();
      ctx.rect(0, 0, canvasWidth, barHeight);
      ctx.clip();

      renderBars(ctx, frequencyData, canvasWidth, barHeight);
      ctx.restore();

      // Render waveform in bottom portion
      ctx.save();
      ctx.translate(0, barHeight);
      ctx.beginPath();
      ctx.rect(0, 0, canvasWidth, waveformHeight);
      ctx.clip();

      renderWaveform(ctx, timeData, canvasWidth, waveformHeight);
      ctx.restore();

      // Restore context state
      ctx.restore();
    },
    [renderBars, renderWaveform]
  );

  // Animation loop
  useAnimationFrame(
    useCallback(() => {
      if (!isAnalyzing || !enabled) return;

      const canvas = canvasRef.current;
      if (!canvas) return;

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

      const { frequency, time } = getAnalysisData();
      if (!frequency || !time) return;

      const canvasWidth =
        responsive && containerRef.current
          ? containerRef.current.getBoundingClientRect().width || width
          : width;
      const canvasHeight =
        responsive && containerRef.current
          ? containerRef.current.getBoundingClientRect().height || height
          : height;

      // Clear canvas completely first
      ctx.clearRect(0, 0, canvasWidth, canvasHeight);

      // Then fill with background color if not transparent
      if (backgroundColor !== 'transparent') {
        ctx.fillStyle = backgroundColor;
        ctx.fillRect(0, 0, canvasWidth, canvasHeight);
      }

      // Render based on type
      switch (type) {
        case 'bars':
          renderBars(ctx, frequency, canvasWidth, canvasHeight);
          break;
        case 'waveform':
          renderWaveform(ctx, time, canvasWidth, canvasHeight);
          break;
        case 'scrolling-bars':
          renderScrollingBars(ctx, time, canvasWidth, canvasHeight);
          break;
        case 'playbar-timeline':
          renderPlaybarTimeline(ctx, time, canvasWidth, canvasHeight);
          break;
        case 'both':
          renderBoth(ctx, frequency, time, canvasWidth, canvasHeight);
          break;
      }
    }, [
      isAnalyzing,
      enabled,
      getAnalysisData,
      type,
      renderBars,
      renderWaveform,
      renderScrollingBars,
      renderPlaybarTimeline,
      renderBoth,
      backgroundColor,
      width,
      height,
      responsive,
    ])
  );

  return (
    <div
      ref={containerRef}
      className={className}
      style={{
        width: responsive ? '100%' : width,
        height: responsive ? '100%' : height,
        minHeight: responsive ? height : undefined,
      }}
    >
      <canvas
        ref={canvasRef}
        width={width}
        height={height}
        style={{
          width: responsive ? '100%' : `${width}px`,
          height: responsive ? '100%' : `${height}px`,
          display: 'block',
        }}
      />
    </div>
  );
};

export default LiveAudioWaveform;
