import { useRef, useState, useEffect, useCallback } from 'react';
import { Play, Pause, Volume1, Volume2, VolumeX } from 'lucide-react';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import type { AvatarShape } from '@/lib/avatarShape';
import { cn } from '@/lib/utils';
import { usePlayerControls } from '@/hooks/usePlayerControls';
import { formatTime } from '@/lib/formatTime';

interface AudioVisualizerProps {
  src: string;
  mime?: string;
  /** Avatar image URL for the circle in the centre */
  avatarUrl?: string;
  /** Fallback display letter for the avatar */
  avatarFallback?: string;
  /** Avatar mask shape, forwarded from the author's profile metadata */
  avatarShape?: AvatarShape;
  className?: string;
}


/**
 * Audio player that renders identically to VideoPlayer — same container,
 * same overlay controls, same progress bar — but the "video surface" is
 * a canvas showing an animated sinewave with the author's avatar centred.
 */
export function AudioVisualizer({
  src,
  mime,
  avatarUrl,
  avatarFallback = '?',
  avatarShape,
  className,
}: AudioVisualizerProps) {
  const audioRef = useRef<HTMLAudioElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const progressRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const animFrameRef = useRef<number>(0);
  const analyserRef = useRef<AnalyserNode | null>(null);
  const audioCtxRef = useRef<AudioContext | null>(null);
  const idlePhaseRef = useRef(0);

  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [hasStarted, setHasStarted] = useState(false);

  const progress = duration > 0 ? (currentTime / duration) * 100 : 0;

  const { showControls, revealControls, scheduleHide, isMuted, volume, toggleMute, handleVolumeChange } = usePlayerControls({
    mediaRef: audioRef,
    containerRef,
    isPlaying,
  });

  // ── Canvas: sinewave drawing ───────────────────────────────────────────
  const draw = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    // Use CSS pixel dimensions for drawing coordinates
    const W = canvas.clientWidth;
    const H = canvas.clientHeight;
    const dpr = window.devicePixelRatio || 1;

    // Ensure backing store matches display size
    if (canvas.width !== Math.round(W * dpr) || canvas.height !== Math.round(H * dpr)) {
      canvas.width = Math.round(W * dpr);
      canvas.height = Math.round(H * dpr);
    }

    ctx.save();
    ctx.scale(dpr, dpr);
    ctx.clearRect(0, 0, W, H);

    // Read the theme's --primary HSL value at draw time so the wave always
    // matches the current color scheme (light / dark / custom theme).
    const primaryHsl = getComputedStyle(canvas).getPropertyValue('--primary').trim();
    const primaryColor = `hsl(${primaryHsl})`;
    const primaryFaint = `hsl(${primaryHsl} / 0.15)`;
    const primaryMid   = `hsl(${primaryHsl} / 0.85)`;
    const primaryGlow  = `hsl(${primaryHsl} / 0.55)`;

    const analyser = analyserRef.current;
    let dataArray: Uint8Array | null = null;
    if (analyser && isPlaying) {
      dataArray = new Uint8Array(analyser.frequencyBinCount);
      analyser.getByteTimeDomainData(dataArray);
    }

    // Avatar clear-zone radius in CSS px (matches the size-20 = 80px avatar)
    const avatarR = 52; // half of size-20 (80px) + a small gap
    const cx = W / 2;
    const midY = H / 2;

    const grad = ctx.createLinearGradient(0, 0, W, 0);
    grad.addColorStop(0,    primaryFaint);
    grad.addColorStop(0.35, primaryMid);
    grad.addColorStop(0.5,  primaryColor);
    grad.addColorStop(0.65, primaryMid);
    grad.addColorStop(1,    primaryFaint);

    ctx.beginPath();
    ctx.strokeStyle = grad;
    ctx.lineWidth = 2.5;
    ctx.shadowBlur = 10;
    ctx.shadowColor = primaryGlow;

    if (dataArray) {
      // Real-time waveform
      const sliceW = W / dataArray.length;
      for (let i = 0; i < dataArray.length; i++) {
        const x = i * sliceW;
        const v = dataArray[i] / 128.0 - 1; // -1..1
        const dist = Math.abs(x - cx);
        // Fade smoothly through the avatar zone
        const fade = dist < avatarR
          ? 0
          : dist < avatarR * 1.6
            ? (dist - avatarR) / (avatarR * 0.6)
            : 1;
        const y = midY + v * (H * 0.35) * fade;
        if (i === 0) ctx.moveTo(x, y);
        else ctx.lineTo(x, y);
      }
    } else {
      // Idle animated sine
      idlePhaseRef.current += 0.03;
      const phase = idlePhaseRef.current;
      const steps = 300;
      for (let i = 0; i <= steps; i++) {
        const x = (i / steps) * W;
        const dist = Math.abs(x - cx);
        const fade = dist < avatarR
          ? 0
          : dist < avatarR * 1.6
            ? (dist - avatarR) / (avatarR * 0.6)
            : 1;
        const y = midY + Math.sin((i / steps) * Math.PI * 4 + phase) * (H * 0.12) * fade;
        if (i === 0) ctx.moveTo(x, y);
        else ctx.lineTo(x, y);
      }
    }

    ctx.stroke();
    ctx.restore();

    animFrameRef.current = requestAnimationFrame(draw);
  }, [isPlaying]);

  useEffect(() => {
    cancelAnimationFrame(animFrameRef.current);
    animFrameRef.current = requestAnimationFrame(draw);
    return () => cancelAnimationFrame(animFrameRef.current);
  }, [draw]);

  // ── Web Audio API ──────────────────────────────────────────────────────
  const ensureAudioContext = useCallback(() => {
    const audio = audioRef.current;
    if (!audio || audioCtxRef.current) return;
    const actx = new AudioContext();
    audioCtxRef.current = actx;
    const analyser = actx.createAnalyser();
    analyser.fftSize = 2048;
    analyserRef.current = analyser;
    actx.createMediaElementSource(audio).connect(analyser);
    analyser.connect(actx.destination);
  }, []);

  // ── Playback controls ──────────────────────────────────────────────────
  const togglePlay = useCallback((e: React.MouseEvent) => {
    e.stopPropagation();
    const audio = audioRef.current;
    if (!audio) return;
    ensureAudioContext();
    if (audioCtxRef.current?.state === 'suspended') audioCtxRef.current.resume();
    if (audio.paused) { audio.play(); } else { audio.pause(); }
  }, [ensureAudioContext]);

  const handleSeek = (e: React.MouseEvent) => {
    e.stopPropagation();
    const audio = audioRef.current;
    const bar = progressRef.current;
    if (!audio || !bar || !duration) return;
    const rect = bar.getBoundingClientRect();
    const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    audio.currentTime = ratio * duration;
  };

  const handleCanvasClick = (e: React.MouseEvent) => {
    e.stopPropagation();
    if (!hasStarted) {
      const audio = audioRef.current;
      if (audio) { ensureAudioContext(); audio.play(); }
      return;
    }
    togglePlay(e);
    revealControls();
  };

  // ── Audio event listeners ──────────────────────────────────────────────
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;
    const onPlay = () => { setIsPlaying(true); setHasStarted(true); };
    const onPause = () => setIsPlaying(false);
    const onEnded = () => setIsPlaying(false);
    const onTime = () => setCurrentTime(audio.currentTime);
    const onDur = () => setDuration(audio.duration);
    audio.addEventListener('play', onPlay);
    audio.addEventListener('pause', onPause);
    audio.addEventListener('ended', onEnded);
    audio.addEventListener('timeupdate', onTime);
    audio.addEventListener('durationchange', onDur);
    audio.addEventListener('loadedmetadata', onDur);
    return () => {
      audio.removeEventListener('play', onPlay);
      audio.removeEventListener('pause', onPause);
      audio.removeEventListener('ended', onEnded);
      audio.removeEventListener('timeupdate', onTime);
      audio.removeEventListener('durationchange', onDur);
      audio.removeEventListener('loadedmetadata', onDur);
    };
  }, []);

  useEffect(() => () => {
    cancelAnimationFrame(animFrameRef.current);
    audioCtxRef.current?.close();
  }, []);

  return (
    <div
      ref={containerRef}
      className={cn(
        'relative mt-3 rounded-2xl overflow-hidden border border-border bg-background group',
        className,
      )}
      style={{ aspectRatio: '16 / 9' }}
      onMouseMove={revealControls}
      onMouseLeave={() => { if (isPlaying) scheduleHide(); }}
      onClick={(e) => e.stopPropagation()}
    >
      {/* Hidden audio element */}
      <audio ref={audioRef} preload="metadata" crossOrigin="anonymous" className="hidden">
        {mime ? <source src={src} type={mime} /> : <source src={src} />}
      </audio>

      {/* Sinewave canvas — fills the entire box */}
      <canvas
        ref={canvasRef}
        className="absolute inset-0 w-full h-full"
        onClick={handleCanvasClick}
        style={{ cursor: 'pointer' }}
      />

      {/* Avatar — centred over the canvas */}
      <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
        <div
          className={cn(
            'rounded-full ring-4 transition-all duration-300',
            isPlaying
              ? 'ring-primary/40 shadow-[0_0_28px_8px_hsl(var(--primary)/0.4)]'
              : 'ring-border',
          )}
        >
          <Avatar className="size-20 border-2 border-white/20" shape={avatarShape}>
            <AvatarImage src={avatarUrl} alt={avatarFallback} />
            <AvatarFallback className="bg-primary/20 text-primary text-2xl font-semibold">
              {avatarFallback}
            </AvatarFallback>
          </Avatar>
        </div>
      </div>

      {/* Big centred play button before first play — identical to VideoPlayer */}
      {!hasStarted && (
        <div
          className="absolute inset-0 flex items-center justify-center bg-black/30 cursor-pointer"
          onClick={handleCanvasClick}
        >
          <div className="size-16 rounded-full bg-black/60 flex items-center justify-center backdrop-blur-sm">
            <Play className="size-8 text-white ml-1" fill="white" />
          </div>
        </div>
      )}

      {/* Bottom control bar — identical markup to VideoPlayer */}
      {hasStarted && (
        <div
          className={cn(
            'absolute bottom-0 left-0 right-0 transition-opacity duration-200',
            'bg-gradient-to-t from-black/80 via-black/40 to-transparent pt-8 pb-2 px-3',
            showControls ? 'opacity-100' : 'opacity-0 pointer-events-none',
          )}
        >
          {/* Progress bar */}
          <div
            ref={progressRef}
            className="w-full h-1 bg-white/30 rounded-full cursor-pointer mb-2 group/progress"
            onClick={handleSeek}
          >
            <div
              className="h-full bg-primary rounded-full relative"
              style={{ width: `${progress}%` }}
            >
              <div className="absolute right-0 top-1/2 -translate-y-1/2 size-3 bg-primary rounded-full opacity-0 group-hover/progress:opacity-100 transition-opacity" />
            </div>
          </div>

          {/* Controls row */}
          <div className="flex items-center gap-3">
            <button
              onClick={togglePlay}
              className="text-white hover:text-white/80 transition-colors"
              aria-label={isPlaying ? 'Pause' : 'Play'}
            >
              {isPlaying
                ? <Pause className="size-5" fill="white" />
                : <Play className="size-5 ml-0.5" fill="white" />}
            </button>

            {/* Volume: icon toggles mute, slider sets level */}
            <div className="flex items-center gap-1.5 group/vol">
              <button
                onClick={toggleMute}
                className="text-white hover:text-white/80 transition-colors shrink-0"
                aria-label={isMuted ? 'Unmute' : 'Mute'}
              >
                {isMuted || volume === 0
                  ? <VolumeX className="size-5" />
                  : volume < 0.5
                    ? <Volume1 className="size-5" />
                    : <Volume2 className="size-5" />}
              </button>
              <input
                type="range"
                min={0}
                max={1}
                step={0.02}
                value={isMuted ? 0 : volume}
                onChange={handleVolumeChange}
                onClick={(e) => e.stopPropagation()}
                aria-label="Volume"
                className={cn(
                  'w-0 opacity-0 group-hover/vol:w-16 group-hover/vol:opacity-100',
                  'transition-all duration-200 cursor-pointer accent-white h-1',
                )}
              />
            </div>

            <span className="text-white text-xs tabular-nums min-w-0">
              {formatTime(currentTime)} / {formatTime(duration)}
            </span>

            <div className="flex-1" />
          </div>
        </div>
      )}
    </div>
  );
}
