/* eslint jsx-a11y/no-static-element-interactions: warn */
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import {
  useCallback,
  useContext,
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
} from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import { OrpheusClipRow } from '@/components/clipBrowser/OrpheusClipRow';
import useClipChanges from '@/components/clipBrowser/useClipChanges';
import ImageWithFallback from '@/components/image/ImageWithFallback';
import Link from '@/components/link/Link';
import useClip from '@/hooks/useClip';
import {
  CloseIcon,
  CreateIcon,
  PauseIcon,
  PlayIcon,
  ShareArrowIcon,
  ThumbsDownIcon,
  ThumbsUpIcon,
} from '@/icons';
import { ContextType } from '@/logging/contextTypes';
import { withWebUserEvent } from '@/logging/logWebUserEvent';
import { isDisliked, isLiked } from '@/state/clipStore';
import { getClipTitle } from '@/utils/clip';
import { DEFAULT_CREATE_CONTROL_VALUE, EMPTY_UUID } from '@/utils/constants';

import useClipPlayer from '../../../create/createV2/componentsQ3/useClipPlayer';
import { ABMessageContext, OrpheusPlaybackContext } from '../../MessageList';
import { useChatStore } from '../../stores';
import { useChatContext } from '../../useChat';
import { useChatMessageActions } from '../../useChatMessageActions';
import { useFadeScrollStyle } from '../../useFadeScrollStyle';
import { ReferenceType } from '../input/ReferenceTypes';
import { SongCellLoading } from './SongCellLoading';
import { StylesLyricsCard } from './StylesLyricsCard';

const ClipItem = observer(
  ({ clipId, index }: { clipId: string; index: number }) => {
    const { clip } = useClip(clipId);
    const { playbar, queue, clips } = useStores();
    const chatStore = useChatStore();
    const {
      getPlaybackTime: _getPlaybackTime,
      setPlaybackTime: _setPlaybackTime,
    } = useClipPlayer(clip?.id || '');
    const { setClipLiked, setClipDisliked } = useClipChanges();
    const messageClipIds = (useContext(OrpheusPlaybackContext)?.clipIds ?? [])
      .map((clipId: string) => clips.clipById[clipId])
      .filter(Boolean);
    return (
      <div
        className={clsx(
          'group relative flex aspect-5/8 h-full w-full flex-shrink-0 flex-col overflow-hidden rounded-3xl p-[6px] hover:bg-background-fog-thin',
          {
            'bg-background-fog-thin':
              playbar.clip?.id === clip?.id && !!clip?.id,
          }
        )}
        style={{ maxWidth: 'min(calc(50% - 8px), calc(300px * 5 / 8))' }}
      >
        <div
          className={clsx('relative aspect-square h-full w-full rounded-3xl', {
            'opacity-50': !['streaming', 'complete'].includes(
              clip?.status ?? ''
            ),
          })}
        >
          {/* Background Image */}
          <ImageWithFallback
            src={clip?.image_url}
            alt={`Image for ${clip?.title}`}
            className='absolute inset-0 aspect-square w-full rounded-3xl object-cover'
          />
          {/* Visualizer Overlay
          <VisualizerOverlay
            clipId={clip?.id || ''}
            isPlaying={isPlaying}
            className='opacity-80'
          />*/}
          {/* Play button overlay - centered */}
          <div
            className={clsx(
              'absolute inset-0 flex items-center justify-center transition-opacity duration-200',
              {
                'opacity-0 group-hover:opacity-100': [
                  'streaming',
                  'complete',
                ].includes(clip?.status ?? ''),
                'opacity-100':
                  playbar.clip?.id === clip?.id && playbar.isPlaying,
              }
            )}
          >
            {clip?.status === 'error' ? (
              <CloseIcon />
            ) : !['streaming', 'complete'].includes(clip?.status ?? '') ? (
              <SongCellLoading />
            ) : (
              <Button
                shape={ButtonShape.Pill}
                size={ButtonSize.Large}
                icon={
                  playbar.clip?.id === clip?.id && playbar.isPlaying
                    ? PauseIcon
                    : PlayIcon
                }
                onClick={() => {
                  if (playbar.clip?.id === clip?.id && !!clip?.id) {
                    playbar.togglePlay();
                  } else if (clip) {
                    queue.setPlayContext({
                      contextId: chatStore.chatUUID,
                      contextType: ContextType.Orpheus,
                      clips: messageClipIds,
                      currentIndex: index,
                    });
                    playbar.playClip(clip);
                  }
                }}
                className='z-2 bg-background-smoke-dense backdrop-blur-[2px]'
              />
            )}
          </div>
        </div>
        {/* Content overlay - bottom with blurred background */}
        <div className='flex h-auto w-full flex-col gap-2 px-4 py-2 text-primary'>
          {/* Title */}
          <Link href={`/song/${clip?.id}`} target='_blank' rel='noreferrer'>
            <h3
              className={clsx(
                'line-clamp-1 text-sm font-medium break-all transition-colors duration-400 ease-in-out',
                {
                  'text-accent-pink':
                    !!clip?.id && playbar.clip?.id === clip?.id,
                }
              )}
            >
              {getClipTitle(clip as any)}
            </h3>
          </Link>
          <h3 className='line-clamp-1 h-[20px] text-sm font-medium break-all text-foreground-secondary'>
            {clip?.display_tags}
          </h3>
          {/* Action buttons */}
          <div className='-ml-2 flex items-center justify-between'>
            <div className='flex gap-1'>
              <Button
                disabled={!clip?.audio_url}
                shape={ButtonShape.Pill}
                icon={
                  <ThumbsUpIcon
                    className={clsx('h-4 w-4', {
                      'fill-foreground-primary': clip && isLiked(clip),
                      'fill-foreground-tertiary': !(clip && isLiked(clip)),
                    })}
                  />
                }
                className={`clip-interaction-button ${
                  clip && isLiked(clip)
                    ? 'animate-[small-bounce_0.25s_ease-in-out]'
                    : ''
                }`}
                onClick={withWebUserEvent(
                  {
                    actionName: 'SongActionLikeClicked',
                    principalObjectValue: clip?.id || '',
                    principalObjectType: 'song',
                    context: {
                      likeStatus: clip ? !isLiked(clip) : false,
                    },
                  },
                  () => clip && setClipLiked(clip.id, !isLiked(clip))
                )}
                variant={ButtonVariant.Tertiary}
              />
              <Button
                disabled={!clip?.audio_url}
                shape={ButtonShape.Pill}
                icon={
                  <ThumbsDownIcon
                    className={clsx('h-4 w-4', {
                      'fill-foreground-primary': clip && isDisliked(clip),
                      'fill-foreground-tertiary': !(clip && isDisliked(clip)),
                    })}
                  />
                }
                className='clip-interaction-button'
                onClick={withWebUserEvent(
                  {
                    actionName: 'SongActionDislikeClicked',
                    principalObjectValue: clip?.id || '',
                    principalObjectType: 'song',
                    context: {
                      dislikeStatus: clip ? !isDisliked(clip) : false,
                    },
                  },
                  () => clip && setClipDisliked(clip.id, !isDisliked(clip))
                )}
                variant={ButtonVariant.Tertiary}
              />
              {clip?.status === 'complete' ? (
                <Button
                  shape={ButtonShape.Pill}
                  size={ButtonSize.Mini}
                  variant={ButtonVariant.Tertiary}
                  icon={ShareArrowIcon}
                  iconClassName='rotate-180'
                  className='px-1'
                  onClick={() => {
                    chatStore.addReference({
                      type: ReferenceType.CLIP,
                      clipId: clip.id,
                    });
                  }}
                />
              ) : null}
            </div>
          </div>
        </div>
      </div>
    );
  }
);

const USE_SONG_ROWS = true;

export const ClipsMessage = ({
  clipIds,
  toolCallId,
  role = 'assistant',
  showCreateMore = true,
  showStylesAndLyrics = true,
  startingIndex = 0,
  lyricsVersion,
  stylesVersion,
  onAllowChildScroll,
}: {
  clipIds: string[];
  toolCallId: string;
  role?: string;
  showCreateMore?: boolean;
  showStylesAndLyrics?: boolean;
  startingIndex?: number;
  lyricsVersion?: number;
  stylesVersion?: number;
  onAllowChildScroll: () => void;
}) => {
  const { clips } = useStores();
  const { executeGenerateSongToolCall } = useChatMessageActions();
  const toolCallPendingGensMap = useChatStore(
    (state) => state.toolCallPendingGensMap
  );
  const { toolArgsCache, chatUUID } = useChatContext();
  const messageClips = (useContext(OrpheusPlaybackContext)?.clipIds ?? [])
    .map((clipId: string) => clips.clipById[clipId])
    .filter(Boolean);
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const { fadeScrollStyle, updateFadeScrollStyle } = useFadeScrollStyle({
    scrollContainerRef,
  });
  const [isDragging, setIsDragging] = useState(false);
  const [startX, setStartX] = useState(0);
  const [scrollLeft, setScrollLeft] = useState(0);
  const SCROLL_SPEED = 2;

  // Add refs to store cleanup functions
  const mouseUpHandlerRef = useRef<(() => void) | null>(null);
  const touchEndHandlerRef = useRef<(() => void) | null>(null);

  // Cleanup function to remove event listeners
  const cleanupEventListeners = useCallback(() => {
    if (mouseUpHandlerRef.current) {
      document.removeEventListener('mouseup', mouseUpHandlerRef.current);
      mouseUpHandlerRef.current = null;
    }
    if (touchEndHandlerRef.current) {
      document.removeEventListener('touchend', touchEndHandlerRef.current);
      touchEndHandlerRef.current = null;
    }
  }, []);

  // Cleanup on unmount
  useEffect(() => {
    return cleanupEventListeners;
  }, [cleanupEventListeners]);

  const getToolCallArgsFromClip = useCallback(
    (clipId: string) => {
      const clip = clips.clipById[clipId];
      if (!clip) return {};
      return {
        title: clip.title,
        lyrics: clip.metadata?.prompt || '',
        tags: clip.metadata?.tags || [],
        weirdness_constraint:
          clip.metadata?.control_sliders?.weirdness_constraint ??
          DEFAULT_CREATE_CONTROL_VALUE,
        style_weight:
          clip.metadata?.control_sliders?.style_weight ??
          DEFAULT_CREATE_CONTROL_VALUE,
        task: clip.metadata?.task ?? null,
        continue_clip_id:
          clip.metadata?.task == 'extend'
            ? clip.metadata?.edited_clip_id !== EMPTY_UUID
              ? clip.metadata?.edited_clip_id
              : null
            : null,
        cover_clip_id: clip.metadata?.cover_clip_id,
        continue_at: clip.metadata?.continue_at,
      };
    },
    [clips.clipById]
  );

  const handleRegenerate = useCallback(
    async (batchOffset?: number) => {
      if (clipIds.length === 0) return;
      onAllowChildScroll();
      await executeGenerateSongToolCall({
        callId: toolCallId,
        toolArgs:
          // toolArgsCache.get(toolCallId) ??
          getToolCallArgsFromClip(clipIds[0]),
        isRegenerate: true,
        batchOffset,
      });
    },
    [
      toolArgsCache,
      toolCallId,
      executeGenerateSongToolCall,
      getToolCallArgsFromClip,
      clips,
      clipIds,
      onAllowChildScroll,
    ]
  );

  const handleMouseDown = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      if (scrollContainerRef.current) {
        // Clean up any existing listeners first
        cleanupEventListeners();

        setIsDragging(true);
        setStartX(e.pageX - scrollContainerRef.current.offsetLeft);
        setScrollLeft(scrollContainerRef.current.scrollLeft);

        // Create the mouseup handler
        const mouseUpHandler = () => {
          setIsDragging(false);
          cleanupEventListeners();
        };

        // Store the handler reference and add the event listener
        mouseUpHandlerRef.current = mouseUpHandler;
        document.addEventListener('mouseup', mouseUpHandler);
      }
    },
    [cleanupEventListeners]
  );

  const handleTouchStart = useCallback(
    (e: React.TouchEvent<HTMLDivElement>) => {
      if (scrollContainerRef.current) {
        // Clean up any existing listeners first
        cleanupEventListeners();

        setIsDragging(true);
        setStartX(e.touches[0].pageX - scrollContainerRef.current.offsetLeft);
        setScrollLeft(scrollContainerRef.current.scrollLeft);

        // Create the touchend handler
        const touchEndHandler = () => {
          setIsDragging(false);
          cleanupEventListeners();
        };

        // Store the handler reference and add the event listener
        touchEndHandlerRef.current = touchEndHandler;
        document.addEventListener('touchend', touchEndHandler);
      }
    },
    [cleanupEventListeners]
  );

  const handleMouseMove = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      if (scrollContainerRef.current) {
        if (!isDragging) return;
        e.preventDefault();
        const x = e.pageX - scrollContainerRef.current.offsetLeft;
        const walk = (x - startX) * SCROLL_SPEED;
        scrollContainerRef.current.scrollLeft = scrollLeft - walk;
      }
    },
    [isDragging, startX, scrollLeft]
  );

  const handleTouchMove = useCallback(
    (e: React.TouchEvent<HTMLDivElement>) => {
      if (scrollContainerRef.current) {
        if (!isDragging) return;
        e.preventDefault();
        const x = e.touches[0].pageX - scrollContainerRef.current.offsetLeft;
        const walk = (x - startX) * SCROLL_SPEED;
        scrollContainerRef.current.scrollLeft = scrollLeft - walk;
      }
    },
    [isDragging, startX, scrollLeft]
  );

  useLayoutEffect(() => {
    const container = scrollContainerRef.current;
    if (!container) return;
    const scrollToRight = () => {
      container.scrollLeft = container.scrollWidth;
    };
    const mutationObserver = new MutationObserver(() => {
      scrollToRight();
    });
    mutationObserver.observe(container, { childList: true });
    return () => {
      mutationObserver.disconnect();
    };
  }, []);

  const referenceClip = useClip(clipIds[0])?.clip;
  const isABMessageContext = useContext(ABMessageContext) !== null;

  return (
    <>
      {showStylesAndLyrics && referenceClip?.status !== 'error' && (
        <StylesLyricsCard
          showStylesLyrics
          isLoading={!referenceClip}
          styles={referenceClip?.metadata?.tags ?? undefined}
          lyrics={referenceClip?.metadata?.prompt ?? undefined}
          title={referenceClip?.title ?? 'Untitled'}
          onAllowChildScroll={onAllowChildScroll}
          toolCallId={toolCallId}
          stylesVersion={stylesVersion}
          lyricsVersion={lyricsVersion}
        />
      )}
      {USE_SONG_ROWS ? (
        <div className='flex flex-col gap-0'>
          {clipIds.map((clipId: string, index: number) => (
            <div key={clipId}>
              <OrpheusClipRow
                clipId={clipId}
                playContext={{
                  contextId: chatUUID,
                  contextType: ContextType.Orpheus,
                  clips: messageClips,
                  currentIndex: index + startingIndex,
                }}
                hideEditTitleIcon={true}
                showShareLabel={true}
                hideRemixEditButton={true}
              />
            </div>
          ))}
          {(toolCallPendingGensMap.get(toolCallId) ?? []).map((ts: number) => (
            <div key={ts}>
              <OrpheusClipRow clipId='' />
              <OrpheusClipRow clipId='' />
            </div>
          ))}
          {role !== 'user' && showCreateMore && !isABMessageContext ? (
            <div className='flex justify-start pt-4'>
              <Button
                shape={ButtonShape.Pill}
                size={ButtonSize.Medium}
                icon={CreateIcon}
                variant={ButtonVariant.Aura}
                className='bg-background-fog-thin'
                onClick={() =>
                  handleRegenerate(
                    (toolCallPendingGensMap.get(toolCallId) ?? []).length +
                      clipIds.length
                  )
                }
              >
                Create More
              </Button>
            </div>
          ) : null}
        </div>
      ) : (
        <div
          className={clsx('relative w-full', {
            'cursor-grabbing': isDragging,
          })}
          onTouchStart={handleTouchStart}
          onTouchMove={handleTouchMove}
          onMouseDown={handleMouseDown}
          onMouseMove={handleMouseMove}
        >
          {/* Horizontal scrolling container */}
          <div className='absolute top-0 right-0 bottom-0 left-0'>
            {fadeScrollStyle === 'both' || fadeScrollStyle === 'left' ? (
              <div className='pointer-events-none absolute top-0 bottom-12 left-0 z-[500] w-6 bg-linear-to-r from-background-primary/80 to-transparent' />
            ) : null}
            {fadeScrollStyle === 'both' || fadeScrollStyle === 'right' ? (
              <div className='pointer-events-none absolute top-0 right-0 bottom-12 z-[500] w-6 bg-linear-to-l from-background-primary/80 to-transparent' />
            ) : null}
          </div>
          <div
            ref={scrollContainerRef}
            onScroll={updateFadeScrollStyle}
            className={clsx(
              'scrollbar-hide flex h-auto max-h-[300px] w-full gap-4 overflow-x-auto overflow-y-hidden',
              {
                'flex-row-reverse': role === 'user',
                'select-none': isDragging,
              }
            )}
          >
            {clipIds.map((clipId: string, i: number) => (
              <ClipItem
                clipId={clipId}
                key={clipId}
                index={startingIndex + i}
              />
            ))}
          </div>
        </div>
      )}
    </>
  );
};
