import { useEffect, useRef, useState } from "react";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Slider } from "@/components/ui/slider";
import { Play, Pause, Volume2, VolumeX, Download, RotateCcw } from "lucide-react";
import { cn } from "@/lib/utils";

interface WaveformVisualizerProps {
  label: string;
  type: "source" | "target" | "result";
  audioUrl?: string;
  waveformData?: number[];
  showDownload?: boolean;
  onDownload?: () => void;
}

export function WaveformVisualizer({
  label,
  type,
  audioUrl,
  waveformData,
  showDownload = false,
  onDownload,
}: WaveformVisualizerProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const audioRef = useRef<HTMLAudioElement>(null);
  const animationRef = useRef<number>();
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [volume, setVolume] = useState(0.8);
  const [isMuted, setIsMuted] = useState(false);

  const colorMap = {
    source: { primary: "hsl(262, 83%, 58%)", secondary: "hsl(262, 83%, 75%)" },
    target: { primary: "hsl(280, 65%, 52%)", secondary: "hsl(280, 65%, 70%)" },
    result: { primary: "hsl(145, 65%, 45%)", secondary: "hsl(145, 65%, 65%)" },
  };

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || !waveformData || waveformData.length === 0) return;

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

    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    ctx.scale(dpr, dpr);

    const drawWaveform = () => {
      const width = rect.width;
      const height = rect.height;
      const centerY = height / 2;
      const barWidth = Math.max(2, width / waveformData.length);
      const gap = 1;

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

      const playedPercent = duration > 0 ? currentTime / duration : 0;
      const playedBars = Math.floor(waveformData.length * playedPercent);

      waveformData.forEach((value, i) => {
        const x = i * barWidth;
        const barHeight = Math.max(2, value * (height * 0.8));

        const isPlayed = i < playedBars;
        ctx.fillStyle = isPlayed
          ? colorMap[type].primary
          : colorMap[type].secondary;

        ctx.beginPath();
        ctx.roundRect(
          x + gap / 2,
          centerY - barHeight / 2,
          barWidth - gap,
          barHeight,
          2
        );
        ctx.fill();
      });

      animationRef.current = requestAnimationFrame(drawWaveform);
    };

    drawWaveform();

    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
    };
  }, [waveformData, currentTime, duration, type]);

  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;

    const handleTimeUpdate = () => setCurrentTime(audio.currentTime);
    const handleLoadedMetadata = () => setDuration(audio.duration);
    const handleEnded = () => setIsPlaying(false);

    audio.addEventListener("timeupdate", handleTimeUpdate);
    audio.addEventListener("loadedmetadata", handleLoadedMetadata);
    audio.addEventListener("ended", handleEnded);

    return () => {
      audio.removeEventListener("timeupdate", handleTimeUpdate);
      audio.removeEventListener("loadedmetadata", handleLoadedMetadata);
      audio.removeEventListener("ended", handleEnded);
    };
  }, [audioUrl]);

  useEffect(() => {
    if (audioRef.current) {
      audioRef.current.volume = isMuted ? 0 : volume;
    }
  }, [volume, isMuted]);

  const togglePlay = () => {
    const audio = audioRef.current;
    if (!audio) return;

    if (isPlaying) {
      audio.pause();
    } else {
      audio.play();
    }
    setIsPlaying(!isPlaying);
  };

  const handleSeek = (values: number[]) => {
    const audio = audioRef.current;
    if (!audio || !duration) return;
    audio.currentTime = (values[0] / 100) * duration;
    setCurrentTime(audio.currentTime);
  };

  const handleReset = () => {
    const audio = audioRef.current;
    if (!audio) return;
    audio.currentTime = 0;
    setCurrentTime(0);
    setIsPlaying(false);
    audio.pause();
  };

  const formatTime = (seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, "0")}`;
  };

  const hasAudio = !!audioUrl;

  return (
    <Card className="overflow-visible">
      <CardHeader className="flex flex-row items-center justify-between gap-2 py-3 px-4">
        <div className="flex items-center gap-2">
          <div
            className={cn(
              "w-2 h-2 rounded-full",
              type === "source" && "bg-primary",
              type === "target" && "bg-chart-2",
              type === "result" && "bg-green-500"
            )}
          />
          <span className="text-sm font-medium">{label}</span>
        </div>
        {showDownload && hasAudio && (
          <Button
            size="icon"
            variant="ghost"
            onClick={onDownload}
            data-testid={`button-download-${type}`}
          >
            <Download className="h-4 w-4" />
          </Button>
        )}
      </CardHeader>
      <CardContent className="px-4 pb-4 pt-0">
        {audioUrl && <audio ref={audioRef} src={audioUrl} preload="metadata" />}

        <div
          className={cn(
            "relative h-24 bg-accent/30 rounded-md overflow-hidden mb-3",
            !hasAudio && "flex items-center justify-center"
          )}
        >
          {hasAudio && waveformData ? (
            <canvas
              ref={canvasRef}
              className="w-full h-full"
              data-testid={`canvas-waveform-${type}`}
            />
          ) : (
            <p className="text-xs text-muted-foreground">No audio loaded</p>
          )}
        </div>

        {hasAudio && (
          <div className="space-y-3">
            <Slider
              value={[duration > 0 ? (currentTime / duration) * 100 : 0]}
              onValueChange={handleSeek}
              max={100}
              step={0.1}
              className="cursor-pointer"
              data-testid={`slider-seek-${type}`}
            />

            <div className="flex items-center justify-between gap-2">
              <div className="flex items-center gap-1">
                <Button
                  size="icon"
                  variant="ghost"
                  onClick={togglePlay}
                  data-testid={`button-play-${type}`}
                >
                  {isPlaying ? (
                    <Pause className="h-4 w-4" />
                  ) : (
                    <Play className="h-4 w-4" />
                  )}
                </Button>
                <Button
                  size="icon"
                  variant="ghost"
                  onClick={handleReset}
                  data-testid={`button-reset-${type}`}
                >
                  <RotateCcw className="h-4 w-4" />
                </Button>
              </div>

              <span className="text-xs font-mono text-muted-foreground" data-testid={`text-time-${type}`}>
                {formatTime(currentTime)} / {formatTime(duration)}
              </span>

              <div className="flex items-center gap-2">
                <Button
                  size="icon"
                  variant="ghost"
                  onClick={() => setIsMuted(!isMuted)}
                  data-testid={`button-mute-${type}`}
                >
                  {isMuted ? (
                    <VolumeX className="h-4 w-4" />
                  ) : (
                    <Volume2 className="h-4 w-4" />
                  )}
                </Button>
                <Slider
                  value={[isMuted ? 0 : volume * 100]}
                  onValueChange={(v) => {
                    setVolume(v[0] / 100);
                    if (v[0] > 0) setIsMuted(false);
                  }}
                  max={100}
                  className="w-20"
                  data-testid={`slider-volume-${type}`}
                />
              </div>
            </div>
          </div>
        )}
      </CardContent>
    </Card>
  );
}
