import { useQuery } from '@tanstack/react-query';
import { observer } from 'mobx-react-lite';
import {
  createContext,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { useApiClient } from '@/lib/apiClient';
import { components } from '@/lib/gen';
import { Clip } from '@/state/clipStore';

export type ContestSchema = components['schemas']['ContestSchema'];

export const useAllContestClips = () => {
  const apiClient = useApiClient();
  const result = useQuery({
    queryKey: ['contest-clips'],
    queryFn: async () => {
      const { data } = await apiClient.GET('/api/contests/');
      return data;
    },
    staleTime: 5 * 60 * 1000,
  });

  return result;
};

export const useContestClips = () => {
  const [contestClipsIds, setContestClipsIds] = useState<string[]>([]);
  const [contestClips, setContestClips] = useState<ContestSchema[]>([]);
  const { session, contest } = useStores();

  const { data: contestData, isLoading: isContestClipsLoading } =
    useAllContestClips();

  useEffect(() => {
    if (contestData && !isContestClipsLoading) {
      setContestClips(contestData.contests);
      const allBaseClips =
        contestData.contests?.flatMap(
          (contest: ContestSchema) => contest.base_clip_ids
        ) || [];

      setContestClipsIds(allBaseClips);
    }
  }, [contestData, isContestClipsLoading]);

  const isContestBaseClip = useCallback(
    ({ clipId }: { clipId: string }) => {
      return !isContestClipsLoading && contestClipsIds.includes(clipId);
    },
    [contestClipsIds, isContestClipsLoading]
  );

  const isActiveContestBaseClip = useCallback(
    ({ clipId }: { clipId: string }) => {
      const currentContest = contestClips.find((contest) =>
        contest.base_clip_ids.includes(clipId)
      );

      return (
        currentContest &&
        currentContest.end_time &&
        new Date() < new Date(currentContest.end_time)
      );
    },
    [contestClips]
  );

  const getContestIdByBaseClipId = useCallback(
    ({ clipId }: { clipId: string }) => {
      return contestClips.find((contest) =>
        contest.base_clip_ids.includes(clipId)
      )?.id;
    },
    [contestClips]
  );

  const isContestEligible = useCallback(({ clip }: { clip?: Clip }) => {
    return Boolean(clip?.is_contest_clip);
  }, []);

  const isContestSubmission = useCallback(
    ({ clip }: { clip?: Clip }) => {
      // TODO: do we want to hide contest submission to other users?
      if (!clip) {
        return false;
      }
      return Boolean(
        isContestEligible({ clip }) &&
          (contest.isClipSubmitted(clip.id) ||
            (clip.metadata.contest_ids && clip.metadata.contest_ids.length > 0))
      );
    },
    [session.user?.id, isContestEligible, contest]
  );

  const isActiveContest = useCallback(
    ({ contestId }: { contestId: string }) => {
      const currentContest = contestClips.find(
        (contest) => contest.id === contestId
      );

      return (
        currentContest &&
        currentContest.end_time &&
        new Date() < new Date(currentContest.end_time)
      );
    },
    [contestClips]
  );

  const hasActiveContest = useCallback(
    ({ contestIds }: { contestIds: string[] }) => {
      return contestIds.some((contestId) => isActiveContest({ contestId }));
    },
    [isActiveContest]
  );

  const isActiveContestSubmission = useCallback(
    ({ clip }: { clip?: Clip }) => {
      if (!clip) {
        return false;
      }
      const contestIds = clip.metadata?.contest_ids;
      if (!contestIds?.length) {
        return false;
      }
      return isContestSubmission({ clip }) && hasActiveContest({ contestIds });
    },
    [isContestSubmission, hasActiveContest]
  );

  return useMemo(
    () => ({
      isContestBaseClip,
      isActiveContestBaseClip,
      isContestSubmission,
      isActiveContestSubmission,
      isContestEligible,
      isLoading: isContestClipsLoading,
      getContestIdByBaseClipId,
    }),
    [
      isContestBaseClip,
      isActiveContestBaseClip,
      isContestSubmission,
      isActiveContestSubmission,
      isContestEligible,
      isContestClipsLoading,
      getContestIdByBaseClipId,
    ]
  );
};

const ContestClipContext = createContext<ReturnType<typeof useContestClips>>(
  undefined as never
);

export const ContestClipProvider: React.FC<React.PropsWithChildren> = observer(
  (props) => {
    const contestClips = useContestClips();
    return (
      <ContestClipContext.Provider value={contestClips}>
        {props.children}
      </ContestClipContext.Provider>
    );
  }
);

export default ContestClipContext;
