import { useCallback, useEffect, useRef, useState } from "react";
import { useUser } from "@clerk/nextjs";
import { track } from "@vercel/analytics";
import { animate, m } from "framer-motion";

import CallToAction from "@/components/call-to-action";
import Icon from "@/components/icon";
import Object3dBounds from "@/components/quiz-section/object3d-bounds";
import { SUNO_PROMOTION } from "@/config/constants";
import { getCopy } from "@/config/copy";
import { GENRES, QUIZ_STEPS, QUIZ_STEPS_VARIANTS } from "@/config/data";
import { useInterval } from "@/hooks/useInterval";
import useQueryData from "@/hooks/useQueryData";
import { useStore } from "@/store";
import { type QuizSectionItem, type SunoSong } from "@/types";

import styles from "./styles.module.scss";

const delayGerationCheck = 5000;
const MAX_CLIPS = 3;

function replaceName(str: string, nameTo: string) {
  return str.replace("[NAMETO]", nameTo || "your love");
}

const GeneratingSection = ({
  text,
  generatingLabel,
  setButtonState,
}: {
  text: string | string[];
  generatingLabel: string;
  setButtonState: (state: boolean) => void;
}) => {
  const threeInstance = useStore.use.three();
  const { createBase64Song } = useQueryData();
  const resetQuiz = useStore.use.resetQuiz();
  const isGenerating = useStore.use.isGenerating();
  const setIsGenerating = useStore.use.setIsGenerating();
  const setIsReturningUser = useStore.use.setIsReturningUser();
  const step = useStore.use.step();
  const setStep = useStore.use.setStep();
  const nameTo = useStore.use.nameTo();
  const language = useStore.use.language();
  const description = useStore.use.description();
  const emailFrom = useStore.use.emailFrom();
  const storeSetSongs = useStore.use.setSongs();
  const storeSong = useStore.use.song();
  const storeSetSong = useStore.use.setSong();
  const { user } = useUser();

  const [imagesReady, setImagesReady] = useState<boolean>(false);
  const [allIsReady, setAllIsReady] = useState<boolean>(false);
  const [clips, setClips] = useState<Array<string>>([]);
  const [isFetchingClip, setIsFetchingClip] = useState<boolean>(false);
  const [songs, setSongs] = useState<Array<SunoSong>>([]);
  const [error, setError] = useState<string>("");
  const [errorTitle, setErrorTitle] = useState<string>("");
  const [isGenerationError, setIsGenerationError] = useState<boolean>(false);
  const [visibleTextIndex, setVisibleTextIndex] = useState<number>(0);
  const [isCurrentStep, setIsCurrentStep] = useState(false);

  const alertRef = useRef<HTMLHeadingElement>(null);
  const genres = useRef<Array<string>>(GENRES);
  const rootEl = useRef<HTMLDivElement>(null);

  const retryGeneration = () => {
    if (!isGenerationError) {
      setIsGenerationError(false);
      setError("");
      setIsGenerating(true);
    } else {
      setIsGenerationError(false);
      setError("");
      setIsGenerating(false);
      resetQuiz(0);
    }
  };

  const resetGeneration = () => {
    setSongs([]);
    setClips([]);
    setIsFetchingClip(false);
    setImagesReady(false);
  };

  const gotoGeneratedSong = useCallback(async () => {
    await animate(1, 0, {
      duration: 0.5,
      onUpdate: (opacity: number) => {
        rootEl.current?.style.setProperty("opacity", opacity.toString());
      },
    });
    const indexResults = QUIZ_STEPS.findIndex(
      (step: QuizSectionItem) => step.id === QUIZ_STEPS_VARIANTS.results,
    );
    setIsGenerating(false);
    setStep(indexResults);
    resetGeneration();
  }, [setStep]);

  useInterval(
    () => {
      /*
      console.log(
        "INTERVAL",
        "SONGS_" + clips.length,
        "GENERATING_" + isGenerating,
      );//*/

      if (text instanceof Array) {
        setVisibleTextIndex((index) => {
          return index < text.length - 1 ? index + 1 : 0;
        });
      }

      if (clips.length !== MAX_CLIPS) return;

      const CLIPS = clips.toString();
      //console.log(CLIPS);
      fetch(`/api/clips/?ids=${CLIPS}`, {
        method: "GET",
      })
        .then((response) => response.json())
        .then((data) => {
          if (!isGenerating) return;
          let metadataError: { error_type: string } | undefined;
          data.forEach((song: SunoSong) => {
            if (song.status === "error" && !metadataError) {
              metadataError = { error_type: song.metadata.error_type };
            }
          });
          if (metadataError) {
            const isModerationError =
              metadataError?.error_type === "moderation_failure";
            setIsGenerating(false);
            setIsGenerationError(true);
            setErrorTitle(
              isModerationError
                ? getCopy("error:moderation_title")
                : getCopy("error:general_title"),
            );
            setError(
              isModerationError
                ? getCopy("error:moderation_body")
                : getCopy("error:general_body"),
            );
          } else {
            setSongs(data);
          }
        })
        .catch(() => {
          setErrorTitle(getCopy("error:general_title"));
          setError(getCopy("error:general_body"));
        });
    },
    // Delay in milliseconds or null to stop it
    isGenerating ? delayGerationCheck : null,
  );

  useEffect(() => {
    if (songs.length !== MAX_CLIPS || !isGenerating) return;
    const allSongImgs = songs.every((song) => !!song.image_url);
    const allAudioSongs = songs.every((song) => !!song.audio_url);
    /*
    console.log(
      "allSongImgs",
      allSongImgs,
      "allAudioSongs",
      allAudioSongs,
      "songs",
      songs,
    );//*/

    setAllIsReady(allSongImgs && allAudioSongs);

    if (allSongImgs && !imagesReady) {
      storeSetSongs(songs);
      storeSetSong(songs[0]);
      setImagesReady(true);
      const index = QUIZ_STEPS.findIndex(
        (step: QuizSectionItem) => step.id === QUIZ_STEPS_VARIANTS.generating,
      );
      !allAudioSongs && threeInstance?.forceTransition(index + 1, index);
    }
    if (allAudioSongs && imagesReady) {
      setIsGenerating(false);
      storeSetSongs(songs);
      storeSetSong(songs[0]);
      createBase64Song(songs[0]);
      setIsReturningUser(true);
      gotoGeneratedSong();
    }
  }, [
    songs,
    gotoGeneratedSong,
    storeSetSong,
    storeSetSongs,
    createBase64Song,
    setIsGenerating,
    storeSong?.id,
    isGenerating,
    setIsReturningUser,
    imagesReady,
    threeInstance,
    setAllIsReady,
  ]);

  useEffect(() => {
    setIsCurrentStep(
      QUIZ_STEPS[step]?.id === QUIZ_STEPS_VARIANTS["generating"],
    );
  }, [step, setIsCurrentStep]);

  useEffect(() => {
    if (isCurrentStep) {
      rootEl.current?.style.removeProperty("opacity");
      resetGeneration();
      setIsGenerating(true);
    }
  }, [isCurrentStep, setIsGenerating]);

  useEffect(() => {
    if (
      !error &&
      isCurrentStep &&
      clips.length < MAX_CLIPS &&
      !isFetchingClip
    ) {
      // -
      genres.current.sort(() => 0.5 - Math.random());
      const tags = genres.current.slice(0, MAX_CLIPS);
      //console.log("TAGS___", tags);
      // A love song about [NAMETO], we first met in [DESCRIPTION] and this person is special because [LANGUAGE].
      const prompt = getCopy("generating:prompt")
        .replace("[NAMETO]", nameTo)
        .replace("[DESCRIPTION]", description)
        .replace("[LANGUAGE]", language);
      setIsFetchingClip(true);

      if (alertRef) alertRef?.current?.focus();

      fetch(`/api/generate`, {
        method: "POST",
        body: JSON.stringify({
          promotion: SUNO_PROMOTION,
          topic: prompt,
          tags,
          extra: {
            question1: nameTo,
            question2: language,
            question3: description,
            email: user?.primaryEmailAddress?.emailAddress || emailFrom,
          },
        }),
      })
        .then((response) => response.json())
        .then((data) => {
          const analyticsData = {
            song_1: data.clips[0].id || undefined,
            song_2: data.clips[1].id || undefined,
            song_3: data.clips[2].id || undefined,
            gpt_prompt:
              data.clips[0].metadata.gpt_description_prompt || undefined,
          };
          track("click_event:generate_song", analyticsData);

          //* Test preloader --------------------
          setIsFetchingClip(false);
          setClips(data.clips.map((c: SunoSong) => c.id));
          //*/
        })
        .catch((e) => {
          setErrorTitle(getCopy("error:general_title"));
          setError(getCopy("error:general_body"));
          console.log(e);
        });
    }
  }, [
    isCurrentStep,
    clips,
    isFetchingClip,
    setIsFetchingClip,
    description,
    language,
    nameTo,
    error,
    emailFrom,
    user?.primaryEmailAddress?.emailAddress,
  ]);

  return (
    <div ref={rootEl} className={styles.section}>
      {!error && (
        <>
          <Object3dBounds />
          <h4
            tabIndex={isCurrentStep && isGenerating ? 0 : -1}
            role="alert"
            aria-live={isGenerating ? "assertive" : "off"}
            aria-hidden={!isGenerating}
            aria-label={getCopy("generating:title-aria-label")}
          >
            <span aria-hidden>{generatingLabel}</span>
          </h4>
          <h4 className={styles.generatingTextWrapper} aria-hidden>
            {text instanceof Array
              ? text.map((t, tIndex) => {
                  const isVisible = visibleTextIndex === tIndex;
                  return (
                    <span
                      key={`t-${tIndex}`}
                      className={`${styles.generatingText} ${isVisible ? styles.visible : ""}`}
                      aria-hidden
                    >
                      <span aria-hidden>{replaceName(t, nameTo)}</span>
                    </span>
                  );
                })
              : replaceName(text, nameTo)}
          </h4>
        </>
      )}
      {error && (
        <m.div
          className={styles.error}
          initial={{ opacity: 0, y: 50 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{
            type: "spring",
            damping: 16,
            stiffness: 120,
          }}
        >
          <div
            className={styles.msg}
            aria-live="assertive"
            aria-label={`${errorTitle} ${error}`}
            aria-errormessage={`${errorTitle} ${error}`}
            role="alert"
          >
            <Icon className={styles.errorIcon} variant={"error"} aria-hidden />
            <h4 aria-hidden>{errorTitle}</h4>
            <h4 className={styles.errorDesc} aria-hidden>
              {error}
            </h4>
          </div>
          <m.div
            className={styles.btRetry}
            initial={{ opacity: 0, y: 50 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{
              delay: 0.8,
              type: "spring",
              damping: 16,
              stiffness: 120,
            }}
          >
            <CallToAction variant="buttonPill" onClick={retryGeneration}>
              {getCopy("error-prompt-cta")}
            </CallToAction>
          </m.div>
        </m.div>
      )}

      <div
        ref={alertRef}
        className="sr-only"
        role="alert"
        aria-live={isGenerating ? "assertive" : "off"}
        aria-hidden={!isGenerating}
        aria-label={getCopy("generating:title-aria-label")}
      ></div>

      <div
        className="sr-only"
        role="alert"
        aria-live={allIsReady ? "assertive" : "off"}
        aria-hidden={!allIsReady}
        aria-label={getCopy("generating:ready")}
      ></div>
    </div>
  );
};

export default GeneratingSection;
