import { useMutation } from '@tanstack/react-query';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import CreateFormContext from '@/app/(root)/create/v2/CreateFormContext';
import {
  setCurrentLyrics,
  setCurrentTitle,
  setLastLyricsGeneration,
} from '@/app/(root)/create/v2/actions/currentModeSetters';
import { toast } from '@/components/toast/Toast';
import formatLyricsGenerationErrorForToast from '@/lib/formatLyricsGenerationErrorForToast';

import { useContextSelector } from './useContextSelector';
import { useGenerateLyrics } from './useGenerateLyrics';
import useLyrics from './useLyrics';
import useLyricsABFlowTracking from './useLyricsABFlowTracking';
import { useRateLyrics } from './useRateLyrics';

const useLyricsABFlow = () => {
  const { genForm } = useStores();
  const setCreateV2State = useContextSelector(
    CreateFormContext,
    (ctx) => ctx?.setState ?? null
  );

  const setCreateV2Lyrics = useCallback(
    (lyrics: string) => {
      if (!setCreateV2State) return;
      setCreateV2State(setCurrentLyrics(lyrics));
    },
    [setCreateV2State]
  );

  const setCreateV2Title = useCallback(
    (title: string) => {
      if (!setCreateV2State) return;
      setCreateV2State(setCurrentTitle(title));
    },
    [setCreateV2State]
  );

  const setCreateV2LastLyricsGeneration = useCallback(
    (
      lastLyricsGeneration: {
        title: string;
        text: string;
        prompt: string;
        lyricsModel: string;
      } | null
    ) => {
      if (!setCreateV2State) return;
      setCreateV2State(setLastLyricsGeneration(lastLyricsGeneration));
    },
    [setCreateV2State]
  );

  const lyricsABFlowTracking = useLyricsABFlowTracking();

  const prompt = genForm.lyricsPrompt;
  const setPrompt = genForm.setLyricsPrompt;

  const lyricsModel = genForm.lyricsModel;
  const setLyricsModel = genForm.setLyricsModel;
  const [lastRatedRequestId, setLastRatedRequestId] = useState<string | null>(
    null
  );

  const [generationCancelled, setGenerationCancelled] = useState(false);

  const generateLyrics = useGenerateLyrics();

  const generateLyricsMutation = useMutation({
    mutationFn: useCallback(
      (payload) => {
        setGenerationCancelled(false);
        return generateLyrics(payload);
      },
      [generateLyrics]
    ) as typeof generateLyrics,

    onSuccess: (data, payload) =>
      lyricsABFlowTracking.logGenerationInitiated(data.requestId, payload),

    onError: (error, payload) => {
      toast({
        ...formatLyricsGenerationErrorForToast(error.message),
        status: 'error',
        duration: 4000,
        isClosable: true,
      });
      lyricsABFlowTracking.logGenerationError(error, payload);
    },
  });

  const [lyricsADisplayed, setLyricsADisplayed] = useState(false);
  const [lyricsBDisplayed, setLyricsBDisplayed] = useState(false);

  const rateLyrics = useRateLyrics();

  const rateLyricsIfBothDisplayed = useCallback(
    ({
      requestId,
      preferredLyricsId,
    }: {
      requestId: string;
      preferredLyricsId: string | null;
    }) => {
      if (lyricsADisplayed && lyricsBDisplayed) {
        rateLyrics({ requestId, preferredLyricsId });
      }
    },
    [lyricsADisplayed, lyricsBDisplayed, rateLyrics]
  );

  const rejectLyricsIfNoneAccepted = useCallback(
    (requestId: string) => {
      if (lastRatedRequestId !== requestId) {
        setLastRatedRequestId(requestId);
        genForm.lastLyricsGeneration = null;
        setCreateV2LastLyricsGeneration(null);
        rateLyricsIfBothDisplayed({
          requestId,
          preferredLyricsId: null,
        });
      }
    },
    [
      lastRatedRequestId,
      genForm,
      rateLyricsIfBothDisplayed,
      setCreateV2LastLyricsGeneration,
    ]
  );

  const {
    data: lyricsA,
    cancel: cancelA,
    error: lyricsAError,
  } = useLyrics(generateLyricsMutation.data?.lyricsAId);

  const {
    data: lyricsB,
    cancel: cancelB,
    error: lyricsBError,
  } = useLyrics(generateLyricsMutation.data?.lyricsBId);

  const cancel = useCallback(() => {
    setGenerationCancelled(true);
    cancelA();
    cancelB();
  }, [cancelA, cancelB]);

  const firstLyricsError = useMemo(
    () => lyricsAError || lyricsBError,
    [lyricsAError, lyricsBError]
  );

  useEffect(() => {
    if (firstLyricsError) {
      toast({
        ...formatLyricsGenerationErrorForToast(firstLyricsError.message),
        status: 'error',
        duration: 4000,
        isClosable: true,
      });
      cancel();
    }
  }, [firstLyricsError, cancel]);

  const generating =
    !generationCancelled &&
    (generateLyricsMutation.isPending ||
      (generateLyricsMutation.data && (!lyricsA?.text || !lyricsB?.text)));

  const generate = useCallback(
    async (explicitPrompt?: string) => {
      await generateLyricsMutation.mutate({
        prompt: explicitPrompt || prompt,
        lyricsModel,
      });
    },
    [prompt, lyricsModel, generateLyricsMutation]
  );

  const handleAcceptLyrics = useCallback(
    (
      requestId: string,
      lyrics: { text: string; title: string; id: string }
    ) => {
      setLastRatedRequestId(requestId);
      genForm.setLyrics(lyrics.text);
      genForm.setTitle(lyrics.title);
      setCreateV2Lyrics(lyrics.text);
      setCreateV2Title(lyrics.title);

      if (generateLyricsMutation.variables) {
        setCreateV2LastLyricsGeneration({
          title: lyrics.title,
          text: lyrics.text,
          prompt: generateLyricsMutation.variables?.prompt || '',
          lyricsModel: generateLyricsMutation.variables?.lyricsModel || '',
        });

        genForm.lastLyricsGeneration = {
          title: lyrics.title,
          lyrics: lyrics.text,
          prompt: generateLyricsMutation.variables?.prompt,
          lyricsModel: generateLyricsMutation.variables?.lyricsModel,
        };
      }

      rateLyricsIfBothDisplayed({
        requestId,
        preferredLyricsId: lyrics.id,
      });
    },
    [
      genForm,
      generateLyricsMutation.variables,
      rateLyricsIfBothDisplayed,
      setCreateV2LastLyricsGeneration,
      setCreateV2Lyrics,
      setCreateV2Title,
    ]
  );

  return useMemo(
    () => ({
      prompt,
      setPrompt,
      lyricsModel,
      setLyricsModel,
      generate,
      generating,
      cancel,
      generationCancelled,
      rejectLyricsIfNoneAccepted,
      lyricsA,
      lyricsB,
      setLyricsADisplayed,
      setLyricsBDisplayed,
      handleAcceptLyrics,
      requestId: generateLyricsMutation.data?.requestId,
    }),
    [
      prompt,
      setPrompt,
      lyricsModel,
      setLyricsModel,
      generate,
      generating,
      cancel,
      generationCancelled,
      rejectLyricsIfNoneAccepted,
      lyricsA,
      lyricsB,
      setLyricsADisplayed,
      setLyricsBDisplayed,
      handleAcceptLyrics,
      generateLyricsMutation.data?.requestId,
    ]
  );
};

export default useLyricsABFlow;
