import styled from '@emotion/styled';
import { observer } from 'mobx-react-lite';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import { ClipContextProvider } from '@/components/clipBrowser/ClipContext';
import {
  AutoClipPlaybackProvider,
  useClipPlaybackContext,
} from '@/components/clipBrowser/useClipPlayback';
import { toast } from '@/components/toast/Toast';
import useClip from '@/hooks/useClip';
import { useContextSelector } from '@/hooks/useContextSelector';
import { CloseIcon } from '@/icons';
import { getClipTitle } from '@/utils/clip';
import {
  DEFAULT_AUDIO_WEIGHT_VALUE,
  MIN_EXTEND_FROM_SECONDS,
} from '@/utils/constants';
import { decodeTimeFormat, encodeTimeFormat } from '@/utils/utils';

import CreateFormContext from '../../v2/CreateFormContext';
import changeClipConditionType from '../../v2/actions/changeClipConditionType';
import {
  ClipConditionTypes,
  ConditionTypes,
  CreateModes,
  ExtendCondition,
  TimedCondition,
  TimedConditionTypes,
} from '../../v2/types';
import { mapConditionTypeToAddConditionType } from '../../v2/utils/mapConditionType';
import { ClipConditionTypePicker } from './ClipConditionTypePicker';
import CreateAudioDisplay from './CreateAudioDisplay';
import CreateCard from './CreateCard';
import { ExtendFromOverlay } from './ExtendFromOverlay';
import { SongMeta } from './common';
import { useUrlAudioSampler } from './useUrlAudioSampler';

const ContentWrapper = styled.div`
  padding: 0 16px 0px 16px;
`;

const TimeReadoutContainer = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
`;

const TimeReadout = styled.div`
  pointer-events: auto;
  color: var(--color-foreground-inactive);
  font-size: 14px;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 4px;
`;

const EditableTime = styled.span`
  border: none;
  color: var(--color-foreground-primary);
  font-size: 14px;
  outline: none;
  display: inline-block;
  cursor: text;

  &:focus {
    outline: 2px solid var(--color-accent-brand);
    outline-offset: -1px;
  }
`;

const CloseButton = styled.button`
  background: none;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-foreground-tertiary);
  transition: color 0.15s ease;

  &:hover {
    color: var(--color-foreground-primary);
  }
`;

const ExtendCard = ({
  clipTitle,
  clipDurationSeconds,
  clipArtworkUrl,

  isPlaying,
  play,
  pause,

  extendFromSeconds,
  setExtendFromSeconds,

  getCurrentProgress,
  setCurrentProgress,

  sampleAudio,
  frameCountRef,

  onClear,
  changeConditionType,
}: {
  clipTitle: string;
  clipArtworkUrl: string;
  clipDurationSeconds: number;

  isPlaying: boolean;
  play: () => void;
  pause: () => void;

  extendFromSeconds: number;
  setExtendFromSeconds: (seconds: number) => void;

  getCurrentProgress: () => number;
  setCurrentProgress: (progress: number) => void;

  sampleAudio: (progress: number) => number;
  frameCountRef?: React.RefObject<number>;

  onClear: () => void;
  changeConditionType: (type: ClipConditionTypes) => void;
}) => {
  const { playbar } = useStores();
  const extendFromProgress = extendFromSeconds / clipDurationSeconds;

  const [isDragging, setIsDragging] = useState(false);

  const editableTimeRef = useRef<HTMLSpanElement>(null);

  useEffect(() => {
    if (!isDragging && editableTimeRef.current) {
      const newValue = encodeTimeFormat(extendFromSeconds, 1) || '';
      // Only update if not currently focused (being edited by user)
      if (document.activeElement !== editableTimeRef.current) {
        editableTimeRef.current.textContent = newValue;
      }
    }
  }, [extendFromSeconds, isDragging]);

  const handleDragUpdate = useCallback(
    (progress: number) => {
      setIsDragging(true);
      const time = progress * clipDurationSeconds;
      if (editableTimeRef.current) {
        editableTimeRef.current.textContent = encodeTimeFormat(time, 1) || '';
      }
    },
    [clipDurationSeconds]
  );

  const getTimeFromProgress = useCallback(
    (progress: number) => progress * clipDurationSeconds,
    [clipDurationSeconds]
  );
  const getProgressFromTime = useCallback(
    (time: number) => time / clipDurationSeconds,
    [clipDurationSeconds]
  );
  const setExtendFromProgress = useCallback(
    (progress: number) => {
      setExtendFromSeconds(progress * clipDurationSeconds);
      setCurrentProgress(progress);
      setIsDragging(false);
    },
    [clipDurationSeconds, setExtendFromSeconds, setCurrentProgress]
  );

  const applyLocalTimeValue = useCallback(() => {
    if (!editableTimeRef.current) return;
    let time = decodeTimeFormat(editableTimeRef.current.textContent || '');
    if (time !== null && !Number.isNaN(time)) {
      if (time < MIN_EXTEND_FROM_SECONDS) {
        time = MIN_EXTEND_FROM_SECONDS;
        toast({
          title: 'Extend from time is too short',
          description: `Must be at least ${MIN_EXTEND_FROM_SECONDS} second${MIN_EXTEND_FROM_SECONDS === 1 ? '' : 's'}`,
          duration: 3000,
          isClosable: true,
        });
      }
      const progress = Math.max(0, Math.min(1, getProgressFromTime(time)));
      setExtendFromProgress(progress);
      editableTimeRef.current.textContent =
        encodeTimeFormat(getTimeFromProgress(progress), 1) || '';
    } else {
      editableTimeRef.current.textContent =
        encodeTimeFormat(extendFromSeconds, 1) || '';
    }
  }, [
    extendFromSeconds,
    getTimeFromProgress,
    getProgressFromTime,
    setExtendFromProgress,
  ]);

  return (
    <CreateCard
      title={
        <SongMeta
          title={clipTitle}
          artworkUrl={clipArtworkUrl}
          duration={clipDurationSeconds}
          getPlaybackTime={() => getCurrentProgress() * clipDurationSeconds}
          isPlaying={isPlaying}
          play={play}
          pause={pause}
        />
      }
      headerContent={
        <div className='flex items-center gap-4'>
          <ClipConditionTypePicker
            conditionType={ConditionTypes.EXTEND}
            changeConditionType={changeConditionType}
          />
          <CloseButton aria-label='Clear extend selection' onClick={onClear}>
            <CloseIcon className='h-4 w-4' />
          </CloseButton>
        </div>
      }
    >
      <ContentWrapper>
        <CreateAudioDisplay
          isPlaying={isPlaying}
          sampleAudio={sampleAudio}
          getCurrentProgress={getCurrentProgress}
          setCurrentProgress={setCurrentProgress}
          frameCountRef={frameCountRef}
        >
          <ExtendFromOverlay
            minExtendFromProgress={
              clipDurationSeconds <= 0
                ? 0
                : MIN_EXTEND_FROM_SECONDS / clipDurationSeconds
            }
            extendFromProgress={extendFromProgress}
            setExtendFromProgress={setExtendFromProgress}
            onDragUpdate={handleDragUpdate}
          />
        </CreateAudioDisplay>
        <TimeReadoutContainer>
          <TimeReadout>
            Extend from{' '}
            <EditableTime
              ref={editableTimeRef}
              contentEditable
              suppressContentEditableWarning
              onKeyDown={(e) => {
                if (e.key === ' ') {
                  e.preventDefault();
                  playbar.togglePlay();
                }
                if (e.key === 'Enter') {
                  e.preventDefault();
                  applyLocalTimeValue();
                  e.currentTarget.blur();
                }
              }}
              onBlur={() => {
                applyLocalTimeValue();
              }}
            />
          </TimeReadout>
        </TimeReadoutContainer>
      </ContentWrapper>
    </CreateCard>
  );
};

export const PlayableExtendCard = observer(
  ({
    clipTitle,
    clipArtworkUrl,
    clipDurationSeconds,
    extendFromSeconds,
    setExtendFromSeconds,
    sampleAudio,
    frameCountRef,
    onClear,
    changeConditionType,
  }: {
    clipTitle: string;
    clipArtworkUrl: string;
    clipDurationSeconds: number;
    extendFromSeconds: number;
    setExtendFromSeconds: (seconds: number) => void;
    sampleAudio: (progress: number) => number;
    frameCountRef?: React.RefObject<number>;
    onClear: () => void;
    changeConditionType: (type: ClipConditionTypes) => void;
  }) => {
    const { isPlaying, onPlayClick, getCurrentProgress, setCurrentProgress } =
      useClipPlaybackContext();

    return (
      <ExtendCard
        clipTitle={clipTitle}
        clipArtworkUrl={clipArtworkUrl}
        clipDurationSeconds={clipDurationSeconds}
        extendFromSeconds={extendFromSeconds}
        setExtendFromSeconds={setExtendFromSeconds}
        sampleAudio={sampleAudio}
        frameCountRef={frameCountRef}
        isPlaying={isPlaying}
        play={onPlayClick}
        pause={onPlayClick}
        getCurrentProgress={getCurrentProgress}
        setCurrentProgress={setCurrentProgress}
        onClear={onClear}
        changeConditionType={changeConditionType}
      />
    );
  }
);

export const CreateExtendCard = observer(() => {
  const [timedCondition, setTimedCondition] = useContextSelector(
    CreateFormContext,
    (context) => {
      return context.selectState<TimedCondition<TimedConditionTypes> | null>([
        'global',
        'timedCondition',
      ]);
    }
  );

  const extendCondition = useMemo(() => {
    return timedCondition?.type === ConditionTypes.EXTEND
      ? (timedCondition.condition as ExtendCondition)
      : null;
  }, [timedCondition]);

  const { clip } = useClip(extendCondition?.clipId);
  const setState = useContextSelector(
    CreateFormContext,
    (context) => context.setState
  );

  const addCondition = useContextSelector(
    CreateFormContext,
    (context) => context.addCondition
  );

  const frameCountRef = useRef<number>(0);
  const sampleAudio = useUrlAudioSampler(extendCondition?.clipId, clip?.status);

  const handleClear = useCallback(() => {
    setTimedCondition(null);

    setState((prev) => ({
      ...prev,
      [CreateModes.CUSTOM]: {
        ...prev[CreateModes.CUSTOM],
        audioInfluence: DEFAULT_AUDIO_WEIGHT_VALUE,
      },
    }));
  }, [setTimedCondition, setState]);

  const changeConditionType = useCallback(
    (newType: ClipConditionTypes) => {
      if (!clip) return;

      const addConditionType = mapConditionTypeToAddConditionType(newType);

      if (addConditionType) {
        // Use unified addCondition with replacement
        addCondition(clip.id, addConditionType, ConditionTypes.EXTEND);
      } else {
        // Fall back to old method for unsupported types
        setState(changeClipConditionType(ConditionTypes.EXTEND, newType, clip));
      }
    },
    [setState, clip, addCondition]
  );

  return (
    extendCondition &&
    clip && (
      <AutoClipPlaybackProvider clip={clip} studioPreviewsWithContextWindow>
        <ClipContextProvider clip={clip}>
          <PlayableExtendCard
            clipTitle={getClipTitle(clip)}
            clipArtworkUrl={clip.image_url ?? ''}
            clipDurationSeconds={clip.metadata?.duration ?? 0}
            extendFromSeconds={extendCondition.startSeconds}
            setExtendFromSeconds={(seconds) =>
              setTimedCondition({
                type: ConditionTypes.EXTEND,
                condition: {
                  ...extendCondition,
                  startSeconds: Math.max(MIN_EXTEND_FROM_SECONDS, seconds),
                },
              })
            }
            sampleAudio={sampleAudio}
            frameCountRef={frameCountRef}
            onClear={handleClear}
            changeConditionType={changeConditionType}
          />
        </ClipContextProvider>
      </AutoClipPlaybackProvider>
    )
  );
});

export default ExtendCard;
