'use client';

import {
  DndContext,
  DragEndEvent,
  PointerSensor,
  closestCenter,
  useSensor,
  useSensors,
} from '@dnd-kit/core';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
import { useSortable } from '@dnd-kit/sortable';
import { SortableContext } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { observer } from 'mobx-react-lite';
import React, { useCallback, useRef } from 'react';
import { twMerge } from 'tailwind-merge';

import { useStores } from '@/app/(root)/AppProviders';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import SongQueueSongItem from '@/components/song-queue/SongItem';
import { Tooltip } from '@/components/tooltip/Tooltip';
import { InfinityIcon, CloseIcon, FastForwardIcon, QueueIcon } from '@/icons';
import { components } from '@/lib/gen';
import { Clip } from '@/state/clipStore';

export type clipType = components['schemas']['GeneratedClipSchema'];

// Utility function to generate song item props
const generateSongItemProps = (
  clip: clipType | undefined | null,
  listId = 'index',
  listTitle = 'Queue'
) => {
  return {
    clipId: clip?.id?.toString() || '',
    imgUrl: clip?.image_url || '',
    title: clip?.title || '',
    tags: clip?.metadata?.tags
      ? Array.isArray(clip?.metadata.tags)
        ? clip?.metadata.tags
        : [clip?.metadata.tags]
      : [],
    displayName: clip?.display_name || '',
    handle: clip?.handle || '',
    avatarImageUrl: clip?.avatar_image_url || '',
    personaName: clip?.persona?.name || '',
    personaImageUrl: clip?.persona?.user_image_url || '',
    playCount: clip?.play_count || 0,
    upvoteCount: clip?.upvote_count || 0,
    commentCount: clip?.comment_count || 0,
    artistId: clip?.user_id || '',
    listId,
    listTitle,
  };
};

export type SongQueuePanelProps = {
  headerClassName?: string;
  nowPlayingClassName?: string;
  nextItemsClassName?: string;
  footerClassName?: string;
};

export type Props = Omit<
  React.HTMLAttributes<HTMLDivElement>,
  keyof SongQueuePanelProps
> &
  SongQueuePanelProps;

const SongQueuePanel = observer((props: Props) => {
  const {
    className,
    headerClassName,
    nowPlayingClassName,
    nextItemsClassName,
    footerClassName,
  } = props;

  const { queue, playbar, clips } = useStores();

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 10,
      },
    })
  );

  // Manual queue songs (highest priority)
  // MobX observer tracks queue.manualQueue and triggers re-renders automatically
  const manualQueueSongs = queue.manualQueue.map((item, i) => ({
    id: `manual-${item.clip.id}-${i}`,
    clip: item.clip,
    queueType: 'manual' as const,
    index: i,
  }));

  // Context queue songs (from current playlist, etc.)
  // MobX observer tracks these observables and triggers re-renders automatically
  const currentContextIndex =
    queue.activeQueue === 'context'
      ? queue.contextQueueIndex
      : queue.activeQueue === 'manual'
        ? queue.contextQueueIndex
        : -1;

  const contextQueueSongs =
    queue.contextClips.length === 0
      ? []
      : queue.contextClipIndexOrder
          .slice(currentContextIndex + 1)
          .map((i, idx) => ({
            id: `context-${queue.contextClips[i].id}-${idx}`,
            clip: queue.contextClips[i],
            queueType: 'context' as const,
            index: currentContextIndex + 1 + idx,
          }));

  // Autoplay queue songs (only shown when context queue is empty)
  const autoplayQueueSongs =
    contextQueueSongs.length === 0 && queue.autoplay
      ? queue.autoplayQueue.map((item, idx) => ({
          id: `autoplay-${item.clip.id}-${idx}`,
          clip: item.clip,
          queueType: 'autoplay' as const,
          index: idx,
        }))
      : [];

  // Combined list for display (context + autoplay)
  const contextAndAutoplaySongs = [...contextQueueSongs, ...autoplayQueueSongs];

  // Stable ref for async access in drag handlers
  // Updated directly on each render (cheap ref assignment)
  const queueSongsRef = useRef({
    manual: manualQueueSongs,
    context: contextQueueSongs,
    autoplay: autoplayQueueSongs,
  });
  queueSongsRef.current = {
    manual: manualQueueSongs,
    context: contextQueueSongs,
    autoplay: autoplayQueueSongs,
  };

  // Drag and drop for manual queue
  const handleManualQueueDragEnd = useCallback(
    (event: DragEndEvent) => {
      const { active, over } = event;
      if (over === null) return;
      if (active.id !== over.id) {
        // Use ref to get stable list during drag operation
        const currentSongs = queueSongsRef.current.manual;
        const oldIndex = currentSongs.findIndex(
          (item) => item.id === active.id
        );
        const newIndex = currentSongs.findIndex((item) => item.id === over.id);
        if (oldIndex >= 0 && newIndex >= 0) {
          const newManualQueue = [...queue.manualQueue];
          const [removed] = newManualQueue.splice(oldIndex, 1);
          newManualQueue.splice(newIndex, 0, removed);
          queue.manualQueue = newManualQueue;
        }
      }
    },
    [queue]
  );
  // Drag and drop for context queue
  const handleContextQueueDragEnd = useCallback(
    (event: DragEndEvent) => {
      const { active, over } = event;
      if (over === null) return;
      if (active.id !== over.id) {
        // Use ref to get stable lists during drag operation
        const contextSongs = queueSongsRef.current.context;
        const autoplaySongs = queueSongsRef.current.autoplay;
        const currentSongs = [...contextSongs, ...autoplaySongs];

        const activeItem = currentSongs.find((item) => item.id === active.id);
        const overItem = currentSongs.find((item) => item.id === over.id);
        if (!activeItem || !overItem) return;

        // Handle autoplay queue reordering
        if (
          activeItem.queueType === 'autoplay' &&
          overItem.queueType === 'autoplay'
        ) {
          const oldIndex = activeItem.index;
          const newIndex = overItem.index;
          const newAutoplayQueue = [...queue.autoplayQueue];
          const [removed] = newAutoplayQueue.splice(oldIndex, 1);
          newAutoplayQueue.splice(newIndex, 0, removed);
          queue.autoplayQueue = newAutoplayQueue;
        }
        // Handle context queue reordering
        else if (
          activeItem.queueType === 'context' &&
          overItem.queueType === 'context'
        ) {
          queue.reorderClips(activeItem.index, overItem.index);
        }
      }
    },
    [queue]
  );

  // Get playlist/context name for "Next from: {name}" header
  const getContextName = () => {
    const currentContextIndex =
      queue.activeQueue === 'context' || queue.activeQueue === 'manual'
        ? queue.contextQueueIndex
        : -1;
    const hasContextSongs =
      queue.contextClipIndexOrder.slice(currentContextIndex + 1).length > 0;

    // Only show playlist name if we have context songs AND can find the playlist
    if (
      hasContextSongs &&
      queue.contextType === 'playlist' &&
      queue.contextId
    ) {
      const playlistData = clips.playlistById[queue.contextId];
      if (playlistData?.name) {
        return playlistData.name;
      }
    }

    return '';
  };

  return (
    <div
      className={twMerge(
        'flex max-h-[inherit] flex-col gap-4 overflow-hidden p-1',
        className
      )}
    >
      <div
        className={twMerge(
          'flex items-center justify-between',
          headerClassName
        )}
      >
        <h2 className='flex items-center gap-1 px-2 text-lg font-semibold'>
          <QueueIcon className='h-5 w-5' />
          <span>Queue</span>
        </h2>
        <Button
          variant={ButtonVariant.Tertiary}
          size={ButtonSize.Mini}
          onClick={playbar.toggleShowSongQueue}
          className='rounded-full p-2 transition-colors hover:bg-background-tertiary dark:hover:bg-background-primary'
          aria-label='Close queue'
          icon={CloseIcon}
        />
      </div>

      <div className={nowPlayingClassName}>
        <h2 className='ml-2 text-foreground-tertiary'>Now playing</h2>
        <SongQueueSongItem
          key='now-playing'
          id='now-playing'
          {...generateSongItemProps(playbar.clip)}
          isNowPlayingSection={true}
        />
      </div>

      <div className={twMerge('flex-1 overflow-y-auto', nextItemsClassName)}>
        {/* Manual Queue Section */}
        {manualQueueSongs.length > 0 && (
          <div className='mb-4'>
            <div className='flex items-center justify-between px-2'>
              <h2 className='text-foreground-tertiary'>Next in queue</h2>
              <Button
                variant={ButtonVariant.Tertiary}
                size={ButtonSize.Mini}
                onClick={() => queue.clearManualQueue()}
                className='text-xs text-foreground-tertiary hover:text-foreground-primary'
              >
                Clear all
              </Button>
            </div>
            <DndContext
              sensors={sensors}
              collisionDetection={closestCenter}
              onDragEnd={handleManualQueueDragEnd}
              modifiers={[restrictToVerticalAxis]}
            >
              <SortableContext items={manualQueueSongs}>
                {manualQueueSongs.map((item, idx) => (
                  <SortableItem
                    key={item.id}
                    id={item.id}
                    clip={item.clip}
                    manualQueueIndex={idx}
                  />
                ))}
              </SortableContext>
            </DndContext>
          </div>
        )}

        {/* Context Queue Section */}
        {contextAndAutoplaySongs.length > 0 && (
          <div>
            <h2 className='ml-2 text-foreground-tertiary'>
              {getContextName() ? `Next from: ${getContextName()}` : 'Next up'}
            </h2>
            <DndContext
              sensors={sensors}
              collisionDetection={closestCenter}
              onDragEnd={handleContextQueueDragEnd}
              modifiers={[restrictToVerticalAxis]}
            >
              <SortableContext items={contextAndAutoplaySongs}>
                {contextAndAutoplaySongs.map((item) => (
                  <SortableItem
                    key={item.id}
                    id={item.id}
                    clip={item.clip}
                    autoplayQueueIndex={
                      item.queueType === 'autoplay' ? item.index : undefined
                    }
                  />
                ))}
              </SortableContext>
            </DndContext>
          </div>
        )}

        {/* Empty state */}
        {manualQueueSongs.length === 0 &&
          contextAndAutoplaySongs.length === 0 && (
            <div className='flex flex-col items-center justify-center py-8 text-center text-foreground-tertiary'>
              <QueueIcon className='mb-2 h-12 w-12 opacity-50' />
              <p>Queue is empty</p>
            </div>
          )}
      </div>

      <div
        className={twMerge(
          'flex flex-row items-center justify-center gap-1',
          footerClassName
        )}
      >
        <div>
          <Tooltip label='Plays the next queued song after the current song ends'>
            <Button
              className='w-full'
              variant={ButtonVariant.Tertiary}
              size={ButtonSize.Small}
              shape={ButtonShape.Pill}
              active={queue.continuous}
              icon={FastForwardIcon}
              onClick={() => {
                queue.toggleContinuous();
              }}
              aria-label='Toggle continuous play'
            />
          </Tooltip>
        </div>
        <div>
          <Tooltip label='Plays related songs when your queue ends'>
            <Button
              className='w-full'
              variant={ButtonVariant.Tertiary}
              size={ButtonSize.Small}
              shape={ButtonShape.Pill}
              active={queue.autoplay}
              icon={InfinityIcon}
              onClick={() => {
                queue.toggleAutoplay();
              }}
              aria-label='Toggle autoplay'
            />
          </Tooltip>
        </div>
      </div>
    </div>
  );
});

const SortableItem = (props: {
  id: string;
  clip: Clip;
  manualQueueIndex?: number;
  autoplayQueueIndex?: number;
}) => {
  const { attributes, listeners, setNodeRef, transform, transition } =
    useSortable({ id: props.id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  return (
    <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
      <SongQueueSongItem
        key={props.id}
        id={props.id}
        {...generateSongItemProps(props.clip)}
        manualQueueIndex={props.manualQueueIndex}
        autoplayQueueIndex={props.autoplayQueueIndex}
      />
    </div>
  );
};

export default SongQueuePanel;
