"use client";

import { Clock, AlertCircle, Loader2, Download, Music } from "lucide-react";
import { Id } from "@/convex/_generated/dataModel";
import { useState, useEffect, useRef, useMemo, memo } from "react";
import { usePlayback } from "@/contexts/PlaybackContext";
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";

interface MidiAtomRowProps {
  atom: {
    _id: string;
    type: string;
    status: "pending" | "processing" | "streaming" | "completed" | "failed";
    progress: number;
    metadata: {
      sunoClipId?: string;
      title?: string;
      midiUrl?: string;
      midiData?: {
        state: string;
        instruments: Array<{
          name: string;
          notes: Array<{
            pitch: number; // MIDI note number (0-127)
            start: number; // Time in seconds
            end: number; // Time in seconds
            velocity: number; // 0-1
          }>;
        }>;
      };
    };
  };
  currentUserId?: string;
}

// MIDI note number to note name mapping
const NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
function midiNoteToName(midiNote: number): string {
  const octave = Math.floor(midiNote / 12) - 1;
  const noteName = NOTE_NAMES[midiNote % 12];
  return `${noteName}${octave}`;
}

export const MidiAtomRow = memo(function MidiAtomRow({ atom }: MidiAtomRowProps) {
  console.log('[MidiAtomRow] Component rendering for atom:', atom._id);

  const [isExpanded, setIsExpanded] = useState(true); // Auto-expand by default
  const metadata = atom.metadata;
  const isGenerating = ["pending", "processing"].includes(atom.status);
  const hasFailed = atom.status === "failed";
  const isCompleted = atom.status === "completed";

  // Get playback context
  const { currentTrackId, isPlaying, audioRef, seek } = usePlayback();
  const staticCanvasRef = useRef<HTMLCanvasElement>(null); // Static canvas for notes/grid
  const playheadCanvasRef = useRef<HTMLCanvasElement>(null); // Overlay canvas for playhead
  const containerRef = useRef<HTMLDivElement>(null);

  // Get provenance to find source song
  const provenance = useQuery(api.atoms.getProvenanceByAtom, { atomId: atom._id as Id<"atoms"> });
  const sourceAtomId = provenance?.inputs?.[0]?.atom?._id;

  // Check if we're playing the source song
  const isPlayingSourceSong = currentTrackId === sourceAtomId && isPlaying;

  // Process MIDI data for visualization - MEMOIZED
  const midiData = metadata.midiData;
  const { allNotes, minPitch, maxPitch, maxTime } = useMemo(() => {
    const notes: Array<{ pitch: number; start: number; end: number; velocity: number; instrument: string }> = [];
    let min = 127;
    let max = 0;
    let time = 0;

    if (midiData?.instruments) {
      midiData.instruments.forEach((instrument) => {
        instrument.notes.forEach((note) => {
          notes.push({
            ...note,
            instrument: instrument.name,
          });
          min = Math.min(min, note.pitch);
          max = Math.max(max, note.pitch);
          time = Math.max(time, note.end);
        });
      });
    }

    return { allNotes: notes, minPitch: min, maxPitch: max, maxTime: time };
  }, [midiData]);

  const pitchRange = maxPitch - minPitch + 1;
  const PIXELS_PER_SECOND = 50; // Scale for time axis
  const NOTE_HEIGHT = 8; // Height of each note row in pixels
  const canvasWidth = Math.max(maxTime * PIXELS_PER_SECOND, 400);
  const canvasHeight = pitchRange * NOTE_HEIGHT;

  // Color mapping for instruments - MEMOIZED
  const getInstrumentColor = useMemo(() => {
    const cache: Record<string, string> = {};
    return (instrumentName: string) => {
      if (!cache[instrumentName]) {
        const hash = instrumentName.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
        const hue = hash % 360;
        cache[instrumentName] = `hsl(${hue}, 70%, 60%)`;
      }
      return cache[instrumentName];
    };
  }, []);

  // Draw the static piano roll (notes + grid) once
  useEffect(() => {
    if (!staticCanvasRef.current || !isCompleted || allNotes.length === 0) return;

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

    console.log('[MidiAtomRow] Drawing static canvas for atom:', atom._id);

    // Handle high-DPI displays (Retina)
    const dpr = window.devicePixelRatio || 1;
    canvas.width = canvasWidth * dpr;
    canvas.height = canvasHeight * dpr;
    canvas.style.width = `${canvasWidth}px`;
    canvas.style.height = `${canvasHeight}px`;
    ctx.scale(dpr, dpr);

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

    // Draw grid lines for time (every second)
    ctx.strokeStyle = "#e5e7eb"; // gray-200
    ctx.fillStyle = "#9ca3af"; // gray-400
    ctx.font = "9px monospace";
    ctx.lineWidth = 1;
    for (let i = 0; i <= Math.ceil(maxTime); i++) {
      const x = i * PIXELS_PER_SECOND;
      ctx.beginPath();
      ctx.moveTo(x, 0);
      ctx.lineTo(x, canvasHeight);
      ctx.stroke();
      ctx.fillText(`${i}s`, x + 2, 10);
    }

    // Draw grid lines for pitches (every octave)
    for (let i = 0; i <= Math.ceil(pitchRange / 12); i++) {
      const pitch = minPitch + (i * 12);
      if (pitch > maxPitch) break;
      const y = (maxPitch - pitch) * NOTE_HEIGHT;
      ctx.beginPath();
      ctx.moveTo(0, y);
      ctx.lineTo(canvasWidth, y);
      ctx.stroke();
      ctx.fillText(midiNoteToName(pitch), 2, y - 2);
    }

    // Draw notes
    allNotes.forEach((note) => {
      const x = note.start * PIXELS_PER_SECOND;
      const width = Math.max((note.end - note.start) * PIXELS_PER_SECOND, 2);
      const y = (maxPitch - note.pitch) * NOTE_HEIGHT;
      const height = NOTE_HEIGHT - 1;
      const opacity = 0.3 + (note.velocity * 0.7);

      ctx.fillStyle = getInstrumentColor(note.instrument);
      ctx.globalAlpha = opacity;
      ctx.fillRect(x, y, width, height);
      ctx.globalAlpha = 1;
    });
  }, [canvasWidth, canvasHeight, allNotes, minPitch, maxPitch, maxTime, isCompleted, getInstrumentColor, atom._id]);

  // Initialize playhead canvas once
  useEffect(() => {
    if (!playheadCanvasRef.current) return;

    const canvas = playheadCanvasRef.current;

    // Handle high-DPI displays (Retina) - set once
    const dpr = window.devicePixelRatio || 1;
    canvas.width = canvasWidth * dpr;
    canvas.height = canvasHeight * dpr;
    canvas.style.width = `${canvasWidth}px`;
    canvas.style.height = `${canvasHeight}px`;

    const ctx = canvas.getContext("2d");
    if (ctx) {
      ctx.scale(dpr, dpr);
    }
  }, [canvasWidth, canvasHeight]);

  // Draw playhead on every frame (but don't recreate canvas)
  useEffect(() => {
    if (!playheadCanvasRef.current || !isPlayingSourceSong) return;

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

    let animationFrameId: number;
    let startTime: number | null = null;
    let audioStartTime: number | null = null;

    const drawPlayhead = (timestamp: number) => {
      // Clear entire canvas
      ctx.clearRect(0, 0, canvasWidth, canvasHeight);

      if (audioRef.current && !audioRef.current.paused) {
        // Initialize timing on first frame
        if (startTime === null) {
          startTime = timestamp;
          audioStartTime = audioRef.current.currentTime;
        }

        // Calculate elapsed time since animation started
        const elapsedMs = timestamp - startTime;
        const elapsedSeconds = elapsedMs / 1000;

        // Interpolate position: use audio time + elapsed animation time for smoother playback
        // This compensates for audio element's discrete time updates
        const time = (audioStartTime || 0) + elapsedSeconds;
        const playheadX = time * PIXELS_PER_SECOND;

        // Periodically resync with actual audio position (every 500ms)
        if (elapsedMs > 500) {
          startTime = timestamp;
          audioStartTime = audioRef.current.currentTime;
        }

        // Draw playhead line
        ctx.strokeStyle = "#ef4444"; // red-500
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.moveTo(playheadX, 0);
        ctx.lineTo(playheadX, canvasHeight);
        ctx.stroke();

        // Draw playhead handle
        ctx.fillStyle = "#ef4444";
        ctx.beginPath();
        ctx.arc(playheadX, 6, 6, 0, Math.PI * 2);
        ctx.fill();
      }

      animationFrameId = requestAnimationFrame(drawPlayhead);
    };

    animationFrameId = requestAnimationFrame(drawPlayhead);
    return () => cancelAnimationFrame(animationFrameId);
  }, [isPlayingSourceSong, canvasWidth, canvasHeight, audioRef]);

  // Auto-scroll to keep playhead visible (throttled to avoid jitter)
  useEffect(() => {
    if (!isPlayingSourceSong || !containerRef.current || !audioRef.current) return;

    const container = containerRef.current;
    let lastScrollTime = 0;
    let animationFrameId: number;

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

      const now = Date.now();
      // Only scroll every 100ms to avoid jitter from 'smooth' behavior fighting with itself
      if (now - lastScrollTime > 100) {
        const time = audioRef.current.currentTime;
        const playheadLeft = time * PIXELS_PER_SECOND;
        const containerWidth = container.clientWidth;
        const currentScroll = container.scrollLeft;

        // Only auto-scroll if playhead is getting close to edges
        const scrollMargin = containerWidth * 0.3; // 30% from edge triggers scroll
        const playheadRelativePos = playheadLeft - currentScroll;

        if (playheadRelativePos < scrollMargin || playheadRelativePos > containerWidth - scrollMargin) {
          // Center playhead
          const targetScroll = playheadLeft - containerWidth / 2;
          container.scrollLeft = Math.max(0, targetScroll);
          lastScrollTime = now;
        }
      }

      animationFrameId = requestAnimationFrame(updateScroll);
    };

    animationFrameId = requestAnimationFrame(updateScroll);
    return () => cancelAnimationFrame(animationFrameId);
  }, [isPlayingSourceSong, audioRef]);

  // Handle double-click to seek
  const handleDoubleClick = (e: React.MouseEvent<HTMLDivElement>) => {
    if (!staticCanvasRef.current || !sourceAtomId || !containerRef.current) return;

    const canvas = staticCanvasRef.current;
    const rect = canvas.getBoundingClientRect();
    const container = containerRef.current;

    // Calculate click position relative to canvas, accounting for scroll
    const clickX = e.clientX - rect.left + container.scrollLeft;
    const timeInSeconds = clickX / PIXELS_PER_SECOND;

    // Seek to the clicked position
    seek(timeInSeconds * 1000); // Convert to milliseconds
  };

  const handleDownload = () => {
    if (metadata.midiUrl) {
      window.open(metadata.midiUrl, "_blank");
    } else if (midiData) {
      // If we have JSON data but no URL, download as JSON
      const blob = new Blob([JSON.stringify(midiData, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `${metadata.title || "midi"}.json`;
      a.click();
      URL.revokeObjectURL(url);
    }
  };

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

  return (
    <div className="midi-atom-row rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 w-full min-w-0">
      {/* Header */}
      <div
        className="flex items-center gap-3 p-3 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors cursor-pointer"
        onClick={() => isCompleted && setIsExpanded(!isExpanded)}
      >
        {/* MIDI Icon */}
        <div className="relative w-16 h-16 flex-shrink-0 rounded-md overflow-hidden bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center">
          {isGenerating && (
            <Loader2 className="w-8 h-8 animate-spin text-white" />
          )}
          {hasFailed && (
            <AlertCircle className="w-8 h-8 text-white" />
          )}
          {isCompleted && (
            <Music className="w-8 h-8 text-white" />
          )}
        </div>

        {/* MIDI Info */}
        <div className="flex-1 min-w-0">
          <div className="text-sm font-medium truncate text-gray-900 dark:text-gray-100">
            {metadata.title || "MIDI File"}
          </div>
          <div className="text-xs text-gray-600 dark:text-gray-400">
            {midiData?.instruments?.length ? `${midiData.instruments.length} instruments` : "MIDI"}
          </div>
          {isGenerating && (
            <div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
              {atom.status === "pending" && "⏳ Queued..."}
              {atom.status === "processing" && "🎹 Generating MIDI..."}
            </div>
          )}
          {hasFailed && (
            <div className="text-xs text-red-600 dark:text-red-400 mt-1">
              Generation failed
            </div>
          )}
          {isGenerating && (
            <div className="w-full h-1 bg-gray-200 dark:bg-gray-600 rounded-full mt-2 overflow-hidden">
              <div
                className="h-full bg-purple-500 transition-all duration-300"
                style={{ width: `${atom.progress}%` }}
              />
            </div>
          )}
        </div>

        {/* Stats */}
        <div className="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
          {maxTime > 0 && (
            <div className="flex items-center gap-1">
              <Clock size={12} />
              {formatDuration(maxTime)}
            </div>
          )}
          {allNotes.length > 0 && (
            <div className="text-xs">
              {allNotes.length} notes
            </div>
          )}
        </div>

        {/* Download Button */}
        {(metadata.midiUrl || midiData) && (
          <button
            className="p-2 rounded-full transition-colors text-gray-400 hover:text-purple-600 dark:text-gray-500 dark:hover:text-purple-400"
            onClick={(e) => {
              e.stopPropagation();
              handleDownload();
            }}
            aria-label="Download MIDI"
            title="Download MIDI"
          >
            <Download size={20} />
          </button>
        )}
      </div>

      {/* Piano Roll Visualization (Canvas) */}
      {isExpanded && isCompleted && allNotes.length > 0 && (
        <div className="border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50">
          <div className="p-4">
            <div className="text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
              Piano Roll ({midiNoteToName(minPitch)} - {midiNoteToName(maxPitch)})
              {sourceAtomId && <span className="ml-2 text-gray-500">Double-click to seek</span>}
            </div>

            {/* Canvas container with scrolling */}
            <div
              ref={containerRef}
              className="overflow-x-auto overflow-y-auto rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800"
              style={{ maxHeight: "400px" }}
              onDoubleClick={handleDoubleClick}
            >
              <div className="relative" style={{ width: canvasWidth, height: canvasHeight }}>
                {/* Static canvas for notes and grid */}
                <canvas
                  ref={staticCanvasRef}
                  className="absolute top-0 left-0 cursor-pointer"
                  style={{ display: "block" }}
                />
                {/* Overlay canvas for playhead */}
                <canvas
                  ref={playheadCanvasRef}
                  className="absolute top-0 left-0 pointer-events-none"
                  style={{ display: "block" }}
                />
              </div>
            </div>

            {/* Instrument legend */}
            <div className="mt-3 flex flex-wrap gap-2">
              {midiData?.instruments.map((instrument, idx) => (
                <div key={idx} className="flex items-center gap-1.5 text-xs">
                  <div
                    className="w-3 h-3 rounded-sm"
                    style={{ backgroundColor: getInstrumentColor(instrument.name) }}
                  />
                  <span className="text-gray-600 dark:text-gray-400">
                    {instrument.name} ({instrument.notes.length})
                  </span>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}, (prevProps, nextProps) => {
  // Custom comparison function for React.memo
  // Only re-render if the atom actually changed
  return (
    prevProps.atom._id === nextProps.atom._id &&
    prevProps.atom.status === nextProps.atom.status &&
    prevProps.atom.progress === nextProps.atom.progress &&
    prevProps.atom.metadata === nextProps.atom.metadata &&
    prevProps.currentUserId === nextProps.currentUserId
  );
});
