/* eslint jsx-a11y/click-events-have-key-events: warn */

/* eslint jsx-a11y/no-static-element-interactions: warn */
import { Modal, ModalContent, ModalOverlay } from '@chakra-ui/react';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import { useRouter } from 'next/navigation';
import {
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';

import { useStores } from '@/app/(root)/AppProviders';
import PostHookScreen from '@/app/(root)/hooks/create/PostHookScreen';
import { UploadHookVideoScreen } from '@/app/(root)/hooks/create/UploadHookVideoScreen';
import HookFormContext from '@/app/(root)/hooks/create/useHookForm';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import CloseButton from '@/components/button/CloseButton';
import ImageWithFallback from '@/components/image/ImageWithFallback';
import Link from '@/components/link/Link';
import { ModalTypes } from '@/components/modal/constants/ModalTypes';
import { formatDuration, tagsToArray } from '@/components/song/songUtils';
import SpinnerSVG from '@/components/svg/SpinnerSVG';
import TextareaV2, { CharCountMode } from '@/components/textarea/TextareaV2';
import { CloseIcon, MusicIcon, PlayIcon, SearchIcon } from '@/icons';
import PulsingLinesIcon from '@/icons/spinners/PulsingLinesIcon';
import { useApiClient } from '@/lib/apiClient';
import { components } from '@/lib/gen';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { getClipDisplayTags } from '@/utils/clip';
import {
  MAX_SEARCH_TERM_LEN,
  REACT_ARIA_TABS_STYLE,
  SEARCH_DEBOUNCE_DELAY,
  SMALL_IMAGE,
} from '@/utils/constants';

type SuggestedSongFilter = 'all' | 'public' | 'liked';
type GeneratedClip = components['schemas']['GeneratedClipSchema'];

const SUGGESTED_SONG_PAGE_SIZE = 20;

export const SuggestedSongItem = observer(
  ({
    clip,
    disableControls,
    tab = 'all',
    searchText = '',
    tabSongIndex = 0,
  }: {
    clip: GeneratedClip;
    disableControls?: boolean;
    tab?: 'all' | 'public' | 'liked';
    searchText?: string;
    tabSongIndex?: number;
  }) => {
    const { menus, playbar, session } = useStores();
    const { setSelectedSong, navigateTo, currentScreen, mediaEditState } =
      useContext(HookFormContext);
    const audioStartMs = clip.audioMetadata?.audio_snippet?.start_timestamp
      ? Math.round(clip.audioMetadata?.audio_snippet?.start_timestamp * 1000)
      : 0;
    const handleSelectSong = (clip: GeneratedClip) => {
      setSelectedSong(clip);
      mediaEditState.onAudioRangeSet(audioStartMs);
      if (playbar.isPlaying) {
        playbar.togglePlay();
      }
      if (currentScreen === 'selectSongInFlow') {
        navigateTo('uploadVideoInFlow');
      } else {
        menus.closeModal(ModalTypes.SELECT_HOOK_SONG);
      }
    };

    const formattedTags = useMemo(() => {
      const tags = tagsToArray(getClipDisplayTags(clip));
      if (tags.length <= 3) {
        return tags.join(', ');
      }
      return `${tags.slice(0, 3).join(', ')} +${tags.length - 3}`;
    }, [clip.display_tags, clip.metadata?.tags]);

    const inPlaybar = playbar.clip?.id === clip.id;
    const playing = playbar.isPlaying && inPlaybar;

    return (
      <div
        className={clsx('group flex items-center gap-2 rounded-md p-2', {
          'cursor-pointer hover:bg-background-tertiary': !disableControls,
          'border border-border-primary': disableControls,
        })}
        onClick={() => {
          if (disableControls) {
            return;
          }
          handleSelectSong(clip);
          logWebUserEvent({
            actionName: 'HookSongSelection',
            context: {
              tab,
              searchText: searchText || undefined,
              isUserSongOwner:
                session?.userId !== undefined &&
                session?.userId === clip.user_id,
              tabSongIndex,
            },
          });
        }}
      >
        <div
          className={'relative'}
          onClick={(e) => {
            e.stopPropagation();
            if (disableControls) {
              return;
            }
            if (inPlaybar) {
              playbar.togglePlay();
            } else {
              // Start at snippet
              playbar.playClip(
                clip,
                null,
                null,
                false,
                clip.audioMetadata?.audio_snippet?.start_timestamp
              );
            }
          }}
        >
          <ImageWithFallback
            className='lazyload h-16 w-12 rounded-lg object-cover'
            src={clip.image_url || ''}
            fallbackSrc={clip.image_url || ''}
            imageSize={SMALL_IMAGE}
            alt='Song Image'
          />
          {!disableControls && (
            <div
              className={clsx(
                'absolute inset-0 flex items-center justify-center rounded-lg',
                {
                  'bg-opacity-black-30 text-foreground-primary-glass': playing,
                }
              )}
            >
              {playing ? (
                <PulsingLinesIcon className='h-7 w-7' />
              ) : (
                <PlayIcon className='h-7 w-7' />
              )}
            </div>
          )}
        </div>
        <div className='flex min-w-0 flex-1 flex-col gap-0.5'>
          <p className='text-md font-medium text-foreground-primary'>
            <Link
              href={`/song/${clip.id}`}
              onClick={(e) => {
                if (e.metaKey || e.shiftKey) {
                  e.stopPropagation();
                } else {
                  e.preventDefault();
                }
              }}
            >
              {clip.title || 'Untitled Clip'}
            </Link>
          </p>
          <p className='text-sm text-foreground-primary'>
            {`${formatDuration(clip.metadata.duration)}`} {' · '}
            <span className='text-foreground-secondary'>{formattedTags}</span>
          </p>
        </div>
        {disableControls ? null : (
          <div className='opacity-0 group-hover:opacity-100'>
            <Button
              className='px-6'
              variant={ButtonVariant.Primary}
              shape={ButtonShape.Pill}
              size={ButtonSize.Small}
            >
              Use
            </Button>
          </div>
        )}
      </div>
    );
  }
);

export const SuggestedSongList = observer(
  ({
    filter,
    searchTerm = '',
  }: {
    filter: SuggestedSongFilter;
    searchTerm?: string;
  }) => {
    const apiClient = useApiClient();
    const { clips } = useStores();
    const sentinelRef = useRef<HTMLDivElement>(null);

    const normalizedSearchTerm = useMemo(
      () => searchTerm?.trim() || '',
      [searchTerm]
    );

    const { data, isLoading, isFetching, fetchNextPage, hasNextPage } =
      useInfiniteQuery({
        queryKey: ['suggested-songs', filter, normalizedSearchTerm],
        initialPageParam: 0,
        refetchOnMount: 'always',
        queryFn: async ({ pageParam: startIndex = 0 }) => {
          // Use the enhanced suggested clips API with search support
          const { data: suggestedClipsResponse } = await apiClient.GET(
            '/api/video/hooks/suggested_clips',
            {
              params: {
                query: {
                  page_size: SUGGESTED_SONG_PAGE_SIZE,
                  is_liked: filter === 'liked',
                  is_public_only: filter === 'public',
                  start_index: startIndex,
                  search_term: normalizedSearchTerm,
                },
              },
            }
          );

          if (suggestedClipsResponse && suggestedClipsResponse.clips) {
            clips.updateClips(suggestedClipsResponse.clips as GeneratedClip[]);
          }
          return (suggestedClipsResponse?.clips as GeneratedClip[]) || [];
        },
        getNextPageParam: (lastPage, allPages) => {
          // If the last page has items and is full (20 items), there might be more
          return lastPage.length === SUGGESTED_SONG_PAGE_SIZE
            ? allPages.length * SUGGESTED_SONG_PAGE_SIZE
            : undefined;
        },
      });

    // Intersection observer for automatic infinite scrolling
    useEffect(() => {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting && hasNextPage && !isFetching) {
            fetchNextPage();
          }
        },
        { threshold: 0.1 }
      );

      if (sentinelRef.current) {
        observer.observe(sentinelRef.current);
      }

      return () => observer.disconnect();
    }, [hasNextPage, isFetching, fetchNextPage]);

    const allClips = data?.pages.flat() || [];

    return isLoading ? (
      <div className='flex h-fit w-full items-center justify-center'>
        <SpinnerSVG />
      </div>
    ) : (
      <div className='m-3 flex flex-col gap-2'>
        {allClips.length > 0
          ? allClips.map((clip: GeneratedClip, index: number) => (
              <SuggestedSongItem
                key={clip.id}
                clip={clip}
                tab={filter}
                searchText={normalizedSearchTerm}
                tabSongIndex={index}
              />
            ))
          : normalizedSearchTerm
            ? 'No songs found for your search'
            : 'No songs available'}
        {/* Sentinel element for intersection observer */}
        {hasNextPage && (
          <div
            ref={sentinelRef}
            className='flex h-4 items-center justify-center'
          >
            {isFetching && <SpinnerSVG />}
          </div>
        )}
      </div>
    );
  }
);

export const SelectHookSongModalContent = observer(
  (props: { onClose?: () => void }) => {
    const { onClose } = props;

    const ALL = 'all';
    const PUBLIC = 'public';
    const LIKED = 'liked';

    const [inputValue, setInputValue] = useState('');
    const [searchTerm, setSearchTerm] = useState('');

    const handleSearchChange = useCallback(
      (e: React.ChangeEvent<HTMLTextAreaElement>) => {
        setInputValue(e.currentTarget.value);
      },
      []
    );

    const handleKeyDown = useCallback(
      (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
        if (e.key === 'Enter') {
          // Prevent default behavior (form submission, line break, etc.)
          e.preventDefault();
        }
      },
      []
    );

    const clearSearch = useCallback(() => {
      setInputValue('');
    }, []);

    useEffect(() => {
      // Update immediately when we're clearing the search
      if (!inputValue.trim()) {
        setSearchTerm('');
        return;
      }

      // Schedule the debounced search term update
      const timeout = setTimeout(() => {
        setSearchTerm(inputValue.trim().substring(0, MAX_SEARCH_TERM_LEN));
      }, SEARCH_DEBOUNCE_DELAY);

      return () => {
        clearTimeout(timeout);
      };
    }, [inputValue]);

    // Load suggested clips just to see whether we have any
    const apiClient = useApiClient();
    const { data: hasAnySongs } = useQuery({
      queryKey: ['suggested-songs-count'],
      queryFn: async () => {
        // Use the enhanced suggested clips API with search support
        const response = await apiClient.GET(
          '/api/video/hooks/suggested_clips',
          {
            params: {
              query: {
                page_size: SUGGESTED_SONG_PAGE_SIZE,
              },
            },
          }
        );
        return !!response.data?.clips?.length;
      },
      refetchOnMount: 'always',
    });

    return (
      <div className='flex h-full w-full flex-col gap-4'>
        <div className='flex w-full flex-row justify-between gap-4'>
          <div className='flex h-fit w-full flex-col items-center p-2 pt-4'>
            <h1 className='text-2xl'>Create a New Hook</h1>
            <h2 className='text-lg'>Select Song</h2>
          </div>
          {onClose ? (
            <div className='absolute top-1 right-1 m-4'>
              <CloseButton onClick={onClose} />
            </div>
          ) : null}
        </div>

        {hasAnySongs === false ? (
          <div className='flex w-full flex-1 flex-col items-center justify-center gap-4 pb-10'>
            <div className='relative size-36'>
              <div className='absolute top-1/2 left-1/2 flex h-30 w-20 origin-bottom -translate-1/2 -rotate-12 items-center justify-center rounded-xl bg-background-tertiary/50' />
              <div className='absolute top-1/2 left-1/2 flex h-30 w-20 origin-bottom -translate-1/2 rotate-12 items-center justify-center rounded-xl bg-background-tertiary text-foreground-primary'>
                <MusicIcon className='size-6' />
              </div>
            </div>
            <p>You have no songs yet</p>
            <Button
              href='/create'
              variant={ButtonVariant.Primary}
              shape={ButtonShape.Pill}
              size={ButtonSize.Medium}
            >
              Create Your First Song
            </Button>
          </div>
        ) : (
          <>
            {/* Search Input */}
            <div className='px-4'>
              <div className='flex flex-row items-center gap-1 rounded-full bg-background-secondary py-1 pr-1 pl-4 text-foreground-primary'>
                <SearchIcon className='h-4 w-4 flex-shrink-0 text-foreground-secondary' />
                <TextareaV2
                  value={inputValue}
                  placeholder='Search by song name or style'
                  className='flex-1 rounded-none border-0 bg-transparent p-0'
                  textAreaClassName={clsx(
                    '-my-1 px-2 py-1 border-0',
                    'h-8 leading-8',
                    'whitespace-nowrap text-foreground-primary overflow-x-hidden overflow-x-auto',
                    'placeholder:text-foreground-secondary'
                  )}
                  onChange={handleSearchChange}
                  onKeyDown={handleKeyDown}
                  rows={1}
                  maxRows={1}
                  maxLength={MAX_SEARCH_TERM_LEN}
                  charCountMode={CharCountMode.Never}
                />
                {inputValue && (
                  <Button
                    icon={CloseIcon}
                    variant={ButtonVariant.Tertiary}
                    size={ButtonSize.Small}
                    shape={ButtonShape.Rounded}
                    onClick={clearSearch}
                    aria-label='Clear search'
                    className='flex-shrink-0 p-1'
                    iconClassName='w-3 h-3'
                  />
                )}
              </div>
            </div>
            <Tabs
              defaultSelectedKey={ALL}
              keyboardActivation='manual'
              className='w-full overflow-y-hidden'
            >
              <TabList
                aria-label='Search type'
                className='flex w-full flex-row justify-between gap-4 px-4 pb-4 font-medium'
              >
                <Tab
                  id={ALL}
                  className={clsx(REACT_ARIA_TABS_STYLE, 'w-full text-center')}
                >
                  All
                </Tab>
                <Tab
                  id={PUBLIC}
                  className={clsx(REACT_ARIA_TABS_STYLE, 'w-full text-center')}
                >
                  Public
                </Tab>
                <Tab
                  id={LIKED}
                  className={clsx(REACT_ARIA_TABS_STYLE, 'w-full text-center')}
                >
                  Liked
                </Tab>
              </TabList>

              <TabPanel id={ALL} className='h-[90%] overflow-y-scroll'>
                <SuggestedSongList filter={ALL} searchTerm={searchTerm} />
              </TabPanel>
              <TabPanel id={PUBLIC} className='h-[90%] overflow-y-scroll'>
                <SuggestedSongList filter={PUBLIC} searchTerm={searchTerm} />
              </TabPanel>
              <TabPanel id={LIKED} className='h-[90%] overflow-y-scroll'>
                <SuggestedSongList filter={LIKED} searchTerm={searchTerm} />
              </TabPanel>
            </Tabs>
          </>
        )}
      </div>
    );
  }
);

export const CreateHookModal = observer(() => {
  const router = useRouter();
  const { menus, playbar } = useStores();
  const { currentScreen, selectedSong, selectedVideo } =
    useContext(HookFormContext);
  const allowClose = !!(selectedSong && selectedVideo);
  const handleClose = useCallback(() => {
    if (allowClose) {
      menus.closeModal(ModalTypes.SELECT_HOOK_SONG);
    }
  }, [menus, allowClose]);
  const handleExit = useCallback(() => {
    menus.closeModal(ModalTypes.SELECT_HOOK_SONG);
    router.push(`/hooks`);
  }, [menus, allowClose, router]);

  // clear playbar when modal is closed
  useEffect(() => {
    return () => {
      playbar.unsetClip();
    };
  }, [playbar]);

  const isSelectSongScreen = ['selectSongInFlow', 'selectSong'].includes(
    currentScreen
  );
  const isUploadVideoScreen = ['uploadVideoInFlow', 'uploadVideo'].includes(
    currentScreen
  );
  const isPostHookScreen = currentScreen === 'postHook';

  useEffect(() => {
    switch (currentScreen) {
      case 'uploadVideoInFlow':
      case 'uploadVideo':
        logWebUserEvent({
          actionName: 'HookUploadVideoModalViewed',
          context: {
            uploadId: undefined,
            clipId: selectedSong?.id || undefined,
          },
        });
        break;
      case 'selectSongInFlow':
      case 'selectSong':
        logWebUserEvent({
          actionName: 'HookSelectSongModalViewed',
          context: {},
        });
        break;
      case 'postHook':
        logWebUserEvent({
          actionName: 'HookPostModalViewed',
          context: {},
        });
        break;
      default:
        break;
    }
  }, [currentScreen]);

  return (
    <Modal
      isOpen
      onClose={handleClose}
      data-qaid='modal-publish-clip-title'
      isCentered
      blockScrollOnMount={false}
    >
      <ModalOverlay />
      <ModalContent
        borderRadius='32px'
        maxW={{ base: '95%', md: '600px' }}
        bg='var(--color-background-secondary)'
        color='var(--color-foreground-primary)'
        position='relative'
        overflow='auto'
        className='h-[60svh] max-h-[800px] max-md:h-[calc(100vh-1rem)] md:min-h-[calc(min(90svh,600px))]'
      >
        <div className='relative h-full w-full'>
          {isSelectSongScreen && (
            <SelectHookSongModalContent
              onClose={allowClose ? handleClose : handleExit}
            />
          )}
          {isUploadVideoScreen && <UploadHookVideoScreen />}
          {isPostHookScreen && <PostHookScreen />}
        </div>
      </ModalContent>
    </Modal>
  );
});

export default CreateHookModal;
