"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { Play, Pause, Download, Volume2, VolumeX, FileArchive } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

function formatTime(sec: number): string {
  if (!Number.isFinite(sec) || sec < 0) return "0:00";
  const m = Math.floor(sec / 60);
  const s = Math.floor(sec % 60);
  return `${m}:${String(s).padStart(2, "0")}`;
}

/**
 * Deterministic pseudo-waveform bars derived from the source URL — a lightweight,
 * decode-free stand-in so the player always renders a waveform without fetching
 * and decoding the audio. Purely decorative; progress fills it as playback moves.
 */
function bars(seed: string, count: number): number[] {
  let h = 2166136261;
  for (let i = 0; i < seed.length; i++) {
    h ^= seed.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  const out: number[] = [];
  for (let i = 0; i < count; i++) {
    h ^= h << 13;
    h ^= h >>> 17;
    h ^= h << 5;
    const n = (h >>> 0) / 4294967295;
    out.push(0.22 + n * 0.78);
  }
  return out;
}

export interface WaveformPlayerProps {
  /** Fully-qualified audio source URL (authed asset route or public route). */
  src: string;
  /** Optional direct download URL for the MP3 (defaults to `${src}?download=1`). */
  downloadUrl?: string;
  /** Server-known duration (seconds) used until metadata loads. */
  durationSec?: number | null;
  /** When set, shows a "Download everything (.zip)" button hitting this URL. */
  exportUrl?: string;
  /** Hide the download controls (e.g. on public pages). */
  hideDownloads?: boolean;
  className?: string;
}

export function WaveformPlayer({
  src,
  downloadUrl,
  durationSec,
  exportUrl,
  hideDownloads = false,
  className,
}: WaveformPlayerProps) {
  const audioRef = useRef<HTMLAudioElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [playing, setPlaying] = useState(false);
  const [current, setCurrent] = useState(0);
  const [duration, setDuration] = useState(durationSec ?? 0);
  const [volume, setVolume] = useState(1);
  const [muted, setMuted] = useState(false);

  const waveform = useRef(bars(src, 96)).current;
  const progress = duration > 0 ? current / duration : 0;

  // Paint the canvas waveform with a played/unplayed split.
  const draw = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const dpr = window.devicePixelRatio || 1;
    const w = canvas.clientWidth;
    const h = canvas.clientHeight;
    if (canvas.width !== w * dpr || canvas.height !== h * dpr) {
      canvas.width = w * dpr;
      canvas.height = h * dpr;
    }
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    ctx.clearRect(0, 0, w, h);

    const styles = getComputedStyle(canvas);
    const played = `hsl(${styles.getPropertyValue("--brand").trim() || "21 100% 45%"})`;
    const unplayed = `hsl(${styles.getPropertyValue("--border").trim() || "0 0% 89%"})`;

    const n = waveform.length;
    const gap = 2;
    const barW = Math.max(1, (w - gap * (n - 1)) / n);
    for (let i = 0; i < n; i++) {
      const bh = waveform[i] * h;
      const x = i * (barW + gap);
      const y = (h - bh) / 2;
      ctx.fillStyle = (i + 0.5) / n <= progress ? played : unplayed;
      ctx.beginPath();
      const r = Math.min(barW / 2, 1.5);
      if (typeof ctx.roundRect === "function") ctx.roundRect(x, y, barW, bh, r);
      else ctx.rect(x, y, barW, bh);
      ctx.fill();
    }
  }, [waveform, progress]);

  useEffect(() => {
    draw();
  }, [draw]);

  useEffect(() => {
    const onResize = () => draw();
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [draw]);

  function togglePlay() {
    const a = audioRef.current;
    if (!a) return;
    if (a.paused) void a.play();
    else a.pause();
  }

  function seekToClientX(clientX: number, el: HTMLElement) {
    const a = audioRef.current;
    if (!a || !duration) return;
    const rect = el.getBoundingClientRect();
    const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
    a.currentTime = ratio * duration;
    setCurrent(a.currentTime);
  }

  function toggleMute() {
    const a = audioRef.current;
    if (!a) return;
    a.muted = !a.muted;
    setMuted(a.muted);
  }

  function onVolume(v: number) {
    const a = audioRef.current;
    setVolume(v);
    if (a) {
      a.volume = v;
      a.muted = v === 0;
      setMuted(v === 0);
    }
  }

  return (
    <div className={cn("space-y-4", className)}>
      {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
      <audio
        ref={audioRef}
        src={src}
        preload="metadata"
        onPlay={() => setPlaying(true)}
        onPause={() => setPlaying(false)}
        onTimeUpdate={(e) => setCurrent(e.currentTarget.currentTime)}
        onLoadedMetadata={(e) => {
          if (Number.isFinite(e.currentTarget.duration)) setDuration(e.currentTarget.duration);
        }}
        onEnded={() => setPlaying(false)}
      />

      <div className="flex items-center gap-3">
        <Button
          type="button"
          variant="default"
          size="icon"
          onClick={togglePlay}
          aria-label={playing ? "Pause" : "Play"}
          className="h-12 w-12 shrink-0"
        >
          {playing ? <Pause className="h-5 w-5" /> : <Play className="h-5 w-5 translate-x-0.5" />}
        </Button>

        <div className="min-w-0 flex-1">
          {/* Canvas waveform doubles as the scrubber. */}
          <div
            role="slider"
            aria-label="Seek"
            aria-valuemin={0}
            aria-valuemax={Math.round(duration)}
            aria-valuenow={Math.round(current)}
            tabIndex={0}
            className="relative h-12 w-full cursor-pointer touch-none select-none"
            onPointerDown={(e) => {
              (e.target as HTMLElement).setPointerCapture?.(e.pointerId);
              seekToClientX(e.clientX, e.currentTarget);
            }}
            onPointerMove={(e) => {
              if (e.buttons === 1) seekToClientX(e.clientX, e.currentTarget);
            }}
            onKeyDown={(e) => {
              const a = audioRef.current;
              if (!a || !duration) return;
              if (e.key === "ArrowRight") a.currentTime = Math.min(duration, a.currentTime + 5);
              else if (e.key === "ArrowLeft") a.currentTime = Math.max(0, a.currentTime - 5);
              else if (e.key === " ") {
                e.preventDefault();
                togglePlay();
              }
            }}
          >
            <canvas ref={canvasRef} className="h-full w-full" />
          </div>
          <div className="mt-1 flex items-center justify-between text-xs tabular-nums text-muted-foreground">
            <span>{formatTime(current)}</span>
            <span>{formatTime(duration)}</span>
          </div>
        </div>

        <div className="hidden items-center gap-2 sm:flex">
          <button
            type="button"
            onClick={toggleMute}
            aria-label={muted ? "Unmute" : "Mute"}
            className="rounded-full p-1.5 text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
          >
            {muted || volume === 0 ? <VolumeX className="h-4 w-4" /> : <Volume2 className="h-4 w-4" />}
          </button>
          <input
            type="range"
            min={0}
            max={1}
            step={0.05}
            value={muted ? 0 : volume}
            onChange={(e) => onVolume(Number(e.target.value))}
            aria-label="Volume"
            className="h-1 w-20 cursor-pointer accent-brand"
          />
        </div>
      </div>

      {!hideDownloads && (
        <div className="flex flex-wrap items-center gap-2">
          <Button asChild variant="outline" size="sm">
            <a href={downloadUrl ?? `${src}?download=1`} download>
              <Download className="h-4 w-4" /> Download MP3
            </a>
          </Button>
          {exportUrl && (
            <Button asChild variant="ghost" size="sm">
              <a href={exportUrl}>
                <FileArchive className="h-4 w-4" /> Download everything (.zip)
              </a>
            </Button>
          )}
        </div>
      )}
    </div>
  );
}
