'use client';

/**
 * HybridWaveform - Real-time frequency visualization for hybrid player.
 *
 * Two modes:
 * - 'frequency': Real-time frequency bars (default, requires playing audio)
 * - 'static': Static progress bar (for when no analyser is available)
 *
 * Features:
 * - Shows buffered regions
 * - Click to seek
 * - Responsive width
 */

import { useRef, useEffect, useCallback, memo } from 'react';
import { useHybridAudioContext } from '../context/HybridAudioProvider';
import { cn } from '@djangocfg/ui-core/lib';
import { useThemeColor } from '@djangocfg/ui-core/styles/palette';

// =============================================================================
// TYPES
// =============================================================================

export interface HybridWaveformProps {
  /** Visualization mode */
  mode?: 'frequency' | 'static';
  /** Canvas height in pixels */
  height?: number;
  /** Bar width in pixels */
  barWidth?: number;
  /** Gap between bars in pixels */
  barGap?: number;
  /** Bar border radius */
  barRadius?: number;
  /** Color for played portion */
  progressColor?: string;
  /** Color for unplayed portion */
  waveColor?: string;
  /** Color for buffered regions indicator */
  bufferedColor?: string;
  /** Additional CSS class */
  className?: string;
  /** Callback when user seeks */
  onSeek?: (time: number) => void;
}

// =============================================================================
// COMPONENT
// =============================================================================

export const HybridWaveform = memo(function HybridWaveform({
  mode = 'frequency',
  height = 64,
  barWidth = 3,
  barGap = 2,
  barRadius = 2,
  progressColor,
  waveColor,
  bufferedColor,
  className,
  onSeek,
}: HybridWaveformProps) {
  const themePrimary = useThemeColor('primary');
  const themePrimaryWave = useThemeColor('primary', 0.3);
  const themePrimaryBuffered = useThemeColor('primary', 0.15);
  const resolvedProgressColor = progressColor ?? themePrimary;
  const resolvedWaveColor = waveColor ?? themePrimaryWave;
  const resolvedBufferedColor = bufferedColor ?? themePrimaryBuffered;
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const animationRef = useRef<number | null>(null);
  const { state, controls, webAudio } = useHybridAudioContext();

  // Handle click to seek
  const handleClick = useCallback(
    (e: React.MouseEvent<HTMLCanvasElement>) => {
      const canvas = canvasRef.current;
      if (!canvas || !state.duration) return;

      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const progress = x / rect.width;
      const time = state.duration * progress;

      controls.seek(time);
      onSeek?.(time);
    },
    [state.duration, controls, onSeek]
  );

  // Render frequency bars (real-time visualization)
  const renderFrequency = useCallback(() => {
    const canvas = canvasRef.current;
    const analyser = webAudio.analyser;
    if (!canvas) return;

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

    const { width, height: canvasHeight } = canvas;
    const dpr = window.devicePixelRatio || 1;
    const displayWidth = width / dpr;

    // Get frequency data if analyser is available
    let dataArray: Uint8Array<ArrayBuffer> | null = null;
    if (analyser) {
      dataArray = new Uint8Array(analyser.frequencyBinCount) as Uint8Array<ArrayBuffer>;
      analyser.getByteFrequencyData(dataArray);
    }

    ctx.clearRect(0, 0, width, canvasHeight);

    // Draw buffered regions at bottom
    if (state.buffered && state.duration > 0) {
      ctx.fillStyle = resolvedBufferedColor;
      for (let i = 0; i < state.buffered.length; i++) {
        const start = (state.buffered.start(i) / state.duration) * width;
        const end = (state.buffered.end(i) / state.duration) * width;
        ctx.fillRect(start, canvasHeight - 3 * dpr, end - start, 3 * dpr);
      }
    }

    // Calculate bar count based on available width
    const barCount = Math.floor(displayWidth / (barWidth + barGap));
    const progress = state.duration > 0 ? state.currentTime / state.duration : 0;
    const progressX = width * progress;

    // Draw bars
    for (let i = 0; i < barCount; i++) {
      let barHeight: number;

      if (dataArray && state.isPlaying) {
        // Real-time frequency data
        const step = Math.floor(dataArray.length / barCount);
        const value = dataArray[i * step] / 255;
        barHeight = Math.max(4 * dpr, value * (canvasHeight - 6 * dpr) * 0.9);
      } else {
        // Static fallback - small bars
        barHeight = 8 * dpr;
      }

      const x = i * (barWidth + barGap) * dpr;
      const y = (canvasHeight - barHeight) / 2;

      ctx.fillStyle = x < progressX ? resolvedProgressColor : resolvedWaveColor;

      // Draw rounded rect
      const radius = barRadius * dpr;
      const rectWidth = barWidth * dpr;
      ctx.beginPath();
      ctx.roundRect(x, y, rectWidth, barHeight, radius);
      ctx.fill();
    }

    // Continue animation if playing
    if (state.isPlaying) {
      animationRef.current = requestAnimationFrame(renderFrequency);
    }
  }, [
    webAudio.analyser,
    state.buffered,
    state.duration,
    state.currentTime,
    state.isPlaying,
    barWidth,
    barGap,
    barRadius,
    resolvedProgressColor,
    resolvedWaveColor,
    resolvedBufferedColor,
  ]);

  // Render static progress bar
  const renderStatic = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

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

    const { width, height: canvasHeight } = canvas;
    const dpr = window.devicePixelRatio || 1;

    ctx.clearRect(0, 0, width, canvasHeight);

    // Draw progress bar
    const progress = state.duration > 0 ? state.currentTime / state.duration : 0;
    const progressWidth = width * progress;

    // Background track
    ctx.fillStyle = resolvedWaveColor;
    ctx.fillRect(0, canvasHeight / 2 - 2 * dpr, width, 4 * dpr);

    // Buffered region (thin strip at bottom)
    if (state.buffered && state.duration > 0) {
      ctx.fillStyle = resolvedBufferedColor;
      for (let i = 0; i < state.buffered.length; i++) {
        const start = (state.buffered.start(i) / state.duration) * width;
        const end = (state.buffered.end(i) / state.duration) * width;
        ctx.fillRect(start, canvasHeight - 2 * dpr, end - start, 2 * dpr);
      }
    }

    // Progress
    ctx.fillStyle = resolvedProgressColor;
    ctx.fillRect(0, canvasHeight / 2 - 2 * dpr, progressWidth, 4 * dpr);

    // Handle
    if (progress > 0) {
      ctx.beginPath();
      ctx.arc(progressWidth, canvasHeight / 2, 6 * dpr, 0, Math.PI * 2);
      ctx.fill();
    }
  }, [state.buffered, state.duration, state.currentTime, resolvedProgressColor, resolvedWaveColor, resolvedBufferedColor]);

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

    const resizeObserver = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (!entry) return;

      const dpr = window.devicePixelRatio || 1;
      const displayWidth = entry.contentRect.width;
      const displayHeight = height;

      canvas.width = displayWidth * dpr;
      canvas.height = displayHeight * dpr;
      canvas.style.width = `${displayWidth}px`;
      canvas.style.height = `${displayHeight}px`;

      // Re-render after resize
      if (mode === 'frequency') {
        renderFrequency();
      } else {
        renderStatic();
      }
    });

    resizeObserver.observe(container);
    return () => resizeObserver.disconnect();
  }, [height, mode, renderFrequency, renderStatic]);

  // Animation loop for frequency mode
  useEffect(() => {
    if (mode === 'frequency') {
      renderFrequency();
    }

    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
    };
  }, [mode, renderFrequency]);

  // Re-render on time change for static mode
  useEffect(() => {
    if (mode === 'static') {
      renderStatic();
    }
  }, [mode, state.currentTime, renderStatic]);

  // Re-render frequency when playback state changes
  useEffect(() => {
    if (mode === 'frequency' && !state.isPlaying) {
      // One final render when stopped
      renderFrequency();
    }
  }, [mode, state.isPlaying, renderFrequency]);

  return (
    <div ref={containerRef} className={cn('w-full', className)}>
      <canvas
        ref={canvasRef}
        onClick={handleClick}
        className="cursor-pointer"
        style={{ width: '100%', height }}
      />
    </div>
  );
});
