'use client';

/* eslint jsx-a11y/click-events-have-key-events: warn */

/* eslint jsx-a11y/label-has-associated-control: warn */

/* eslint jsx-a11y/no-static-element-interactions: warn */
import { useStatsigClient } from '@statsig/react-bindings';
import { usePathname, useRouter } from 'next/navigation';
import React, { useCallback, useEffect, useRef, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import SwitchButton from '@/components/button/SwitchButton';
import { isValidClipForPersona } from '@/components/clipBrowser/ClipMenuItems';
import ImageUploader from '@/components/image/ImageUploader';
import { CaretDownIcon, PauseIcon, PlayIcon, SearchIcon } from '@/icons';
import { ContextType } from '@/logging/contextTypes';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { Clip } from '@/state/clipStore';
import { CreatePersonaParams } from '@/state/personaStore';
import { PlanFeature } from '@/state/sessionStore';
import { getClipTitle } from '@/utils/clip';
import {
  MAX_DESCRIPTION_LENGTH,
  MAX_SEARCH_TERM_LEN,
  MODAL_Z_INDEX,
  SEARCH_DEBOUNCE_DELAY,
  SUPER_MODAL_Z_INDEX,
} from '@/utils/constants';
import { isFeatureEnabledForPlan } from '@/utils/session';

import Button, { ButtonSize, ButtonVariant } from '../button/Button';
import ImageWithFallback from '../image/ImageWithFallback';
import SpinnerSVG from '../svg/SpinnerSVG';
import Modal from './Modal';

interface CreatePersonaModalProps {
  isOpen: boolean;
  onClose: () => void;
  initialClip?: Clip;
}

const CreatePersonaModal: React.FC<CreatePersonaModalProps> = ({
  isOpen,
  onClose,
  initialClip,
}) => {
  const {
    library,
    clips,
    persona,
    session,
    playbar,
    queue,
    search,
    genForm,
    createV2,
  } = useStores();
  const statsigClient = useStatsigClient();
  const defaultPublic = !statsigClient.checkGate('privacy-mode');
  const pathname = usePathname();
  const router = useRouter();
  const [selectedClipId, setSelectedClipId] = useState<string | null>(
    initialClip?.id || null
  );
  const [name, setName] = useState<string>('');
  const [description, setDescription] = useState<string>('');
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [showPlayIcon, setShowPlayIcon] = useState<string | null>(null);
  const [imageDataURL, setImageDataURL] = useState<string | null>(null);
  const [initialImageDataURL, setInitialImageDataURL] = useState<string | null>(
    null
  );
  const [isLoading, setIsLoading] = useState(false);
  const [isLoadingClips, setIsLoadingClips] = useState(false);
  const [libraryClipIds, setLibraryClipIds] = useState<string[]>([]);

  const dropdownRef = useRef<HTMLDivElement>(null);
  const [isGenerating, setIsGenerating] = useState(false);
  const [editedImagePrompt, setEditedImagePrompt] = useState<string>('');
  const searchInputRef = useRef<HTMLInputElement>(null);
  const [playingClipId, setPlayingClipId] = useState<string | null>(null);
  const searchTimeout = useRef<any>(null);
  const [isPublic, setIsPublic] = useState(defaultPublic);

  const isStaff = session?.isStaff || false;

  const loadClips = async () => {
    if (isLoadingClips) return;

    setIsLoadingClips(true);
    try {
      await library.loadClips();
      setLibraryClipIds([...library.clipIds]);
    } catch (error) {
    } finally {
      setIsLoadingClips(false);
    }
  };

  const debouncedLibrarySearch = (searchTerm: string) => {
    if (searchTimeout.current !== null) {
      clearTimeout(searchTimeout.current);
      searchTimeout.current = null;
    }
    searchTimeout.current = setTimeout(async () => {
      setIsLoadingClips(true);
      const term = searchTerm.trim().substring(0, MAX_SEARCH_TERM_LEN);
      await library.updateFiltersSync({
        ...library.filters.clips,
        query: term,
        page: 0,
      });
      setLibraryClipIds([...library.clipIds]);
      requestAnimationFrame(() => {
        setIsLoadingClips(false);
      });
      search.librarySearchTerm = term;
    }, SEARCH_DEBOUNCE_DELAY);
  };

  useEffect(() => {
    loadClips();
  }, []);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        dropdownRef.current &&
        !dropdownRef.current.contains(event.target as Node)
      ) {
        setIsDropdownOpen(false);
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [dropdownRef.current]);

  useEffect(() => {
    if (isDropdownOpen && searchInputRef.current) {
      searchInputRef.current.focus();
    }
  }, [isDropdownOpen]);

  const handleDropdownToggle = () => {
    setIsDropdownOpen(!isDropdownOpen);
  };

  const handleClipSelect = (clipId: string) => {
    setSelectedClipId(clipId);
    setIsDropdownOpen(false);
    setErrorMessage(null);

    logWebUserEvent({
      actionName: 'PersonaRootClipSelectClicked',
      context: {
        clipId: clipId,
      },
    });
  };

  const clipInfo = (clipId: string) => {
    const clip = clips.clipById[clipId];
    const title = getClipTitle(clip);
    return {
      title: title?.length > 100 ? `${title.substring(0, 100)}...` : title,
      playCount: clip?.play_count,
      imageUrl: clip?.image_url,
      metadata: clip?.metadata,
    };
  };

  const handlePlayClip = (clipId: string) => {
    if (playingClipId === clipId && playbar.isPlaying) {
      playbar.togglePlay();
      setPlayingClipId(null);
    } else {
      setPlayingClipId(clipId);
      queue.setPlayContext({
        clips: library.currentClips || [],
        currentIndex: (library.currentClips || []).findIndex(
          (c: Clip) => c.id === clipId
        ),
        contextType: ContextType.PersonaPreview,
        contextId: 'persona_preview_list',
      });
      playbar.playClip(clips.clipById[clipId]);
    }
  };

  useEffect(() => {
    if (playbar.clip && playbar.isPlaying) {
      setPlayingClipId(playbar.clip.id);
    } else {
      setPlayingClipId(null);
    }
  }, [playbar.clip, playbar.isPlaying]);

  const savePersona = async () => {
    if (!selectedClipId) {
      setErrorMessage('You need to select a base clip!');
      return;
    }

    const isValid = isValidClipForPersona({
      clip: clips.clipById[selectedClipId],
      session,
    });
    if (!isValid) {
      setErrorMessage('A Persona cannot be created from this clip.');
      return;
    }

    setIsLoading(true);
    const params: CreatePersonaParams = {
      user_id: session.userId || '',
      root_clip_id: selectedClipId,
      name: name,
      description: description,
      image_s3_id: imageDataURL,
      clips: [selectedClipId],
      is_public: isPublic,
    };

    try {
      const result = await persona.createPersona(params);
      if (result) {
        clips.clipById[selectedClipId].persona = {
          id: result.id,
          is_public: isPublic,
        };

        createV2.resetPlaylistConditioning();
        createV2.resetPainting();

        genForm.setPersona(result.id);
        genForm.setPersonaClipId(result.root_clip_id);

        createV2.setTagInput(result.clip.metadata?.tags || '');
        const rootClip = clips.clipById[result.root_clip_id];
        const rootClipStyles = rootClip?.metadata?.tags || '';
        genForm.setStyle(rootClipStyles);
        genForm.setTask('artist_consistency');
        genForm.setContinueClip(null);

        resetModalState();
        onClose();

        logWebUserEvent({
          actionName: 'PersonaSaveButtonClicked',
          context: {
            createdPersona: result?.id,
            isMultiPersona: session.flags?.['multi-root-persona'] || false,
            totalSelectedPersonas: createV2.activePersonas.length,
          },
        });
        if (pathname === '/me/personas') {
          router.push(`/persona/${result.id}`);
        }
      }
    } catch (error) {
    } finally {
      setIsLoading(false);
    }
  };

  const handleSave = async () => {
    if (!selectedClipId) {
      setErrorMessage('You need to select a base clip!');
      return;
    }

    if (persona.personas.length === 0 && isPublic) {
      setShowFirstTimeNotice(true);
      return;
    }

    await savePersona();
  };

  const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };

  const handleDescriptionChange = (
    e: React.ChangeEvent<HTMLTextAreaElement>
  ) => {
    if (e.target.value.length <= MAX_DESCRIPTION_LENGTH) {
      setDescription(e.target.value);
    }
  };

  const resetModalState = useCallback(() => {
    setSelectedClipId(initialClip?.id || null);
    setName('');
    setDescription('');
    setErrorMessage(null);
    setImageDataURL(null);
    setIsDropdownOpen(false);
  }, [initialClip]);

  const handleClose = useCallback(() => {
    resetModalState();
    onClose();

    logWebUserEvent({
      actionName: 'PersonaModalCloseClicked',
    });
  }, [onClose, resetModalState]);

  const [showFirstTimeNotice, setShowFirstTimeNotice] = useState(false);

  const handleFirstTimeNoticeConfirm = async () => {
    setShowFirstTimeNotice(false);

    logWebUserEvent({
      actionName: 'PersonaPublicConfirmClicked',
    });

    await savePersona();
  };

  useEffect(() => {
    if (isOpen) {
      setSelectedClipId(initialClip?.id || null);
    } else {
      resetModalState();
      search.librarySearchTerm = '';
      library.updateFilters({
        ...library.filters.clips,
        query: '',
        page: 0,
      });
    }
  }, [isOpen, initialClip]);

  if (!isOpen) return null;

  return (
    <Modal
      title='Create a Persona'
      onClose={handleClose}
      wrapperClasses='overflow-y-auto'
    >
      <div className='flex h-full flex-col'>
        <div className='grow overflow-y-auto'>
          <div className='mb-4'>
            <div className='flex items-center justify-between'>
              <label className='mb-1 block p-1'>Based on</label>
              {errorMessage && (
                <span className='text-sm text-accent-pink'>{errorMessage}</span>
              )}
            </div>
            <div className='relative' ref={dropdownRef}>
              <div
                className='flex cursor-pointer items-center justify-between rounded-md border border-border-secondary bg-background-primary p-2 hover:bg-background-tertiary'
                onClick={handleDropdownToggle}
              >
                {selectedClipId && (
                  <ImageWithFallback
                    src={clipInfo(selectedClipId).imageUrl}
                    alt={clipInfo(selectedClipId).title}
                    className='mr-2 h-[41px] w-[30px] rounded-md object-cover'
                  />
                )}
                <div className='w-full border-none pl-2 text-foreground-primary'>
                  {selectedClipId
                    ? clipInfo(selectedClipId).title
                    : 'Select a song...'}
                </div>
                <button>
                  <CaretDownIcon className='z-10000 ml-2 h-4 w-4 fill-foreground-primary' />
                </button>
              </div>
              {isDropdownOpen && (
                <div
                  className='absolute mt-4 w-full overflow-y-auto rounded-md border border-border-secondary bg-background-primary px-4 pt-3 pb-4 text-foreground-primary shadow-xl'
                  style={{
                    zIndex: MODAL_Z_INDEX,
                    boxShadow:
                      '0 4px 6px rgba(0, 0, 0, 0.1), 0 10px 15px rgba(0, 0, 0, 0.2)',
                  }}
                >
                  <div
                    className='sticky top-0 px-1 pt-2 pb-1'
                    style={{ zIndex: SUPER_MODAL_Z_INDEX }}
                  >
                    <SearchIcon className='absolute top-[17px] left-4 h-4 w-4 transform fill-foreground-secondary' />
                    <input
                      ref={searchInputRef}
                      type='text'
                      placeholder='Search for a song in your library...'
                      className='placeholder-opacity-70 mb-2 w-full rounded-full border border-border-secondary bg-background-secondary py-1 pr-4 pl-10 text-foreground-primary placeholder-foreground-secondary focus:outline-none'
                      onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                        debouncedLibrarySearch(e.target.value);
                      }}
                    />
                  </div>
                  {isLoadingClips ? (
                    <div className='flex items-center justify-center py-4'>
                      <SpinnerSVG />
                    </div>
                  ) : libraryClipIds.length === 0 ? (
                    <div className='py-4 text-center font-serif'>
                      No clips found
                    </div>
                  ) : (
                    library.clipIds.map((clipId: string) => {
                      const { title, playCount, imageUrl, metadata } =
                        clipInfo(clipId);
                      const isPlaying =
                        playingClipId === clipId && playbar.isPlaying;
                      const isUpload = metadata?.has_vocal;
                      const isSelectable =
                        !isUpload ||
                        isStaff ||
                        session.flags?.['personas-audio-upload'];

                      return (
                        <div
                          key={clipId}
                          className={`flex items-center p-2 ${
                            isSelectable
                              ? 'cursor-pointer hover:rounded-lg hover:bg-secondary/20'
                              : 'cursor-not-allowed opacity-50'
                          }`}
                          onClick={() =>
                            isSelectable && handleClipSelect(clipId)
                          }
                          onMouseEnter={() =>
                            isSelectable && setShowPlayIcon(clipId)
                          }
                          onMouseLeave={() =>
                            isSelectable && setShowPlayIcon(null)
                          }
                        >
                          <div
                            className={`relative mr-2 h-[41px] w-[30px] shrink-0 rounded-md ${
                              isSelectable
                                ? 'cursor-pointer'
                                : 'cursor-not-allowed'
                            }`}
                            onMouseEnter={() =>
                              isSelectable && setShowPlayIcon(clipId)
                            }
                            onMouseLeave={() =>
                              isSelectable && setShowPlayIcon(null)
                            }
                          >
                            <ImageWithFallback
                              src={imageUrl}
                              alt={title}
                              className={`h-full w-full rounded-md object-cover ${isSelectable ? '' : 'opacity-50'}`}
                            />
                            {isSelectable && (
                              <div
                                className={`bg-opacity-50 absolute inset-0 flex items-center justify-center rounded-md bg-black transition-opacity ${
                                  showPlayIcon === clipId ||
                                  playingClipId === clipId
                                    ? 'opacity-100'
                                    : 'opacity-0'
                                }`}
                                onClick={(e) => {
                                  e.stopPropagation();
                                  handlePlayClip(clipId);
                                }}
                              >
                                {isPlaying ? (
                                  <PauseIcon className='h-4 w-4 fill-foreground-primary' />
                                ) : (
                                  <PlayIcon className='h-4 w-4 fill-foreground-primary' />
                                )}
                              </div>
                            )}
                          </div>
                          <div
                            className={`flex-1 pl-2 font-sans text-sm font-medium ${
                              isSelectable ? '' : 'flex flex-col text-gray-500'
                            }`}
                          >
                            {title}
                            {isUpload && !isStaff && (
                              <div
                                className={`font-sans text-xs font-normal text-gray-500`}
                              >
                                (Personas from audio uploads are disabled at
                                this time.)
                              </div>
                            )}
                          </div>

                          <div
                            className={`${
                              isSelectable
                                ? 'cursor-pointer bg-accent-brand text-foreground-primary-on-dark'
                                : 'cursor-not-allowed bg-gray-400 text-foreground-primary-on-dark'
                            } rounded-full px-2 py-1 font-mono text-[10px]`}
                            onClick={(e) => {
                              if (isSelectable) {
                                e.stopPropagation();
                                handlePlayClip(clipId);
                              }
                            }}
                          >
                            {playCount === 1 ? '1 PLAY' : `${playCount} PLAYS`}
                          </div>
                        </div>
                      );
                    })
                  )}
                </div>
              )}
            </div>
          </div>
          <div className='mb-4'>
            <label className='block p-1'>Name</label>
            <input
              type='text'
              className='placeholder-opacity-70 w-full rounded-lg border border-border-secondary bg-background-primary px-4 py-2 text-foreground-primary placeholder-foreground-secondary focus:shadow-[0_0_0_0.5px_accent-brand] focus:outline-none'
              value={name}
              placeholder='This is shown on songs created with this Persona'
              onChange={handleNameChange}
            />
          </div>
          <div className='mb-4'>
            <label className='mb-1 block p-1'>Avatar</label>

            <ImageUploader
              initialImageURL={initialImageDataURL || undefined}
              onImageChanged={(imageData: string | null) => {
                setImageDataURL(imageData);
              }}
            />

            {isFeatureEnabledForPlan(session, PlanFeature.Persona) && (
              <>
                <div className='mt-2 flex gap-2 pt-1'>
                  <input
                    type='text'
                    className='focus:border-0.5 placeholder-opacity-70 w-full rounded-lg border border-border-secondary bg-background-primary px-4 py-2 text-sm text-foreground-primary placeholder-foreground-secondary focus:border focus:border-accent-brand focus:shadow-[0_0_0_0.5px_accent-brand] focus:outline-none'
                    value={editedImagePrompt}
                    maxLength={200}
                    placeholder='Prompt Suno to create an image...'
                    onChange={(event) =>
                      setEditedImagePrompt(event.target.value)
                    }
                  />
                  <Button
                    variant={ButtonVariant.Primary}
                    size={ButtonSize.Small}
                    onClick={async () => {
                      setIsGenerating(true);

                      const { data } = await library.apiClient.POST(
                        '/api/gen/prompt_image/',
                        {
                          body: {
                            prompt: editedImagePrompt,
                          },
                        }
                      );

                      setIsGenerating(false);

                      if (data) {
                        setInitialImageDataURL((data as any).image_url);
                      }
                    }}
                    disabled={isGenerating || editedImagePrompt === ''}
                    icon={
                      isGenerating && <SpinnerSVG className='text-current' />
                    }
                  >
                    Create
                  </Button>
                </div>
              </>
            )}
          </div>
          <div className='mb-4'>
            <label className='mb-2 block'>Description</label>
            <textarea
              className='focus:border-0.5 placeholder-opacity-70 w-full rounded-lg border border-border-secondary bg-background-primary px-4 py-2 text-foreground-primary placeholder-foreground-secondary focus:border focus:border-accent-brand focus:shadow-[0_0_0_0.5px_accent-brand] focus:outline-none'
              rows={4}
              value={description}
              placeholder="Describe this Persona's sound or bio"
              onChange={handleDescriptionChange}
              maxLength={MAX_DESCRIPTION_LENGTH}
            />
            <div className='text-right text-sm text-foreground-secondary'>
              {description.length}/{MAX_DESCRIPTION_LENGTH}
            </div>
          </div>
        </div>

        <div className='sticky bottom-0 border-t border-border-secondary bg-background-secondary pt-4'>
          <div className='flex items-center justify-between'>
            <div className='flex items-center'>
              <SwitchButton
                buttonText='Public'
                onChange={(newValue) => setIsPublic(newValue)}
                checked={isPublic}
                tooltip='Personas copy the voice and styles of a song to new songs you create. This feature is only available to Pro and Premier users. Subscribe to gain access.'
              />
            </div>
            <div className='flex gap-2'>
              <Button size={ButtonSize.Small} onClick={handleClose}>
                Cancel
              </Button>
              <Button
                variant={ButtonVariant.Primary}
                size={ButtonSize.Small}
                icon={isLoading && <SpinnerSVG className='text-current' />}
                onClick={handleSave}
              >
                Save
              </Button>
            </div>
          </div>
        </div>
      </div>
      {showFirstTimeNotice && (
        <Modal
          title='Make Persona Public?'
          onClose={() => setShowFirstTimeNotice(false)}
          wrapperClasses='overflow-y-auto'
          titleClassName='text-xl'
        >
          <div className='text-foreground-primary'>
            Creation is better together! Making a Persona public allows other
            Pro & Premier users to create new songs using your Persona. Those
            songs will link to your profile and show up on your Persona&apos;s
            page so you get credit for your creations, and more listeners in the
            process.
          </div>
          <div className='mt-4 flex items-center justify-end gap-2 px-4 pb-4'>
            <Button onClick={() => setShowFirstTimeNotice(false)}>
              Cancel
            </Button>
            <Button
              variant={ButtonVariant.Primary}
              onClick={handleFirstTimeNoticeConfirm}
            >
              Confirm
            </Button>
          </div>
        </Modal>
      )}
    </Modal>
  );
};

export default CreatePersonaModal;
