import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { noop } from 'lodash-es';
import React, {
  Dispatch,
  SetStateAction,
  memo,
  useCallback,
  useEffect,
  useMemo,
  useRef,
} from 'react';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import { useSynchronizingStateHistory } from '@/components/edit2025/useStateHistory';
import SpinnerSVG from '@/components/svg/SpinnerSVG';
import { useContextSelector } from '@/hooks/useContextSelector';
import {
  BookmarkIcon,
  BookmarkOutlineIcon,
  DiscardIcon,
  EditUndoIcon,
  LibraryIcon,
  PlusIcon,
  WandIcon,
} from '@/icons';
import logWebUserEvent from '@/logging/logWebUserEvent';

import CreateFormContext from '../../v2/CreateFormContext';
import { CreateFormModals, CreateModes } from '../../v2/types';
import { useUpsampleTags } from '../../v2/useUpsampleTags';
import { getTagsMaxLengthForModel } from '../utils';
import CreateCard from './CreateCard';
import CreateTextarea from './CreateTextarea';
import StudioCreateContext from './StudioCreateContext';
import { CollapsibleCardTitle } from './common';
import { CreateTheme, bigSpace, smallSpace } from './themes';
import useResizer, {
  RESIZABLE_CONTAINER_CLASS_NAME,
  ResizerHandle,
} from './useResizer';
import useSuggestedStyles from './useSuggestedStyles';

interface StylesCardProps {
  styles: string;
  setStyles: Dispatch<SetStateAction<string>>;
  clearStyles?: () => void;

  expanded: boolean;
  setExpanded: Dispatch<SetStateAction<boolean>>;

  suggestedStyles: string[];
  suggestedStylesLoading?: boolean;
  onPickSuggestedStyle: (style: string) => void;

  stylesInputHeight?: number;
  setStylesInputHeight?: Dispatch<SetStateAction<number>>;

  onSavePrompt: () => void;
  canSavePrompt: boolean;
  hasSavedPrompts: boolean;

  onUndoSetStyles: () => void;
  canUndoSetStyles: boolean;

  onUpsampleStyles: () => void;
  canUpsampleStyles: boolean;
  isUpsamplingStyles: boolean;

  setActiveModal: Dispatch<SetStateAction<CreateFormModals | null>>;

  model: string;

  headerContentHidden?: boolean;
  nested?: boolean;
  fixed?: boolean;
  headerContentItemsOverride?: StylesCardHeaderItems[];
  styleOverrides?: React.CSSProperties;
}

const StylesContent = styled.div`
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 0;
  overflow: hidden;
`;

const TextareaWrapper = styled.div`
  flex-grow: 1;
  padding: 0 16px;
  position: relative;
`;

const CharacterCounter = styled.div`
  position: absolute;
  bottom: 8px;
  right: 8px;
  display: flex;
  align-items: center;
  font-size: 12px;
  color: var(--color-foreground-secondary);
  z-index: 10;
`;

const CharacterCount = styled.span<{ isOverLimit: boolean }>`
  color: ${({ isOverLimit }) =>
    isOverLimit
      ? 'var(--color-accent-error)'
      : 'var(--color-foreground-secondary)'};
  text-shadow: 0 6px 20px rgba(0, 0, 0, 1);
`;

const Footer = styled.div`
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
  gap: ${smallSpace}px;
  padding-left: ${bigSpace}px;
  padding-bottom: ${bigSpace}px;
  padding-right: ${bigSpace}px;
  overflow-x: auto;
`;

enum StylesCardHeaderItems {
  UNDO = 'undo',
  SAVE = 'save',
  CLEAR = 'clear',
  UPSAMPLE = 'upsample',
}

const EMPTY_STYLES: React.CSSProperties = {};
const StylesCard: React.FC<StylesCardProps> = memo(function StylesCard({
  styles,
  setStyles,
  clearStyles,

  suggestedStyles,
  suggestedStylesLoading,
  onPickSuggestedStyle,

  expanded,
  setExpanded,

  stylesInputHeight,
  setStylesInputHeight,

  onUpsampleStyles,
  canUpsampleStyles,

  onSavePrompt,
  canSavePrompt,
  hasSavedPrompts,

  setActiveModal,

  isUpsamplingStyles,

  model,

  headerContentHidden,
  nested,
  fixed = false,
  headerContentItemsOverride,
  styleOverrides = EMPTY_STYLES,
}) {
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  const maxLength = useMemo(() => getTagsMaxLengthForModel(model), [model]);

  const handleClearStyles = useCallback(() => {
    if (!clearStyles) {
      setStyles('');
    } else {
      clearStyles();
    }
  }, [setStyles, clearStyles]);

  const {
    state: undoableStyles,
    setState: setUndoableStyles,
    undo,
    canUndo,
    handleKeyboardEvent,
  } = useSynchronizingStateHistory(styles, setStyles);

  const isOverLimit = useMemo(
    () => undoableStyles.length > maxLength,
    [undoableStyles.length, maxLength]
  );

  const shouldShowCounter = useMemo(
    () => undoableStyles.length >= maxLength * 0.8,
    [undoableStyles.length, maxLength]
  );

  const hasContent = undoableStyles.length > 0;

  const receiveResizerRef = useResizer({
    height: stylesInputHeight ?? 120,
    setHeight: setStylesInputHeight,
    minHeight: 60,
    resizeTargetRef: textareaRef,
  });

  const theme = useTheme() as CreateTheme;

  const isHeaderContentItemEnabled = useCallback(
    (item: StylesCardHeaderItems) => {
      if (!headerContentItemsOverride) return true;
      return headerContentItemsOverride.includes(item);
    },
    [headerContentItemsOverride]
  );

  return (
    <CreateCard
      nested={nested}
      className={RESIZABLE_CONTAINER_CLASS_NAME}
      style={styleOverrides}
      title={
        <CollapsibleCardTitle
          title='Styles'
          subtitle={undoableStyles.replace(/\n/g, ' / ') || ''}
          expanded={expanded}
        />
      }
      collapsible={!fixed}
      expanded={expanded}
      setExpanded={setExpanded}
      headerContentHidden={headerContentHidden || !hasContent || !expanded}
      headerContent={
        <>
          {isHeaderContentItemEnabled(StylesCardHeaderItems.UNDO) ? (
            <Button
              shape={ButtonShape.Pill}
              className={theme.tailwind.bigButtonPadding}
              icon={<EditUndoIcon className='h-4 w-4' />}
              onClick={undo}
              disabled={!canUndo}
            />
          ) : null}
          {isHeaderContentItemEnabled(StylesCardHeaderItems.SAVE) ? (
            <Button
              shape={ButtonShape.Pill}
              className={theme.tailwind.bigButtonPadding}
              disabled={!canSavePrompt}
              icon={canSavePrompt ? BookmarkOutlineIcon : BookmarkIcon}
              onClick={onSavePrompt}
            />
          ) : null}
          {isHeaderContentItemEnabled(StylesCardHeaderItems.CLEAR) ? (
            <Button
              shape={ButtonShape.Pill}
              className={theme.tailwind.bigButtonPadding}
              icon={DiscardIcon}
              onClick={handleClearStyles}
            />
          ) : null}
          {isHeaderContentItemEnabled(StylesCardHeaderItems.UPSAMPLE) ? (
            <Button
              disabled={!canUpsampleStyles}
              variant={ButtonVariant.Aura}
              shape={ButtonShape.Pill}
              className={theme.tailwind.bigButtonPadding}
              aria-label='Upsample styles'
              icon={
                isUpsamplingStyles ? (
                  <SpinnerSVG className='h-4 w-4' />
                ) : (
                  <WandIcon className='h-4 w-4' />
                )
              }
              onClick={onUpsampleStyles}
            />
          ) : null}
        </>
      }
    >
      <StylesContent>
        <TextareaWrapper>
          <CreateTextarea
            ref={textareaRef}
            value={undoableStyles}
            onKeyDown={(e) => handleKeyboardEvent(e.nativeEvent)}
            onChange={(e) => {
              const newValue = e.target.value;
              setUndoableStyles(newValue.slice(0, maxLength));
            }}
            placeholder={'indie, electronic, synths, 120bpm, distorted'}
            className='mb-0 pb-0'
            textareaClassName='placeholder:text-background-fog-dense'
            maxLength={maxLength}
          />
          {shouldShowCounter && (
            <CharacterCounter>
              <CharacterCount isOverLimit={isOverLimit}>
                {undoableStyles.length}/{maxLength}
              </CharacterCount>
            </CharacterCounter>
          )}
        </TextareaWrapper>
        <Footer>
          {suggestedStyles.length > 0 && (
            <Button
              shape={ButtonShape.Pill}
              size={ButtonSize.Small}
              variant={ButtonVariant.Standard}
              icon={LibraryIcon}
              disabled={!hasSavedPrompts}
              onClick={() => setActiveModal(CreateFormModals.StylesPrompts)}
            />
          )}
          {suggestedStyles.map((style, index) => (
            <Button
              key={`${style}-${index}`}
              shape={ButtonShape.Pill}
              size={ButtonSize.Small}
              variant={ButtonVariant.Standard}
              onClick={() => {
                onPickSuggestedStyle(style);
                logWebUserEvent({
                  actionName: 'ClickSuggestedTag',
                  context: {
                    stylesBefore: styles,
                    suggestedTag: style,
                  },
                });
              }}
              aria-label={`Add style: ${style}`}
              className='text-xs whitespace-nowrap'
              iconStart={PlusIcon}
            >
              {style}
            </Button>
          ))}
          {suggestedStylesLoading && (
            <Button
              shape={ButtonShape.Pill}
              size={ButtonSize.Small}
              variant={ButtonVariant.Tertiary}
              icon={<SpinnerSVG className='h-4 w-4' />}
            />
          )}
        </Footer>
      </StylesContent>
      {expanded && !fixed && <ResizerHandle ref={receiveResizerRef} />}
    </CreateCard>
  );
});

export const CustomStylesCard = () => {
  const [styles, setStyles] = useContextSelector(CreateFormContext, (context) =>
    context.selectState<string>([CreateModes.CUSTOM, 'styles'])
  );
  const stylesRef = useRef<string>(styles);
  useEffect(() => {
    stylesRef.current = styles;
  }, [styles]);
  const [expanded, setExpanded] = useContextSelector(
    CreateFormContext,
    (context) =>
      context.selectState<boolean>([CreateModes.CUSTOM, 'stylesExpanded'])
  );
  const [stylesInputHeight, setStylesInputHeight] = useContextSelector(
    CreateFormContext,
    (context) =>
      context.selectState<number>([CreateModes.CUSTOM, 'stylesInputHeight'])
  );
  const model = useContextSelector(
    CreateFormContext,
    (context) => context.state.global.model
  );
  const {
    upsampleTags: upsampleTags,
    isUpsamplingTags,
    canUpsampleTags,
  } = useUpsampleTags(model);
  const handleUpsampleTags = useCallback(async () => {
    const styles = stylesRef.current;
    const { data } = await upsampleTags(styles);
    if (data?.generatedTags) {
      setStyles(data.generatedTags);
    }
  }, [upsampleTags, setStyles]);

  const setActiveModal = useContextSelector(
    CreateFormContext,
    (ctx) => ctx.setActiveModal
  );

  const saveTagsPrompt = useContextSelector(
    CreateFormContext,
    (ctx) => ctx.saveTagsPrompt
  );

  const lastSavedTagsPromptState = useContextSelector(
    CreateFormContext,
    (ctx) => ctx.lastSavedTagsPromptState
  );

  const getTagsPromptToSave = useCallback(() => {
    const currentStyles = styles?.trim() || '';
    const promptToSave = {
      tags: currentStyles !== '' ? currentStyles : undefined,
    };
    return promptToSave;
  }, [styles]);

  const isTagsPromptSaveable = useMemo(() => {
    const currentStyles = styles?.trim() || '';
    const lastSavedTags = lastSavedTagsPromptState?.tags?.trim() || '';

    return currentStyles !== lastSavedTags;
  }, [lastSavedTagsPromptState, styles]);

  const hasSavedPrompts = useContextSelector(
    CreateFormContext,
    (ctx) => ctx.tagsPrompts.length > 0
  );

  const saveCurrentTagsPrompt = useCallback(async () => {
    if (!isTagsPromptSaveable) return;
    const promptToSave = getTagsPromptToSave();
    await saveTagsPrompt(promptToSave);
  }, [isTagsPromptSaveable, saveTagsPrompt, getTagsPromptToSave]);

  const { suggestedStyles, suggestedStylesLoading, onPickSuggestedStyle } =
    useSuggestedStyles(setStyles);

  return (
    <StylesCard
      styles={styles}
      setStyles={setStyles}
      expanded={expanded}
      setExpanded={setExpanded}
      stylesInputHeight={stylesInputHeight}
      setStylesInputHeight={setStylesInputHeight}
      suggestedStyles={suggestedStyles}
      suggestedStylesLoading={suggestedStylesLoading}
      onPickSuggestedStyle={onPickSuggestedStyle}
      onUpsampleStyles={handleUpsampleTags}
      canUpsampleStyles={canUpsampleTags}
      isUpsamplingStyles={isUpsamplingTags}
      canSavePrompt={isTagsPromptSaveable}
      onSavePrompt={saveCurrentTagsPrompt}
      hasSavedPrompts={hasSavedPrompts}
      onUndoSetStyles={noop}
      canUndoSetStyles={true}
      setActiveModal={setActiveModal}
      model={model}
    />
  );
};

export const StudioEditStylesCard = () => {
  const selectedStyles = useContextSelector(
    StudioCreateContext,
    ({ studio }) =>
      studio.stylesEditController.getCurrentSelectionStyles().styles
  );
  const [styles, setStyles] = useContextSelector(
    StudioCreateContext,
    ({ create }) =>
      create.selectState<string>([CreateModes.STUDIO_EDIT, 'styles'])
  );

  const model = useContextSelector(
    StudioCreateContext,
    ({ create }) => create.state.global.model
  );

  const [copyStylesFromSelection, setCopyStylesFromSelection] =
    useContextSelector(StudioCreateContext, ({ create }) =>
      create.selectState<boolean>([
        CreateModes.STUDIO_EDIT,
        'copyStylesFromSelection',
      ])
    );

  const [expanded, setExpanded] = useContextSelector(
    StudioCreateContext,
    ({ create }) =>
      create.selectState<boolean>([CreateModes.STUDIO_EDIT, 'stylesExpanded'])
  );

  const [stylesInputHeight, setStylesInputHeight] = useContextSelector(
    StudioCreateContext,
    ({ create }) =>
      create.selectState<number>([CreateModes.STUDIO_EDIT, 'stylesInputHeight'])
  );

  const overrideSelectedStyles = useCallback(
    (newStyles: SetStateAction<string>) => {
      setStyles(newStyles);
      setCopyStylesFromSelection(false);
    },
    [setStyles, setCopyStylesFromSelection]
  );

  const handleClearStyles = useCallback(() => {
    setStyles('');
    setCopyStylesFromSelection(true);
  }, [setStyles, setCopyStylesFromSelection]);

  const { suggestedStyles, suggestedStylesLoading, onPickSuggestedStyle } =
    useSuggestedStyles(overrideSelectedStyles);

  const setActiveModal = useContextSelector(
    CreateFormContext,
    (ctx) => ctx.setActiveModal
  );

  return (
    <StylesCard
      styles={copyStylesFromSelection ? selectedStyles : styles}
      setStyles={copyStylesFromSelection ? overrideSelectedStyles : setStyles}
      clearStyles={handleClearStyles}
      expanded={expanded}
      setExpanded={setExpanded}
      stylesInputHeight={stylesInputHeight}
      setStylesInputHeight={setStylesInputHeight}
      suggestedStyles={suggestedStyles}
      suggestedStylesLoading={suggestedStylesLoading}
      onPickSuggestedStyle={onPickSuggestedStyle}
      onUpsampleStyles={noop}
      canUpsampleStyles={false}
      isUpsamplingStyles={false}
      canSavePrompt={false}
      hasSavedPrompts={false}
      onSavePrompt={noop}
      onUndoSetStyles={noop}
      canUndoSetStyles={true}
      setActiveModal={setActiveModal}
      model={model}
    />
  );
};

export const StudioStemStylesCard = () => {
  const selectedStyles = useContextSelector(
    StudioCreateContext,
    ({ studio }) =>
      studio.stylesEditController.getCurrentSelectionStyles().styles
  );
  const [styles, setStyles] = useContextSelector(
    StudioCreateContext,
    ({ create }) =>
      create.selectState<string>([CreateModes.STUDIO_STEM, 'styles'])
  );

  const model = useContextSelector(
    StudioCreateContext,
    ({ create }) => create.state.global.model
  );

  const [copyStylesFromSelection, setCopyStylesFromSelection] =
    useContextSelector(StudioCreateContext, ({ create }) =>
      create.selectState<boolean>([
        CreateModes.STUDIO_STEM,
        'copyStylesFromSelection',
      ])
    );

  const [expanded, setExpanded] = useContextSelector(
    StudioCreateContext,
    ({ create }) =>
      create.selectState<boolean>([CreateModes.STUDIO_STEM, 'stylesExpanded'])
  );

  const [stylesInputHeight, setStylesInputHeight] = useContextSelector(
    StudioCreateContext,
    ({ create }) =>
      create.selectState<number>([CreateModes.STUDIO_STEM, 'stylesInputHeight'])
  );

  const overrideSelectedStyles = useCallback(
    (newStyles: SetStateAction<string>) => {
      setStyles(newStyles);
      setCopyStylesFromSelection(false);
    },
    [setStyles, setCopyStylesFromSelection]
  );

  const handleClearStyles = useCallback(() => {
    setStyles('');
    setCopyStylesFromSelection(true);
  }, [setStyles, setCopyStylesFromSelection]);

  const { suggestedStyles, suggestedStylesLoading, onPickSuggestedStyle } =
    useSuggestedStyles(overrideSelectedStyles);

  const setActiveModal = useContextSelector(
    CreateFormContext,
    (ctx) => ctx.setActiveModal
  );

  return (
    <StylesCard
      styles={copyStylesFromSelection ? selectedStyles : styles}
      setStyles={copyStylesFromSelection ? overrideSelectedStyles : setStyles}
      clearStyles={handleClearStyles}
      expanded={expanded}
      setExpanded={setExpanded}
      stylesInputHeight={stylesInputHeight}
      setStylesInputHeight={setStylesInputHeight}
      suggestedStyles={suggestedStyles}
      suggestedStylesLoading={suggestedStylesLoading}
      onPickSuggestedStyle={onPickSuggestedStyle}
      onUpsampleStyles={noop}
      canUpsampleStyles={false}
      isUpsamplingStyles={false}
      canSavePrompt={false}
      hasSavedPrompts={false}
      onSavePrompt={noop}
      onUndoSetStyles={noop}
      canUndoSetStyles={true}
      setActiveModal={setActiveModal}
      model={model}
    />
  );
};

const CHAT_STYLE_OVERRIDES = {
  backgroundColor: 'var(--color-background-fog-thin)',
  backdropFilter: 'blur(20px)',
};

const EMPTY_ARRAY: string[] = [];

const CHAT_CONTENT_ITEMS_OVERRIDE: StylesCardHeaderItems[] = [
  StylesCardHeaderItems.UNDO,
];
export const ChatStylesCard = () => {
  const [styles, setStyles] = useContextSelector(CreateFormContext, (context) =>
    context.selectState<string>([CreateModes.CUSTOM, 'styles'])
  );
  const [expanded, setExpanded] = useContextSelector(
    CreateFormContext,
    (context) =>
      context.selectState<boolean>([CreateModes.CUSTOM, 'stylesExpanded'])
  );
  const [stylesInputHeight, setStylesInputHeight] = useContextSelector(
    CreateFormContext,
    (context) =>
      context.selectState<number>([CreateModes.CUSTOM, 'stylesInputHeight'])
  );
  const model = useContextSelector(
    CreateFormContext,
    (context) => context.state.global.model
  );

  const setActiveModal = useContextSelector(
    CreateFormContext,
    (ctx) => ctx.setActiveModal
  );

  return (
    <StylesCard
      styles={styles}
      setStyles={setStyles}
      expanded={expanded}
      setExpanded={setExpanded}
      stylesInputHeight={stylesInputHeight}
      setStylesInputHeight={setStylesInputHeight}
      suggestedStyles={EMPTY_ARRAY}
      onUpsampleStyles={noop}
      canUpsampleStyles={false}
      isUpsamplingStyles={false}
      onPickSuggestedStyle={noop}
      canSavePrompt={false}
      onSavePrompt={noop}
      hasSavedPrompts={false}
      onUndoSetStyles={noop}
      canUndoSetStyles={true}
      setActiveModal={setActiveModal}
      model={model}
      headerContentItemsOverride={CHAT_CONTENT_ITEMS_OVERRIDE}
      styleOverrides={CHAT_STYLE_OVERRIDES}
    />
  );
};

export default StylesCard;
