'use client';

import { useAuth } from '@clerk/nextjs';
import * as Ably from 'ably';
import { observer } from 'mobx-react-lite';
import { useSearchParams } from 'next/navigation';
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import storageAvailable from 'storage-available';

import { useStores } from '@/app/(root)/AppProviders';
import { useBreakpointMd } from '@/hooks/useBreakpoint';
import { useCheckoutSuccessModals } from '@/hooks/useCheckoutSuccessModals';
import { useContextSelector } from '@/hooks/useContextSelector';
import usePageViewLog from '@/hooks/usePageViewLog';
import { useRemixUrlParams } from '@/hooks/useRemixUrlParams';
import { useApiClient } from '@/lib/apiClient';
import { ContextType } from '@/logging/contextTypes';
import logWebUserEvent, {
  TransactionLogger,
  createTransactionLogger,
} from '@/logging/logWebUserEvent';
import { TagSuggestionContexts } from '@/state/createV2Store';
import { OVERRIDE_MODEL_POST_SUBSCRIPTION_TTL } from '@/utils/constants';
import {
  clearMusicianCreateState,
  loadMusicianCreateState,
} from '@/utils/musicianCreateStorage';
import { isProjectsFeatureEnabled } from '@/utils/session';
import { getDefaultModel } from '@/utils/utils';

import { useOrpheusExperimentGroup } from '../chat/hooks/useOrpheusExperimentGroup';
import { genreAdjectives, getTopics, top1KGenres } from './createV2/genres';
import CreateFormContext from './v2/CreateFormContext';
import CreateV2Client from './v2/CreateV2Client';
import { CreateFormState, CreateModes, LyricsInputModes } from './v2/types';

type Props = {
  ably?: Ably.RealtimeClient;
};

const CreateClient = observer<Props>(({ ably }: Props) => {
  const {
    clips,
    createV2,
    genForm,
    playbar,
    session,
    edit,
    queue: queueStore,
  } = useStores();

  const [isLoading, setIsLoading] = useState(true);
  const [, setIsProjectEnabled] = useState(false);
  const setState = useContextSelector(
    CreateFormContext,
    (context) => context.setState
  );
  const generate = useContextSelector(
    CreateFormContext,
    (context) => context.generate
  );

  const songsRef = useRef<HTMLDivElement>(null);
  const isMobile = !useBreakpointMd();
  const lyricsRef = useRef<HTMLTextAreaElement>(null);
  const debouncedScrollRef = useRef<NodeJS.Timeout | null>(null);

  const isCreateV1p5Enabled = true;
  const { isControlGroup: isChatExpControlGroup, isLoading: isChatExpLoading } =
    useOrpheusExperimentGroup();
  const isChatExpEnabled = !isChatExpControlGroup;

  const rightPanelRef = useRef<any>(undefined);
  const apiClient = useApiClient();

  const mode = useContextSelector(
    CreateFormContext,
    (context) => context.state.global.mode
  );

  const simplePrompt = useContextSelector(
    CreateFormContext,
    (context) => context.state[CreateModes.SIMPLE].prompt
  );

  const customStyles = useContextSelector(
    CreateFormContext,
    (context) => context.state[CreateModes.CUSTOM].styles
  );

  const customLyrics = useContextSelector(
    CreateFormContext,
    (context) => context.state[CreateModes.CUSTOM].lyrics
  );

  const customLyricsPrompt = useContextSelector(
    CreateFormContext,
    (context) => context.state[CreateModes.CUSTOM].lyricsPrompt
  );

  // Track if we should auto-generate after state update
  const [shouldAutoGenerate, setShouldAutoGenerate] = useState(false);
  const autoGenerateTransactionLoggerRef = useRef<TransactionLogger | null>(
    null
  );

  // Trigger generation when flag is set AND state values have actually updated
  useEffect(() => {
    if (!shouldAutoGenerate) return;

    const isSimpleModeReady =
      mode === CreateModes.SIMPLE && simplePrompt && simplePrompt.length > 0;

    const isCustomModeReady =
      mode === CreateModes.CUSTOM &&
      (customStyles.length > 0 ||
        customLyrics.length > 0 ||
        customLyricsPrompt.length > 0);

    if (isSimpleModeReady || isCustomModeReady) {
      setShouldAutoGenerate(false);
      const transactionLogger = autoGenerateTransactionLoggerRef.current;
      autoGenerateTransactionLoggerRef.current = null;

      if (!transactionLogger) return;
      generate(transactionLogger);
      localStorage.removeItem('prompt');
      localStorage.removeItem('prompt_saved_at');
      localStorage.removeItem('prompt_source');
      clearMusicianCreateState();
    }
  }, [
    shouldAutoGenerate,
    mode,
    simplePrompt,
    customStyles,
    customLyrics,
    customLyricsPrompt,
    generate,
    clearMusicianCreateState,
  ]);

  useEffect(() => {
    const getPromptsData = async () => {
      const { data } = await apiClient.GET('/api/clips/clip_prompts/');
      createV2.setPromptLibrary(data?.prompts || []);
    };
    const getTagsData = async () => {
      createV2.setSuggestedTags({
        [TagSuggestionContexts.SimpleTags]: await createV2.getRandomGenres(),
        [TagSuggestionContexts.AdvancedTags]: await createV2.getRandomGenres(),
        [TagSuggestionContexts.AdvancedExcludeTags]:
          await createV2.getRandomGenres(),
      });
    };

    createV2.genreAdjectives = genreAdjectives;
    createV2.top1KGenres = top1KGenres;
    createV2.lyricsTopics = getTopics();
    createV2.promptPlaceholder = createV2.getPromptPlaceholder();
    if (Object.values(createV2.suggestedTags).flat().length === 0) {
      getTagsData();
    }
    getPromptsData();
  }, []);

  useEffect(() => {
    if (genForm.continueClipId && genForm.coverClipId) {
      createV2.setCoverExtendMode('cover_extend');
    } else if (genForm.continueClipId) {
      createV2.setCoverExtendMode('extend');
    } else if (genForm.coverClipId) {
      createV2.setCoverExtendMode('cover');
    } else {
      createV2.setCoverExtendMode(null);
    }
  }, [genForm.continueClipId, genForm.coverClipId]);

  const scrollToBottom = () => {
    requestAnimationFrame(() => {
      if (songsRef.current) {
        songsRef.current.scrollTo({
          top: songsRef.current.scrollHeight,
          behavior: 'smooth',
        });
      }
    });
  };

  const debouncedScrollToBottom = () => {
    if (debouncedScrollRef.current) {
      clearTimeout(debouncedScrollRef.current);
    }
    debouncedScrollRef.current = setTimeout(scrollToBottom, 200);
  };

  const scrollToTop = () => {
    requestAnimationFrame(() => {
      if (songsRef.current) {
        songsRef.current.scrollTo({
          top: 0,
          behavior: 'smooth',
        });
      }
    });
  };

  useEffect(() => {
    const projectsEnabled = isProjectsFeatureEnabled(session);
    const fetchData = async () => {
      setIsLoading(true);
      await clips.fetchRecentClips(ably);
      setIsLoading(false);
      const clipQueue = clips.clipIds.map(
        (clipId: string) => clips.clipById[clipId]
      );
      if (!playbar.clip) {
        queueStore.setPlayContext({
          clips: clipQueue,
          contextType: ContextType.Create,
          contextId: 'create',
        });
      }
      await session.getUserConfig();
    };
    if (session.userId && !projectsEnabled) {
      fetchData();
    }
    if (session.flags?.configs) {
      genForm.loadAdvancedParams();
    }
  }, [session.userId, session.flags]);

  useEffect(() => {
    genForm.lyricsRef = lyricsRef;
  }, []);

  useEffect(() => {
    if (isCreateV1p5Enabled && !genForm.continueAtSeconds) {
      genForm.setContinueAtSeconds(
        clips.clipById[genForm.continueClipId || '']?.metadata?.duration || 0
      );
    }
  }, [genForm.continueClipId, isCreateV1p5Enabled]);

  useEffect(() => {
    setIsProjectEnabled(!!isProjectsFeatureEnabled(session));
  }, [session.flags]);

  useEffect(() => {
    if (rightPanelRef.current && storageAvailable('localStorage')) {
      const rightPanelWidth = localStorage.getItem('right-panel-width');
      rightPanelRef.current.style.width = `${rightPanelWidth}px`;
    }
  }, [rightPanelRef.current, session.flags]);

  useLayoutEffect(() => {
    const shouldScrollToTop = isMobile;
    if (
      songsRef.current &&
      edit.pendingScrollPositionChange &&
      edit.pendingScrollPositionChange.page === 'create'
    ) {
      songsRef.current.scrollTo({
        top: edit.pendingScrollPositionChange.scrollPosition,
      });
    } else if (shouldScrollToTop && !edit.pendingScrollPositionChange) {
      scrollToTop();
    } else if (!isLoading && !edit.pendingScrollPositionChange) {
      debouncedScrollToBottom();
    } else if (edit.pendingScrollPositionChange) {
      requestAnimationFrame(() => {
        edit.setPendingScrollPositionChange(null);
      });
    }
  }, [
    songsRef.current,
    edit.pendingScrollPositionChange,
    clips.clips.length,
    clips.clipIds,
    clips.loadingRequests.length,
    isLoading,
    isMobile,
  ]);

  useEffect(() => {
    return () => {
      if (clips.replyChannel) {
        clips.replyChannel?.unsubscribe();
        clips.replyChannel
          ?.detach()
          .then(() => {
            if (clips.replyChannel?.name) {
              clips.ablyClient?.channels.release(clips.replyChannel?.name);
              clips.replyChannel = null;
            }
          })
          .catch(console.error);
      }
    };
  }, []);

  useEffect(() => {
    if (storageAvailable('localStorage') && session?.models?.length > 0) {
      if (isChatExpEnabled || isChatExpLoading) {
        return;
      }

      const TEN_MINUTES_MS = 600000;
      const musicianCreateState = loadMusicianCreateState(TEN_MINUTES_MS);
      const transactionLogger = createTransactionLogger();

      if (musicianCreateState) {
        localStorage.removeItem('prompt');
        localStorage.removeItem('prompt_saved_at');
        localStorage.removeItem('prompt_source');

        setState((state: CreateFormState) => ({
          ...state,
          global: {
            ...state.global,
            mode: CreateModes.CUSTOM,
          },
          [CreateModes.CUSTOM]: {
            ...state[CreateModes.CUSTOM],
            lyricsMode:
              musicianCreateState.activeLyricsMode as LyricsInputModes,
            lyricsPrompt: musicianCreateState.activeLyrics,
            lyrics: ['auto', 'instrumental'].includes(
              musicianCreateState.activeLyricsMode
            )
              ? ''
              : musicianCreateState.activeLyrics,
            styles: musicianCreateState.activeStyles,
          },
        }));
        clearMusicianCreateState();
        if (musicianCreateState.shouldGenerate) {
          logWebUserEvent({
            actionName: 'GenerateStartedFromAdvancedCreatePage',
            context: {
              musicianCreateState: musicianCreateState,
            },
          });
          // Set flag to trigger auto-generation after state updates
          autoGenerateTransactionLoggerRef.current = transactionLogger;
          setShouldAutoGenerate(true);
        }
        return;
      }

      const prompt = localStorage.getItem('prompt');
      const promptSavedAt = localStorage.getItem('prompt_saved_at');
      const promptSource = localStorage.getItem('prompt_source');
      if (
        prompt &&
        Date.now() - (parseInt(promptSavedAt || '') || 0) < TEN_MINUTES_MS &&
        !genForm.hasRunSavedPrompt
      ) {
        genForm.hasRunSavedPrompt = true;

        setState((state: CreateFormState) => ({
          ...state,
          global: {
            ...state.global,
            mode: CreateModes.SIMPLE,
          },
          [CreateModes.SIMPLE]: {
            ...state[CreateModes.SIMPLE],
            prompt: prompt,
          },
        }));

        transactionLogger.logWebUserEvent({
          actionName: 'GenerateStartedAfterLogin',
        });

        if (promptSource === 'quickbox') {
          logWebUserEvent({
            actionName: 'GenerateStartedFromLandingPage',
            context: {
              prompt: prompt || '',
            },
          });
        } else if (promptSource === 'songpage') {
          logWebUserEvent({
            actionName: 'GenerateStartedFromSongPage',
            context: {
              prompt: prompt || '',
            },
          });
        }

        // Set flag to trigger auto-generation after state updates (for all sources)
        autoGenerateTransactionLoggerRef.current = transactionLogger;
        setShouldAutoGenerate(true);
      }
    }
  }, [mode, session, session.models, isChatExpEnabled, isChatExpLoading]);

  usePageViewLog({ actionName: 'PageViewed', componentContext: 'create' });

  const searchParams = useSearchParams();

  useRemixUrlParams();
  useCheckoutSuccessModals();

  // Check if user just subscribed. If so, set the model to latest model.
  useEffect(() => {
    const checkoutSessionId = searchParams.get('checkout_session_id');
    // Check if there's a model override from checkout in localStorage
    const overrideModelPostSubExpiry =
      typeof window !== 'undefined' && storageAvailable('localStorage')
        ? localStorage.getItem('override_model_post_sub')
        : null;

    // We will override the model of create if:
    // 1. There is a checkout session id in url params
    // 2. AND the local storage key doesn't exist or is expired
    const shouldOverrideModel =
      checkoutSessionId &&
      (!overrideModelPostSubExpiry ||
        (overrideModelPostSubExpiry &&
          Date.now() - parseInt(overrideModelPostSubExpiry) >
            OVERRIDE_MODEL_POST_SUBSCRIPTION_TTL));

    if (shouldOverrideModel) {
      // User just subscribed, set model to latest model
      genForm.setMvUserPreference(getDefaultModel(session.getViewableModels()));
      // Set the local storage key so we don't override again
      localStorage.setItem('override_model_post_sub', Date.now().toString());
    }
  }, [genForm, searchParams]);

  // Set default model on first web visit
  useEffect(() => {
    if (session.sessionIsLoaded && session.userId) {
      // If user has existing preferences, don't override them
      if (genForm.hasExistingLocalStoragePreferences()) {
        return;
      }

      // Use the same default model logic as CreateFormContext
      const defaultModelKey = getDefaultModel(session.billingModels);
      genForm.setMvUserPreference(defaultModelKey);
    }
  }, [session.sessionIsLoaded, session.userId, session.billingModels, genForm]);

  return <CreateV2Client />;
});

const CreateClientWrapper = observer<any>(() => {
  const auth = useAuth();
  const { session } = useStores();
  const ablyEnabled = session.flags?.['websockets'];
  const ablyClient = useMemo(
    () =>
      ablyEnabled
        ? new Ably.Realtime({
            authUrl: '/api/ably-auth',
            clientId: auth.userId || null,
          } as Ably.ClientOptions)
        : undefined,
    [auth.userId, ablyEnabled]
  );
  return <CreateClient ably={ablyClient} />;
});

export default CreateClientWrapper;
