'use client';

import React, { useRef, useState } from 'react';

import { toast } from '@/components/toast/Toast';
import type { components } from '@/lib/gen';

import { colors, timeRangeColorSets } from '../constants';
import { useMarketplaceProject } from './MarketplaceProjectProvider';

type MediaReference = components['schemas']['MediaResponse'];
type Message = components['schemas']['MessageResponse'];

const getHashValue = (seed: string) => {
  const x = Math.sin(
    seed.split('').reduce((acc, char) => {
      return acc + char.charCodeAt(0);
    }, 0) * 10000
  );
  return x - Math.floor(x);
};

const pickRandomColor = (seed: string) => {
  const hashValue = getHashValue(seed);
  return colors[Math.floor(hashValue * colors.length) % colors.length];
};

// Get consistent tonal color set for a time range message based on its ID
const getTimeRangeColorSet = (messageId: string) => {
  const hashValue = getHashValue(messageId);
  return timeRangeColorSets[
    Math.floor(hashValue * timeRangeColorSets.length) %
      timeRangeColorSets.length
  ];
};

// Get unique time ranges for filter buttons
const getUniqueTimeRanges = (messages: Message[]) => {
  const timeRangeMessages = messages.filter((msg) => msg.time_range);

  // Get all unique time ranges (grouped by start/end values) sorted by first occurrence
  const uniqueTimeRanges = timeRangeMessages.reduce(
    (acc, msg) => {
      if (!msg.time_range) return acc;

      const key = `${(msg.time_range.start as number).toFixed(1)}-${(msg.time_range.end as number).toFixed(1)}`;
      if (!acc[key]) {
        acc[key] = {
          start: msg.time_range.start as number,
          end: msg.time_range.end as number,
          firstMessageId: msg.id,
          firstCreatedAt: msg.created_at,
        };
      }
      return acc;
    },
    {} as Record<
      string,
      {
        start: number;
        end: number;
        firstMessageId: string;
        firstCreatedAt: string;
      }
    >
  );

  // Sort unique time ranges by start time (chronological order)
  const sortedTimeRanges = Object.values(uniqueTimeRanges).sort(
    (a, b) => a.start - b.start
  );

  // Filter out time ranges that are fully contained within other time ranges
  // Keep the most specific (nested/child) ranges and hide parent ranges
  return sortedTimeRanges.filter((range) => {
    // Check if this range is fully contained within any other range
    const isFullyContainedInAnother = sortedTimeRanges.some((otherRange) => {
      // Don't compare a range with itself
      if (range === otherRange) return false;

      // Check if 'range' is fully contained within 'otherRange'
      const isContained =
        range.start >= otherRange.start && range.end <= otherRange.end;

      // If contained, we need to check if they're not the same range
      const isSameRange =
        Math.abs(range.start - otherRange.start) < 0.1 &&
        Math.abs(range.end - otherRange.end) < 0.1;

      return isContained && !isSameRange;
    });

    // Keep ranges that are NOT fully contained in another range
    return !isFullyContainedInAnother;
  });
};

// Timeline component with selection pane (like studio)
const TimelineWithSelectionPane = ({
  audioUrl,
  className = '',
  onSelectionChange,
  selection,
  clipId,
  isLarge = false,
  messages = [],
  setSelectedTimeRange,
  selectedTimeRange,
  waveformColor = '#726e6c',
  playButtonColor = '#726e6c',
  onDurationChange,
  onTimeRangeChange,
  playableRanges,
}: {
  audioUrl: string;
  className?: string;
  onSelectionChange?: (change: { start: number; end?: number } | null) => void;
  selection?: { start: number; end?: number };
  clipId?: string;
  isLarge?: boolean;
  messages?: Message[];
  setSelectedTimeRange?: (
    timeRange: { start: number; end: number } | null
  ) => void;
  selectedTimeRange?: { start: number; end: number } | null;
  waveformColor?: string;
  playButtonColor?: string;
  onDurationChange?: (duration: number) => void;
  onTimeRangeChange?: (
    timeRange: { start: number; end: number } | null
  ) => void;
  playableRanges?: Array<{ start: number; end: number }>;
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const audioRef = useRef<HTMLAudioElement>(null);
  const currentPlayingTimeRangeRef = useRef<{
    start: number;
    end: number;
    mediaId: string;
  } | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [waveformData, setWaveformData] = useState<number[]>([]);
  const [duration, setDuration] = useState<number>(0);
  const [isDragging, setIsDragging] = useState(false);
  const [startPosition, setStartPosition] = useState<number | null>(null);
  const [endPosition, setEndPosition] = useState<number | null>(null);
  const [inputStartPosition, setInputStartPosition] = useState<number | null>(
    null
  );
  const [currentWaveformColor, setCurrentWaveformColor] =
    useState<string>(waveformColor);
  const justCompletedDragRef = useRef(false);

  // Audio playback state
  const [isPlaying, setIsPlaying] = useState(false);
  const [progress, setProgress] = useState(0);

  // Store dot positions for click detection
  const dotPositionsRef = useRef<
    Array<{
      x: number;
      y: number;
      radius: number;
      timeRange: { start: number; end: number };
    }>
  >([]);

  // Get project ID from URL params
  const { setCurrentPlayingTimeRange } = useMarketplaceProject();

  // Track the last interacted waveform for spacebar handling
  const setLastInteractedWaveform = (mediaId: string) => {
    window.dispatchEvent(
      new CustomEvent('setLastInteractedWaveform', { detail: { mediaId } })
    );
  };

  // Generate random color based on clip ID (only if no specific color was passed)
  React.useEffect(() => {
    if (clipId && waveformColor === '#726e6c') {
      setCurrentWaveformColor(pickRandomColor(clipId));
    } else if (waveformColor !== '#726e6c') {
      // Update color when waveformColor prop changes
      setCurrentWaveformColor(waveformColor);
    }
  }, [clipId, waveformColor]);

  // No need to fetch time ranges here - the provider handles this

  // Add event listener for time range playback (always listening)
  React.useEffect(() => {
    const handlePlayTimeRange = (event: CustomEvent) => {
      const { timeRange, mediaId } = event.detail;

      // Only play if this is the correct media
      if (mediaId === clipId) {
        if (!audioRef.current) {
          return;
        }

        // Check if this time range overlaps with any playable range
        if (playableRanges && playableRanges.length > 0) {
          const overlapsPlayableRange = playableRanges.some(
            (range) =>
              // Check if time range overlaps with playable range
              (timeRange.start >= range.start &&
                timeRange.start <= range.end) ||
              (timeRange.end >= range.start && timeRange.end <= range.end) ||
              (timeRange.start <= range.start && timeRange.end >= range.end)
          );

          // If doesn't overlap with any playable range, don't play
          if (!overlapsPlayableRange) {
            return;
          }
        }

        const audio = audioRef.current;

        // Pause all other audio (including this one if it's playing) before playing this time range
        const pauseEvent = new CustomEvent('pauseAllAudio');
        window.dispatchEvent(pauseEvent);

        // Small delay to ensure pause completes before starting playback
        // This prevents audio from playing over itself
        setTimeout(() => {
          if (!audioRef.current) return;

          // Store the current time range in ref so we can check it in the timeupdate listener
          currentPlayingTimeRangeRef.current = { ...timeRange, mediaId };
          setCurrentPlayingTimeRange({ ...timeRange, mediaId });

          const playTimeRange = () => {
            // Seek to the start time
            audio.currentTime = timeRange.start;

            // Start playing (will continue to the end of the track)
            audio.play().catch((error) => {
              console.debug('[Audio Playback] Failed to play time range:', {
                mediaId,
                timeRange,
                error: error.message || error,
              });
            });
          };

          // Ensure audio is loaded
          if (audio.readyState < 2) {
            const handleCanPlay = () => {
              audio.removeEventListener('canplay', handleCanPlay);
              playTimeRange();
            };
            audio.addEventListener('canplay', handleCanPlay);
            return;
          }

          playTimeRange();
        }, 50);
      }
    };

    window.addEventListener(
      'playTimeRange',
      handlePlayTimeRange as EventListener
    );

    return () => {
      window.removeEventListener(
        'playTimeRange',
        handlePlayTimeRange as EventListener
      );
    };
  }, [clipId, setCurrentPlayingTimeRange, playableRanges]);

  // Listen for pause all audio event
  React.useEffect(() => {
    const handlePauseAllAudio = () => {
      if (audioRef.current && !audioRef.current.paused) {
        audioRef.current.pause();
        // Also clear the current playing time range
        currentPlayingTimeRangeRef.current = null;
        setCurrentPlayingTimeRange(null);
      }
    };

    window.addEventListener('pauseAllAudio', handlePauseAllAudio);

    return () => {
      window.removeEventListener('pauseAllAudio', handlePauseAllAudio);
    };
  }, [setCurrentPlayingTimeRange]);

  // Audio playback effects
  React.useEffect(() => {
    if (!audioRef.current) return;

    const audio = audioRef.current;

    const handleTimeUpdate = () => {
      if (audio.duration) {
        setProgress(audio.currentTime / audio.duration);
      }

      // If there are playable ranges, check if we're outside them
      if (playableRanges && playableRanges.length > 0) {
        const currentTime = audio.currentTime;
        const isInPlayableRange = playableRanges.some(
          (range) => currentTime >= range.start && currentTime <= range.end
        );

        // If we're playing and outside playable ranges, pause
        if (!audio.paused && !isInPlayableRange) {
          audio.pause();
          setIsPlaying(false);
        }
      }
    };

    const handlePlay = () => setIsPlaying(true);
    const handlePause = () => setIsPlaying(false);
    const handleEnded = () => {
      setIsPlaying(false);
      setProgress(0);
    };

    audio.addEventListener('timeupdate', handleTimeUpdate);
    audio.addEventListener('play', handlePlay);
    audio.addEventListener('pause', handlePause);
    audio.addEventListener('ended', handleEnded);

    return () => {
      audio.removeEventListener('timeupdate', handleTimeUpdate);
      audio.removeEventListener('play', handlePlay);
      audio.removeEventListener('pause', handlePause);
      audio.removeEventListener('ended', handleEnded);
    };
  }, [clipId, playableRanges]);

  // Handle play/pause
  const handlePlayPause = React.useCallback(async () => {
    if (!audioRef.current) return;

    // Mark this waveform as the last interacted one
    if (clipId) {
      setLastInteractedWaveform(clipId);
    }

    const audio = audioRef.current;

    try {
      // Check directly from audio element to get real-time state
      const isCurrentlyPlaying = !audio.paused;

      if (isCurrentlyPlaying) {
        // Clear the current playing time range ref FIRST to prevent any resumption logic
        currentPlayingTimeRangeRef.current = null;
        setCurrentPlayingTimeRange(null);

        // Pause the audio - this is synchronous and should work immediately
        audio.pause();
      } else {
        // If there are playable ranges, check if current position is in one
        if (playableRanges && playableRanges.length > 0) {
          const currentTime = audio.currentTime;
          const isInPlayableRange = playableRanges.some(
            (range) => currentTime >= range.start && currentTime <= range.end
          );

          // If not in playable range, seek to the start of the first playable range
          if (!isInPlayableRange && playableRanges.length > 0) {
            audio.currentTime = playableRanges[0].start;
          }
        }

        // Pause all other audio before playing this one
        const pauseEvent = new CustomEvent('pauseAllAudio');
        window.dispatchEvent(pauseEvent);

        // Ensure audio is loaded before playing
        if (audio.readyState < 2) {
          const handleCanPlay = async () => {
            audio.removeEventListener('canplay', handleCanPlay);
            try {
              await audio.play();
            } catch (error) {
              console.debug(
                '[Audio Playback] Failed to play after canplay (toggle):',
                {
                  clipId,
                  error: error instanceof Error ? error.message : error,
                }
              );
            }
          };
          audio.addEventListener('canplay', handleCanPlay);
          return;
        }

        await audio.play();
      }
    } catch (error) {
      console.debug('[Audio Playback] Failed to toggle play/pause:', {
        clipId,
        error: error instanceof Error ? error.message : error,
      });
    }
  }, [clipId, setCurrentPlayingTimeRange, playableRanges]);

  // Handle explicit play (not toggle) - always plays from beginning
  const handlePlay = React.useCallback(async () => {
    if (!audioRef.current) return;

    const audio = audioRef.current;

    try {
      // Pause all other audio before playing this one
      const pauseEvent = new CustomEvent('pauseAllAudio');
      window.dispatchEvent(pauseEvent);

      // If there are playable ranges, start at the first one instead of beginning
      if (playableRanges && playableRanges.length > 0) {
        audio.currentTime = playableRanges[0].start;
        setProgress(playableRanges[0].start / (audio.duration || 1));
      } else {
        // Reset to beginning
        audio.currentTime = 0;
        setProgress(0);
      }

      // Ensure audio is loaded before playing
      if (audio.readyState < 2) {
        const handleCanPlay = async () => {
          audio.removeEventListener('canplay', handleCanPlay);
          try {
            await audio.play();
          } catch (error) {
            console.debug(
              '[Audio Playback] Failed to play after canplay (explicit play):',
              {
                clipId,
                error: error instanceof Error ? error.message : error,
              }
            );
          }
        };
        audio.addEventListener('canplay', handleCanPlay);
        return;
      }

      await audio.play();
    } catch (error) {
      console.debug(
        '[Audio Playback] Failed to explicitly play from beginning:',
        {
          clipId,
          error: error instanceof Error ? error.message : error,
        }
      );
    }
  }, [clipId, playableRanges]);

  // Listen for toggle play/pause event
  React.useEffect(() => {
    const handleTogglePlayPause = (event: CustomEvent) => {
      const { mediaId } = event.detail || {};
      // Only respond if this is the target media or no specific media is targeted
      if (!mediaId || mediaId === clipId) {
        handlePlayPause();
      }
    };

    window.addEventListener(
      'togglePlayPause',
      handleTogglePlayPause as EventListener
    );

    return () => {
      window.removeEventListener(
        'togglePlayPause',
        handleTogglePlayPause as EventListener
      );
    };
  }, [clipId, handlePlayPause]);

  // Listen for explicit play event
  React.useEffect(() => {
    const handlePlayEvent = (event: CustomEvent) => {
      const { mediaId } = event.detail || {};
      // Only respond if this is the target media
      if (mediaId === clipId) {
        handlePlay();
      }
    };

    window.addEventListener('playAudio', handlePlayEvent as EventListener);

    return () => {
      window.removeEventListener('playAudio', handlePlayEvent as EventListener);
    };
  }, [clipId, handlePlay]);

  // Track last interacted waveform ID (shared across all waveforms)
  const lastInteractedWaveformRef = useRef<string | null>(null);

  // Listen for last interacted waveform updates
  React.useEffect(() => {
    const handleSetLastInteracted = (event: CustomEvent) => {
      const { mediaId } = event.detail;
      lastInteractedWaveformRef.current = mediaId;
    };

    window.addEventListener(
      'setLastInteractedWaveform',
      handleSetLastInteracted as EventListener
    );

    return () => {
      window.removeEventListener(
        'setLastInteractedWaveform',
        handleSetLastInteracted as EventListener
      );
    };
  }, []);

  // Spacebar shortcut to toggle play/pause
  // Only respond if this waveform is the last interacted one, or if it's currently playing
  React.useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      // Only handle spacebar if not typing in an input/textarea
      if (
        event.code === 'Space' &&
        document.activeElement?.tagName !== 'INPUT' &&
        document.activeElement?.tagName !== 'TEXTAREA' &&
        !(document.activeElement as HTMLElement)?.isContentEditable
      ) {
        // Check if this audio is currently playing (check directly from audio element)
        const thisAudioPlaying = audioRef.current && !audioRef.current.paused;

        // Check if this is the last interacted waveform
        const isLastInteracted =
          lastInteractedWaveformRef.current === clipId ||
          lastInteractedWaveformRef.current === null;

        // Only respond if:
        // 1. This waveform is currently playing (pause it), OR
        // 2. This is the last interacted waveform (or no waveform has been interacted with)
        if (thisAudioPlaying || isLastInteracted) {
          event.preventDefault(); // Prevent page scrolling
          event.stopPropagation(); // Prevent other handlers from firing
          handlePlayPause();
        }
      }
    };

    window.addEventListener('keydown', handleKeyDown, true); // Use capture phase

    return () => {
      window.removeEventListener('keydown', handleKeyDown, true);
    };
  }, [clipId, handlePlayPause]);

  // Handle seeking
  const handleSeek = React.useCallback((newProgress: number) => {
    if (!audioRef.current || !audioRef.current.duration) return;

    const newTime = newProgress * audioRef.current.duration;
    audioRef.current.currentTime = newTime;
    setProgress(newProgress);
  }, []);

  React.useEffect(() => {
    if (!audioUrl) {
      setIsLoading(false);
      return;
    }

    setIsLoading(true);
    const generateWaveform = async () => {
      try {
        const response = await fetch(audioUrl);
        const arrayBuffer = await response.arrayBuffer();
        const audioContext = new (window.AudioContext ||
          (window as unknown as { webkitAudioContext: typeof AudioContext })
            .webkitAudioContext)();
        const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

        const audioDuration = audioBuffer.duration;
        setDuration(audioDuration);
        onDurationChange?.(audioDuration);

        // Generate waveform data with higher resolution for better visual quality
        const channelData = audioBuffer.getChannelData(0);
        const samples = channelData.length;
        // Use 1000 data points for large version to match the granularity of the create modal
        const dataPoints = isLarge ? 1000 : 500;
        const blockSize = Math.floor(samples / dataPoints);
        const waveform: number[] = [];

        for (let i = 0; i < dataPoints; i++) {
          const start = i * blockSize;
          const end = start + blockSize;
          let sumSquares = 0;
          let maxAmplitude = 0;

          // Calculate RMS (Root Mean Square) for better visual representation
          for (let j = start; j < end; j++) {
            const amplitude = Math.abs(channelData[j]);
            sumSquares += amplitude * amplitude;
            maxAmplitude = Math.max(maxAmplitude, amplitude);
          }

          // Use RMS with some peak emphasis, similar to the mock data style
          const rms = Math.sqrt(sumSquares / blockSize);
          // Combine RMS with peak information for visual detail
          const combined = rms * 0.7 + maxAmplitude * 0.3;
          waveform.push(combined);
        }

        // Normalize to 0-1 range like mock data
        const maxValue = Math.max(...waveform, 0.0001);
        const minValue = Math.min(...waveform, 0);
        const range = maxValue - minValue || 1;

        // Scale to match mock data range (0.1-0.9) for consistency
        const normalizedWaveform = waveform.map(
          (value) => ((value - minValue) / range) * 0.8 + 0.1
        );

        setWaveformData(normalizedWaveform);
      } catch (error) {
        console.debug('[Waveform] Failed to generate waveform:', {
          audioUrl,
          clipId,
          error: error instanceof Error ? error.message : error,
        });
        setWaveformData([]);
      } finally {
        setIsLoading(false);
      }
    };

    generateWaveform();
  }, [audioUrl, isLarge, clipId, onDurationChange]);

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

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

    const width = canvas.width;
    const height = canvas.height;
    const centerY = height / 2;

    // Clear canvas
    ctx.clearRect(0, 0, width, height);

    // Enable high-quality anti-aliasing for smooth curves
    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';

    // Draw waveform as area chart with enhanced gradient
    const gradient = ctx.createLinearGradient(0, 0, 0, height);
    gradient.addColorStop(0, currentWaveformColor);
    gradient.addColorStop(0.5, currentWaveformColor + 'CC');
    gradient.addColorStop(1, currentWaveformColor + '80');
    ctx.fillStyle = gradient;
    ctx.beginPath();

    // Start at the left edge
    ctx.moveTo(0, centerY);

    // Calculate zoom parameters and generate high-resolution data
    let dataToDraw;
    let dataLength;

    dataToDraw = waveformData;
    dataLength = waveformData.length;

    // Draw the top half of the waveform
    dataToDraw.forEach((value, index) => {
      const x = (index / (dataLength - 1)) * width;
      const amplitude = (value / Math.max(...waveformData)) * (height * 0.4);
      const y = centerY - amplitude;
      ctx.lineTo(x, y);
    });

    // Draw the bottom half of the waveform (mirrored)
    for (let i = dataLength - 1; i >= 0; i--) {
      const x = (i / (dataLength - 1)) * width;
      const amplitude =
        (dataToDraw[i] / Math.max(...waveformData)) * (height * 0.4);
      const y = centerY + amplitude;
      ctx.lineTo(x, y);
    }

    // Close the path back to the start
    ctx.lineTo(0, centerY);
    ctx.closePath();
    ctx.fill();

    // Draw progress line
    if (progress > 0) {
      ctx.fillStyle = '#ffffff';
      const progressX = progress * width;
      ctx.fillRect(progressX - 1, 0, 2, height);
    }

    // Clear dot positions before redrawing
    dotPositionsRef.current = [];

    // Draw time range messages as indicators
    const timeRangeMessages = messages.filter((msg) => msg.time_range);

    // Get unique time ranges to match the filter button colors (use the helper function)
    const uniqueTimeRangeArray = getUniqueTimeRanges(timeRangeMessages);

    timeRangeMessages.forEach((message) => {
      if (!message.time_range) return;

      // Calculate positions for this time range
      const startX = ((message.time_range.start as number) / duration) * width;
      const endX = ((message.time_range.end as number) / duration) * width;
      const dotX = startX + (endX - startX) / 2;
      const dotY = 8;
      const dotRadius = 4;

      // Store dot position for click detection (store ALL dots, not just visible ones)
      // This allows clicking on dots even when filtering is active
      dotPositionsRef.current.push({
        x: dotX,
        y: dotY,
        radius: dotRadius,
        timeRange: {
          start: message.time_range.start as number,
          end: message.time_range.end as number,
        },
      });

      // If filtering is active, only show the selected time range visually
      if (selectedTimeRange) {
        const isExactMatch =
          Math.abs(
            (message.time_range.start as number) - selectedTimeRange.start
          ) < 0.1 &&
          Math.abs((message.time_range.end as number) - selectedTimeRange.end) <
            0.1;
        if (!isExactMatch) return;
      }

      // Find the matching time range in unique time ranges to get the same color as the filter button
      const matchingTimeRange = uniqueTimeRangeArray.find(
        (tr) =>
          Math.abs(tr.start - (message.time_range!.start as number)) < 0.1 &&
          Math.abs(tr.end - (message.time_range!.end as number)) < 0.1
      );

      // Use the same color assignment logic as the chat section
      const colorSet = matchingTimeRange
        ? timeRangeColorSets[
            uniqueTimeRangeArray.indexOf(matchingTimeRange) %
              timeRangeColorSets.length
          ]
        : getTimeRangeColorSet(message.id);

      // Comment indicator background
      ctx.fillStyle = colorSet.base + '40'; // 25% opacity
      ctx.fillRect(startX, 0, endX - startX, height);

      // Comment indicator border
      ctx.strokeStyle = colorSet.base;
      ctx.lineWidth = 2;
      ctx.strokeRect(startX, 0, endX - startX, height);

      // Comment indicator dot at the top
      ctx.fillStyle = colorSet.base;
      ctx.beginPath();
      ctx.arc(dotX, dotY, dotRadius, 0, 2 * Math.PI);
      ctx.fill();
    });

    // Draw selection overlay
    if (selection && selection.start !== undefined) {
      const startX = (selection.start / duration) * width;
      const endX = selection.end ? (selection.end / duration) * width : startX;
      const selectionWidth = Math.max(endX - startX, 2); // Minimum 2px width for visibility

      // Selection background (only if there's actual width)
      if (selectionWidth > 2) {
        ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
        ctx.fillRect(startX, 0, selectionWidth, height);
      }

      // Selection border/line (always visible, even for single-point selections)
      ctx.strokeStyle = '#ffffff';
      ctx.lineWidth = 2;
      ctx.strokeRect(startX, 0, selectionWidth, height);
    }

    // Draw current drag selection
    if (isDragging && startPosition !== null && endPosition !== null) {
      const startX = Math.min(startPosition, endPosition);
      const endX = Math.max(startPosition, endPosition);

      // Drag selection background
      ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
      ctx.fillRect(startX, 0, endX - startX, height);

      // Drag selection border
      ctx.strokeStyle = '#ffffff';
      ctx.lineWidth = 2;
      ctx.strokeRect(startX, 0, endX - startX, height);
    }
  }, [
    waveformData,
    selection,
    isDragging,
    startPosition,
    endPosition,
    duration,
    currentWaveformColor,
    progress,
    messages,
    selectedTimeRange,
  ]);

  const _handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
    if (!canvasRef.current) return;

    const rect = canvasRef.current.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    const relativeX = (x / rect.width) * canvasRef.current.width;
    const relativeY = (y / rect.height) * canvasRef.current.height;

    // Check if click is on a dot first
    const clickPadding = 8;
    const clickedDot = dotPositionsRef.current.find((dot) => {
      const distance = Math.sqrt(
        Math.pow(relativeX - dot.x, 2) + Math.pow(relativeY - dot.y, 2)
      );
      return distance <= dot.radius + clickPadding;
    });

    // If clicking on a dot, don't start selection - let the click handler deal with it
    if (clickedDot) {
      return;
    }

    setInputStartPosition(relativeX);
    setStartPosition(relativeX);
    setEndPosition(relativeX);
    setIsDragging(true);
  };

  const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
    if (!isDragging || !canvasRef.current || inputStartPosition === null)
      return;

    const rect = canvasRef.current.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const relativeX = (x / rect.width) * canvasRef.current.width;

    setEndPosition(relativeX);
  };

  const handleMouseUp = () => {
    if (
      !isDragging ||
      !canvasRef.current ||
      inputStartPosition === null ||
      endPosition === null
    ) {
      setIsDragging(false);
      justCompletedDragRef.current = false;
      return;
    }

    const _rect = canvasRef.current.getBoundingClientRect();
    const start = Math.min(inputStartPosition, endPosition);
    const end = Math.max(inputStartPosition, endPosition);

    const startTime = (start / canvasRef.current.width) * duration;
    const endTime = (end / canvasRef.current.width) * duration;

    // Only create selection if there's actual width (not just a click)
    if (Math.abs(end - start) > 2) {
      onSelectionChange?.({
        start: startTime,
        end: endTime,
      });
      // Mark that we just completed a drag to prevent onClick from overwriting
      justCompletedDragRef.current = true;
      // Reset the flag after a short delay
      setTimeout(() => {
        justCompletedDragRef.current = false;
      }, 100);
    }

    setIsDragging(false);
  };

  const handleCanvasClick = (e: React.MouseEvent<HTMLCanvasElement>) => {
    if (!canvasRef.current || isDragging) return;

    // Don't create a selection if we just completed a drag
    if (justCompletedDragRef.current) {
      return;
    }

    // Mark this waveform as the last interacted one
    if (clipId) {
      setLastInteractedWaveform(clipId);
    }

    const rect = canvasRef.current.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    const relativeX = (x / rect.width) * canvasRef.current.width;
    const relativeY = (y / rect.height) * canvasRef.current.height;
    const clickTime = (relativeX / canvasRef.current.width) * duration;

    // Check if click is on a dot (within dot radius + some padding for easier clicking)
    const clickPadding = 8; // Make dots easier to click
    const clickedDot = dotPositionsRef.current.find((dot) => {
      const distance = Math.sqrt(
        Math.pow(relativeX - dot.x, 2) + Math.pow(relativeY - dot.y, 2)
      );
      return distance <= dot.radius + clickPadding;
    });

    if (clickedDot) {
      // Clicked on a dot - toggle filter for this time range
      const currentTimeRange = selectedTimeRange;
      const isSelected =
        currentTimeRange &&
        Math.abs(currentTimeRange.start - clickedDot.timeRange.start) < 0.1 &&
        Math.abs(currentTimeRange.end - clickedDot.timeRange.end) < 0.1;

      if (isSelected) {
        // Deselect
        setSelectedTimeRange?.(null);
        onTimeRangeChange?.(null);
      } else {
        // Select this time range
        setSelectedTimeRange?.(clickedDot.timeRange);
        onTimeRangeChange?.(clickedDot.timeRange);

        // Also trigger audio playback for this time range
        const event = new CustomEvent('playTimeRange', {
          detail: { timeRange: clickedDot.timeRange, mediaId: clipId },
        });
        window.dispatchEvent(event);
      }
      return;
    }

    // NOTE: We removed the automatic selection of existing time ranges on click
    // This was preventing users from creating sub-selections within existing time ranges
    // Users can still select existing time ranges using the filter buttons below

    // Create a selection at the clicked position (start and end at same point for a single-click selection)
    // This allows users to see where they clicked and leave a comment at that position
    onSelectionChange?.({
      start: clickTime,
      end: clickTime,
    });

    // Seek to clicked position - only affects this waveform's audio element
    if (audioRef.current && audioRef.current.duration) {
      audioRef.current.currentTime = clickTime;
      setProgress(clickTime / audioRef.current.duration);
    }
  };

  if (isLoading) {
    return (
      <div className={`space-y-4 ${className}`}>
        {/* Mimic the loaded state structure to prevent layout shift */}
        <div className='flex items-center gap-3'>
          {/* Placeholder for play button */}
          <div className='h-10 w-10 flex-shrink-0' />

          {/* Loading indicator in place of timeline - min-w-0 allows flex to work properly */}
          <div className='relative min-w-0 flex-1'>
            <div
              className='relative w-full rounded bg-background-secondary/50'
              style={{ aspectRatio: isLarge ? '800/128' : '400/64' }}
            >
              <div className='absolute inset-0 flex items-center justify-center'>
                <div className='flex items-center gap-2'>
                  <svg
                    className='h-4 w-4 animate-spin'
                    fill='none'
                    viewBox='0 0 24 24'
                  >
                    <circle
                      className='opacity-25'
                      cx='12'
                      cy='12'
                      r='10'
                      stroke='currentColor'
                      strokeWidth='4'
                    />
                    <path
                      className='opacity-75'
                      fill='currentColor'
                      d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'
                    />
                  </svg>
                  <span className='text-sm text-foreground-secondary'>
                    Loading waveform...
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className={`space-y-4 ${className}`}>
      {/* Play Button and Timeline */}
      <div className='flex items-center gap-3'>
        {/* Play Button */}
        <button
          onClick={handlePlayPause}
          className='flex h-10 w-10 items-center justify-center rounded-full text-white transition-colors'
          style={
            {
              backgroundColor: playButtonColor || '#726e6c',
              '--hover-color': (playButtonColor || '#726e6c') + '90',
            } as React.CSSProperties
          }
          onMouseEnter={(e) => {
            e.currentTarget.style.backgroundColor =
              (playButtonColor || '#726e6c') + '90';
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.backgroundColor =
              playButtonColor || '#726e6c';
          }}
        >
          {isPlaying ? (
            <svg className='h-5 w-5' fill='currentColor' viewBox='0 0 24 24'>
              <path d='M6 4h4v16H6V4zm8 0h4v16h-4V4z' />
            </svg>
          ) : (
            <svg
              className='ml-0.5 h-5 w-5'
              fill='currentColor'
              viewBox='0 0 24 24'
            >
              <path d='M8 5v14l11-7z' />
            </svg>
          )}
        </button>

        {/* Timeline Canvas */}
        <div className='relative min-w-0 flex-1'>
          <canvas
            ref={canvasRef}
            width={isLarge ? 800 : 400}
            height={isLarge ? 128 : 64}
            className='w-full cursor-crosshair rounded'
            onMouseDown={(e) => {
              if (!canvasRef.current) return;

              // Mark this waveform as the last interacted one
              if (clipId) {
                setLastInteractedWaveform(clipId);
              }

              const rect = canvasRef.current.getBoundingClientRect();
              const x = e.clientX - rect.left;
              const y = e.clientY - rect.top;
              const relativeX = (x / rect.width) * canvasRef.current.width;
              const relativeY = (y / rect.height) * canvasRef.current.height;

              // Check if click is on a dot first
              const clickPadding = 12; // Increased padding for easier clicking
              let clickedDot = null;

              // Check all dots to find the closest one
              for (const dot of dotPositionsRef.current) {
                const distance = Math.sqrt(
                  Math.pow(relativeX - dot.x, 2) +
                    Math.pow(relativeY - dot.y, 2)
                );
                if (distance <= dot.radius + clickPadding) {
                  clickedDot = dot;
                  break;
                }
              }

              // If clicking on a dot, handle it immediately and prevent selection
              if (clickedDot) {
                e.preventDefault();
                e.stopPropagation();

                // Toggle filter for this time range
                const currentTimeRange = selectedTimeRange;
                const isSelected =
                  currentTimeRange &&
                  Math.abs(
                    currentTimeRange.start - clickedDot.timeRange.start
                  ) < 0.1 &&
                  Math.abs(currentTimeRange.end - clickedDot.timeRange.end) <
                    0.1;

                if (isSelected) {
                  // Deselect
                  setSelectedTimeRange?.(null);
                  onTimeRangeChange?.(null);
                } else {
                  // Select this time range
                  setSelectedTimeRange?.(clickedDot.timeRange);
                  onTimeRangeChange?.(clickedDot.timeRange);

                  // Also trigger audio playback for this time range
                  const event = new CustomEvent('playTimeRange', {
                    detail: {
                      timeRange: clickedDot.timeRange,
                      mediaId: clipId,
                    },
                  });
                  window.dispatchEvent(event);
                }
                return;
              }

              const newProgress = relativeX / canvasRef.current.width;

              // Seek to the clicked position without autoplay - only affects this waveform's audio
              handleSeek(newProgress);

              setInputStartPosition(relativeX);
              setStartPosition(relativeX);
              setEndPosition(relativeX);
              setIsDragging(true);
            }}
            onMouseMove={handleMouseMove}
            onMouseUp={handleMouseUp}
            onMouseLeave={handleMouseUp}
            onClick={handleCanvasClick}
          />

          {/* Time markers for large version */}
          {isLarge && (
            <div className='absolute right-0 bottom-0 left-0 flex justify-between px-2 pb-1 text-xs text-foreground-tertiary'>
              <span>0:00</span>
              <span>
                {Math.floor(duration / 60)}:
                {(duration % 60).toFixed(0).padStart(2, '0')}
              </span>
            </div>
          )}
        </div>
      </div>

      {/* Hidden Audio Element */}
      <audio
        ref={audioRef}
        src={audioUrl || undefined}
        preload='metadata'
        onTimeUpdate={() => {
          if (audioRef.current?.duration) {
            setProgress(
              audioRef.current.currentTime / audioRef.current.duration
            );
          }
        }}
        onPlay={() => setIsPlaying(true)}
        onPause={() => setIsPlaying(false)}
        onEnded={() => {
          setIsPlaying(false);
          setProgress(0);
        }}
      />
    </div>
  );
};

interface MarketplaceProjectWaveformProps {
  media: MediaReference;
  mediaMessages: Message[];
  waveformColor: string;
  playButtonColor: string;
  onTimeRangeChange: (timeRange: { start: number; end: number } | null) => void;
  selectedTimeRange: { start: number; end: number } | null;
}

const MarketplaceProjectWaveform: React.FC<MarketplaceProjectWaveformProps> = ({
  media,
  mediaMessages,
  waveformColor,
  playButtonColor,
  onTimeRangeChange,
  selectedTimeRange: _selectedTimeRange,
}) => {
  const {
    trackTimeRanges,
    setTrackTimeRanges,
    currentPlayingTimeRange: _currentPlayingTimeRange,
    setCurrentPlayingTimeRange: _setCurrentPlayingTimeRange,
  } = useMarketplaceProject();

  // Local selection state for this specific waveform (not shared across all waveforms)
  const [localWaveformSelection, setLocalWaveformSelection] = useState<{
    start: number;
    end?: number;
  } | null>(null);

  // Get time range messages for this specific media
  const timeRangeMessages = mediaMessages.filter((msg) => msg.time_range);
  const uniqueTimeRanges = getUniqueTimeRanges(timeRangeMessages);

  // Track audio duration for positioning buttons
  const [duration, setDuration] = useState<number>(0);

  // Calculate playable ranges for unaccepted submissions (everything except fulfiller time ranges)
  const playableRanges = React.useMemo(() => {
    // Only apply to unaccepted submissions
    const isUnacceptedSubmission =
      media.reference_type === 'submission' &&
      (media.is_accepted === null || media.is_accepted === undefined);

    if (!isUnacceptedSubmission || duration === 0) {
      return undefined;
    }

    // Find any messages from fulfillers with time ranges (can be nested)
    const fulfillerMessagesWithTimeRanges = mediaMessages.filter((msg) => {
      const isFulfiller = msg.sender_role === 'fulfiller';
      const hasTimeRange = !!msg.time_range;
      return isFulfiller && hasTimeRange;
    });

    // If there are fulfiller messages with time ranges, playable ranges are ONLY those fulfiller ranges
    if (fulfillerMessagesWithTimeRanges.length > 0) {
      // Get all fulfiller time ranges - these are the ONLY playable ranges
      const fulfillerRanges = fulfillerMessagesWithTimeRanges.map((msg) => ({
        start: msg.time_range!.start as number,
        end: msg.time_range!.end as number,
      }));

      // Sort by start time
      fulfillerRanges.sort((a, b) => a.start - b.start);

      // Return the fulfiller ranges as the playable ranges
      return fulfillerRanges.length > 0 ? fulfillerRanges : undefined;
    }

    return undefined;
  }, [media.reference_type, media.is_accepted, mediaMessages, duration]);

  // Don't render if media URL is empty or invalid
  if (!media.url || media.url.trim() === '') {
    return null;
  }

  return (
    <>
      <div className='relative mb-4'>
        <TimelineWithSelectionPane
          audioUrl={media.url}
          clipId={media.id}
          selection={localWaveformSelection || undefined}
          onSelectionChange={(selection) => {
            setLocalWaveformSelection(selection);
            // Update the track-specific time range when waveform selection changes
            if (selection && selection.end !== undefined) {
              const selectedRange = {
                start: selection.start,
                end: selection.end as number,
              };

              // Check if this is an unaccepted submission with playable ranges
              // and if the selected range is outside the playable ranges
              if (playableRanges && playableRanges.length > 0) {
                const overlapsPlayableRange = playableRanges.some((range) => {
                  // Check if selected range overlaps with any playable range
                  return (
                    (selectedRange.start >= range.start &&
                      selectedRange.start <= range.end) ||
                    (selectedRange.end >= range.start &&
                      selectedRange.end <= range.end) ||
                    (selectedRange.start <= range.start &&
                      selectedRange.end >= range.end)
                  );
                });

                if (!overlapsPlayableRange) {
                  // Selection is outside playable ranges - show error toast
                  toast({
                    title: 'Selection outside playable range',
                    description:
                      'The full track will be available when the project is completed.',
                    status: 'error',
                    duration: 5000,
                    isClosable: true,
                  });
                  // Don't update the selection - clear it instead
                  setLocalWaveformSelection(null);
                  return;
                }
              }

              setTrackTimeRanges({
                ...trackTimeRanges,
                [media.id]: selectedRange,
              });
            }
          }}
          isLarge={true}
          className='w-full'
          messages={timeRangeMessages}
          setSelectedTimeRange={(timeRange) => {
            setTrackTimeRanges({
              ...trackTimeRanges,
              [media.id]: timeRange,
            });
            onTimeRangeChange(timeRange);
          }}
          selectedTimeRange={trackTimeRanges[media.id] || null}
          waveformColor={waveformColor}
          playButtonColor={playButtonColor}
          onDurationChange={setDuration}
          playableRanges={playableRanges}
        />

        {/* Time Range Filter Buttons - Positioned at bottom of waveform canvas */}
        {uniqueTimeRanges.length > 0 && duration > 0 && (
          <div className='pointer-events-none absolute right-0 -bottom-8 left-[52px] h-6'>
            {uniqueTimeRanges.map((timeRange, index) => {
              const currentTrackTimeRange = trackTimeRanges[media.id];
              const isSelected =
                currentTrackTimeRange &&
                Math.abs(currentTrackTimeRange.start - timeRange.start) < 0.1 &&
                Math.abs(currentTrackTimeRange.end - timeRange.end) < 0.1;
              // Only consider it an active filter if there's a time range selection > 1 second
              // Selections <= 1 second (like single clicks) won't hide other filters
              const hasActiveFilter =
                !!currentTrackTimeRange &&
                Math.abs(
                  currentTrackTimeRange.start - currentTrackTimeRange.end
                ) > 1;

              // Get color set for this time range (same logic as chat section)
              const colorSet =
                timeRangeColorSets[index % timeRangeColorSets.length];

              // Count messages/comments for this time range (including child comments)
              const parentMessages = timeRangeMessages.filter((msg) => {
                if (!msg.time_range) return false;
                return (
                  Math.abs((msg.time_range.start as number) - timeRange.start) <
                    0.1 &&
                  Math.abs((msg.time_range.end as number) - timeRange.end) < 0.1
                );
              });

              // Get IDs of parent messages in this time range
              const parentMessageIds = new Set(
                parentMessages.map((msg) => msg.id)
              );

              // Count child messages (replies) that belong to any parent message in this time range
              const childMessages = mediaMessages.filter((msg) => {
                const parentId = (msg as any).parent_message_id;
                return parentId && parentMessageIds.has(parentId);
              });

              // Total count includes both parent messages and their children
              const commentCount = parentMessages.length + childMessages.length;

              // Calculate position: center of the time range
              // The container starts at left-10 (40px) to account for play button
              // So we need to calculate the position relative to the canvas area
              const centerTime = (timeRange.start + timeRange.end) / 2;
              // Calculate percentage of total duration, then apply to container width
              const leftPercent = (centerTime / duration) * 100;

              return (
                <button
                  key={`${timeRange.start}-${timeRange.end}`}
                  onClick={() => {
                    // Clear waveform selection when clicking a filter button
                    setLocalWaveformSelection(null);

                    if (isSelected) {
                      setTrackTimeRanges({
                        ...trackTimeRanges,
                        [media.id]: null,
                      });
                      onTimeRangeChange(null);
                    } else {
                      const newTimeRange = {
                        start: timeRange.start,
                        end: timeRange.end,
                      };
                      setTrackTimeRanges({
                        ...trackTimeRanges,
                        [media.id]: newTimeRange,
                      });
                      onTimeRangeChange(newTimeRange);

                      // Also trigger audio playback for this time range
                      const event = new CustomEvent('playTimeRange', {
                        detail: { timeRange: newTimeRange, mediaId: media.id },
                      });
                      window.dispatchEvent(event);
                    }
                  }}
                  className={`pointer-events-auto absolute top-0 z-10 flex -translate-x-1/2 transform items-center gap-1 rounded-md px-1.5 py-0.5 text-xs font-medium whitespace-nowrap transition-all hover:opacity-100 ${
                    isSelected
                      ? 'opacity-100'
                      : hasActiveFilter
                        ? 'opacity-50'
                        : 'opacity-100'
                  }`}
                  style={{
                    left: `${leftPercent}%`,
                    color: colorSet.base,
                  }}
                >
                  <svg
                    className='h-3 w-3'
                    fill='none'
                    stroke='currentColor'
                    viewBox='0 0 24 24'
                  >
                    <path
                      strokeLinecap='round'
                      strokeLinejoin='round'
                      strokeWidth={2}
                      d='M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z'
                    />
                  </svg>
                  <span>{commentCount}</span>
                </button>
              );
            })}

            {/* Clear filter button */}
            {trackTimeRanges[media.id] && (
              <button
                onClick={() => {
                  // Clear waveform selection when clearing filter
                  setLocalWaveformSelection(null);
                  setTrackTimeRanges({
                    ...trackTimeRanges,
                    [media.id]: null,
                  });
                  onTimeRangeChange(null);
                }}
                className='pointer-events-auto absolute top-0 right-0 z-10 text-xs text-foreground-tertiary transition-colors hover:text-foreground-primary'
              >
                Clear
              </button>
            )}
          </div>
        )}
      </div>
    </>
  );
};

export default MarketplaceProjectWaveform;
