'use client';

import { useQuery } from '@tanstack/react-query';
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import { useRouter } from 'next/navigation';
import { useCallback, useMemo, useState } from 'react';
import { twMerge } from 'tailwind-merge';

import { useStores } from '@/app/(root)/AppProviders';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import DiscoverCarousel from '@/components/carousel/carousels/DiscoverCarousel';
import { redirectToRemixFromClipId } from '@/components/contest/util';
import { shouldShowContestSubmissions } from '@/components/contest/util';
import SongCard from '@/components/song/SongCard';
import { fetchClip } from '@/hooks/useClip';
import { ContestSchema, useAllContestClips } from '@/hooks/useContestClip';
import { RemixIcon, SortDescendingIcon, ThumbsUpIcon } from '@/icons/generated';
import { useApiClient } from '@/lib/apiClient';
import { ContextType } from '@/logging/contextTypes';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { Clip } from '@/state/clipStore';

interface ContestSubmissionsProps {
  contest?: ContestSchema;
  baseClipId?: string; // when provided without contest, we will resolve the contest by base clip id
  className?: string;
  contextClipId?: string; // used for queue context; defaults to first base clip
  clipId?: string; // used for remix CTAs
  staffOnlyText?: string; // optional red notice when section is staff-only
  title?: string; // optional title override (e.g., "Remix Submissions")
  showCta?: boolean; // optionally hide CTA for contexts like song/profile
  location?: string; // used for logging
  titleClassName?: string; // optional class name for title
  containerClassName?: string; // optional class name for container
}

type SortType = 'likes' | 'latest';

const ContestSubmissions: React.FC<ContestSubmissionsProps> = observer(
  ({
    contest,
    baseClipId,
    className,
    contextClipId,
    clipId,
    staffOnlyText,
    title,
    showCta = true,
    location,
    titleClassName,
    containerClassName,
  }) => {
    const [currentSort, setCurrentSort] = useState<SortType>('latest');
    const [scrollState, setScrollState] = useState({
      maskLeft: 0,
      maskRight: 0,
    });

    const { playbar, queue, clips, session } = useStores();
    const apiClient = useApiClient();
    const router = useRouter();

    // If a contest is not provided, try to resolve via baseClipId
    const {
      data: allContestsData,
      isLoading: isContestsLoading,
      error: contestsError,
    } = useAllContestClips();

    const effectiveContest: ContestSchema | undefined = useMemo(() => {
      if (contest) return contest;
      if (!baseClipId) return undefined;
      const all = allContestsData?.contests || [];
      return all.find((c: any) => c.base_clip_ids?.includes(baseClipId));
    }, [contest, baseClipId, allContestsData]);

    const effectiveContextClipId = useMemo(() => {
      return (
        contextClipId ||
        effectiveContest?.base_clip_ids?.[0] ||
        effectiveContest?.id ||
        ''
      );
    }, [contextClipId, effectiveContest?.base_clip_ids, effectiveContest?.id]);

    const handleChangeSort = useCallback(
      (nextSort: SortType) => {
        setCurrentSort(nextSort);
        if (effectiveContest?.id) {
          logWebUserEvent({
            actionName: 'ContestSubmissionsSortChanged',
            context: {
              contestId: effectiveContest.id,
              contestSlug: effectiveContest.slug ?? undefined,
              sortBy: nextSort,
              location,
            },
          });
        }
      },
      [effectiveContest?.id, effectiveContest?.slug]
    );

    // Respect submissions visibility levels if present
    const shouldShowSubmissions = useMemo(() => {
      return shouldShowContestSubmissions(
        effectiveContest,
        Boolean(session.isStaff)
      );
    }, [effectiveContest, session.isStaff]);

    // Top submissions by likes
    const {
      data: topLikedClips,
      isLoading: isLikedLoading,
      error: likedError,
    } = useQuery({
      queryKey: ['contest-top-clips-likes', effectiveContest?.id],
      queryFn: async () => {
        const response = await apiClient.GET(
          '/api/contests/top_k_contest_clips/{contest_id}/{k}',
          {
            params: {
              path: {
                contest_id: effectiveContest!.id,
                k: 12,
              },
              query: {
                sort_by: 'likes',
                need_hydration: true,
              },
            },
          }
        );

        const clipsData =
          response.data
            ?.map((contestClip) => contestClip.clip?.[0])
            .filter(Boolean) || [];

        if (clipsData.length > 0) {
          clips.updateClips(clipsData as Clip[]);
        }

        return clipsData as Clip[];
      },
      enabled: !!effectiveContest?.id && shouldShowSubmissions,
      staleTime: 5 * 60 * 1000,
    });

    // Top submissions by recency
    const {
      data: topLatestClips,
      isLoading: isLatestLoading,
      error: latestError,
    } = useQuery({
      queryKey: ['contest-top-clips-latest', effectiveContest?.id],
      queryFn: async () => {
        const response = await apiClient.GET(
          '/api/contests/top_k_contest_clips/{contest_id}/{k}',
          {
            params: {
              path: {
                contest_id: effectiveContest!.id,
                k: 12,
              },
              query: {
                sort_by: 'recency',
                need_hydration: true,
              },
            },
          }
        );

        const clipsData =
          response.data
            ?.map((contestClip) => contestClip.clip?.[0])
            .filter(Boolean) || [];

        if (clipsData.length > 0) {
          clips.updateClips(clipsData as Clip[]);
        }

        return clipsData as Clip[];
      },
      enabled: !!effectiveContest?.id && shouldShowSubmissions,
      staleTime: 5 * 60 * 1000,
    });

    // Total submissions, for determining whether to show "See more"
    const {
      data: totalSubmissionsData,
      isLoading: isTotalLoading,
      error: totalError,
    } = useQuery({
      queryKey: ['contest-submissions-count', effectiveContest?.id],
      queryFn: async () => {
        const response = await apiClient.GET('/api/contests/contest_clips/', {
          params: {
            query: {
              contest_id: effectiveContest!.id,
              page: 1,
              page_size: 10,
            },
          },
        });
        return response.data;
      },
      enabled: !!effectiveContest?.id && shouldShowSubmissions,
      staleTime: 5 * 60 * 1000,
    });

    const isLoading =
      isContestsLoading || isLikedLoading || isLatestLoading || isTotalLoading;
    const error = contestsError || likedError || latestError || totalError;

    const submissions = useMemo<Clip[]>(() => {
      switch (currentSort) {
        case 'likes':
          return (topLikedClips || []) as Clip[];
        case 'latest':
          return (topLatestClips || []) as Clip[];
        default:
          return [];
      }
    }, [currentSort, topLikedClips, topLatestClips]);

    const handlePlayClip = useCallback(
      async (submission: Clip, index: number) => {
        if (!submission || !submissions.length) return;
        try {
          const fullClip = await fetchClip(clips, submission.id, false, true);
          queue.setPlayContext({
            contextType: ContextType.SongRecommendationsRemixes,
            contextId: effectiveContextClipId,
            currentIndex: index,
            clips: submissions,
            pendingClips: [],
          });
          playbar.playClip(fullClip);
        } catch (e) {
          queue.setPlayContext({
            contextType: ContextType.SongRecommendationsRemixes,
            contextId: effectiveContextClipId,
            currentIndex: index,
            clips: submissions,
            pendingClips: [],
          });
          playbar.playClip(submission);
        }
      },
      [submissions, effectiveContextClipId, queue, playbar, clips]
    );

    const renderSubmissionItem = useCallback(
      ({ item, index }: { item: Clip; index: number }) => (
        <SongCard
          key={item.id}
          clip={item}
          onClick={() => {}}
          onPlay={() => handlePlayClip(item, index)}
          index={index}
          contextType={ContextType.SongRecommendationsRemixes}
          contextId={effectiveContextClipId}
        />
      ),
      [handlePlayClip, effectiveContextClipId]
    );

    const handleSeeMore = useCallback(() => {
      if (effectiveContest?.submissions_playlist_id) {
        router.push(`/playlist/${effectiveContest.submissions_playlist_id}`);
        if (effectiveContest.id) {
          logWebUserEvent({
            actionName: 'ContestSubmissionsPlaylistClicked',
            context: {
              contestId: effectiveContest.id,
              contestSlug: effectiveContest.slug ?? undefined,
              source: 'submissions_footer',
              clipId,
              location,
            },
          });
        }
      }
    }, [effectiveContest?.submissions_playlist_id, router]);

    const handleScrollStateChange = useCallback(
      (maskLeft: number, maskRight: number) => {
        setScrollState({ maskLeft, maskRight });
      },
      []
    );

    const tabs = [
      { id: 'latest' as const, label: 'Recent', icon: <SortDescendingIcon /> },
      { id: 'likes' as const, label: 'Most Liked', icon: <ThumbsUpIcon /> },
    ];

    if (error || !effectiveContest || !shouldShowSubmissions) {
      return null;
    }

    const hasSubmissions = submissions.length > 0;
    if (!hasSubmissions && !isLoading) {
      return (
        <div className={clsx('w-full', className)}>
          <div className='mb-6'>
            <h2
              className={twMerge(
                'mb-[8px] text-[30px] leading-[30px] font-normal text-foreground-primary md:text-[60px] md:leading-[54px]',
                titleClassName
              )}
            >
              {title || 'Submissions'}
            </h2>
            {staffOnlyText && (
              <div className='-mt-2 mb-4 text-sm font-medium text-accent-pink-on-primary'>
                {staffOnlyText}
              </div>
            )}
            <div className='py-12 text-center'>
              <div className='text-sm text-foreground-secondary'>
                No remix submissions yet. Be the first to submit a remix!
              </div>
            </div>
          </div>
        </div>
      );
    }

    return (
      <div className={clsx('w-full', className)}>
        <div className='mb-6'>
          <div
            className={twMerge(
              'mt-[100px] mb-[8px] flex items-center justify-between',
              containerClassName
            )}
          >
            <h2
              className={twMerge(
                'text-[28px] leading-[32px] font-normal text-foreground-primary md:text-[42px] md:leading-[48px]',
                titleClassName
              )}
            >
              {title || 'Submissions'}
            </h2>
            {totalSubmissionsData &&
              totalSubmissionsData.clips &&
              totalSubmissionsData.clips.length > 5 &&
              effectiveContest.submissions_playlist_id && (
                <Button
                  onClick={handleSeeMore}
                  variant={ButtonVariant.Tertiary}
                  size={ButtonSize.Small}
                  shape={ButtonShape.Rounded}
                >
                  See more →
                </Button>
              )}
          </div>
          {staffOnlyText && (
            <div className='-mt-2 mb-4 text-sm font-medium text-accent-pink-on-primary'>
              {staffOnlyText}
            </div>
          )}

          <div className='mb-6'>
            <div className='flex gap-2'>
              {tabs.map((tab) => (
                <Button
                  key={tab.id}
                  onClick={() => handleChangeSort(tab.id)}
                  size={ButtonSize.Small}
                  iconStart={tab.icon}
                  shape={ButtonShape.Pill}
                  variant={
                    currentSort === tab.id
                      ? ButtonVariant.Primary
                      : ButtonVariant.Secondary
                  }
                >
                  {tab.label}
                </Button>
              ))}
            </div>
          </div>

          <div className='relative w-full'>
            {isLoading ? (
              <div className='flex gap-4 overflow-x-auto pb-2'>
                {Array.from({ length: 12 }).map((_, i) => (
                  <div
                    key={i}
                    className='aspect-[3/4] w-40 flex-shrink-0 animate-pulse rounded-lg bg-background-secondary'
                  />
                ))}
              </div>
            ) : (
              <div className='relative overflow-hidden'>
                {scrollState.maskLeft > 0.1 && (
                  <div className='pointer-events-none absolute top-0 left-0 z-10 h-full w-8 bg-gradient-to-r from-background-primary to-transparent max-sm:hidden' />
                )}
                {scrollState.maskRight > 0.1 && (
                  <div className='pointer-events-none absolute top-0 right-0 z-10 h-full w-8 bg-gradient-to-l from-background-primary to-transparent max-sm:hidden' />
                )}
                <DiscoverCarousel
                  items={submissions}
                  renderItem={renderSubmissionItem}
                  itemClassName='w-40 max-md:max-w-[calc(100vw-4rem)]'
                  carouselClassName='relative'
                  itemsContainerClassName='flex flex-row gap-4'
                  maskClassName=''
                  onScrollStateChange={handleScrollStateChange}
                  buttonVariant={ButtonVariant.Standard}
                  buttonShape={ButtonShape.Pill}
                  buttonSize={ButtonSize.Small}
                  buttonClassName='bg-background-primary shadow-md'
                />
              </div>
            )}
          </div>
          {showCta && (
            <div className='flex justify-center py-[48px]'>
              <Button
                variant={ButtonVariant.Primary}
                size={ButtonSize.Small}
                shape={ButtonShape.Pill}
                iconStart={<RemixIcon />}
                onClick={() => {
                  if (effectiveContest?.id) {
                    logWebUserEvent({
                      actionName: 'ContestLandingRemixCtaClicked',
                      context: {
                        contestId: effectiveContest.id,
                        contestSlug: effectiveContest.slug ?? undefined,
                        source: 'submissions_footer',
                        clipId,
                      },
                    });
                  }
                  // Use imperative redirect to aid iOS deep-linking
                  redirectToRemixFromClipId(clipId);
                }}
              >
                Create Your Own Remix
              </Button>
            </div>
          )}
        </div>
      </div>
    );
  }
);

export default ContestSubmissions;
