import { type FC, memo, useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router';

import { useWavesurfer } from '@wavesurfer/react';
import {
  IconMicrophone,
  IconMicrophoneFilled,
  IconPlayerPause,
  IconPlayerPlayFilled,
} from '@material-hu/icons/tabler';
import CircularProgress from '@material-hu/mui/CircularProgress';
import IconButton from '@material-hu/mui/IconButton';
import Stack from '@material-hu/mui/Stack';
import { useTheme } from '@material-hu/mui/styles';
import Typography from '@material-hu/mui/Typography';

import HuChips from '@material-hu/components/design-system/Chip';

import { useIntersectionObserver } from 'src/hooks/useIntersectionObserver';
import { BUNNY_IMAGE_CLASSES } from 'src/pages/dashboard/Conversations/constants';
import {
  type ConversationMessage,
  type HuDataMessage,
  type SendAudiosType,
} from 'src/pages/dashboard/Conversations/types';
import {
  formatTimeInPlayAudio,
  formatTotalTimeAudio,
  isAudioAttachment,
} from 'src/pages/dashboard/Conversations/utils';
import { getInitials } from 'src/utils/userUtils';

import { useAudioManager } from '../../../contexts/AudioManagerContext';
import { useGetCorrectAttachmentUrl } from '../../../hooks/useConversationsQueries';
import LazySecureImage from '../../shared/LazySecureImage';

type LazyAudioProps = {
  audio: SendAudiosType;
  conversationId?: string;
  huData?: HuDataMessage;
  isLoggedUserMessage: boolean;
  username: string;
  isMessageForwarded: boolean;
  nextMessage?: ConversationMessage;
  messageTs?: string;
};

const WIDTH_AUDIO = 200;
const WIDTH_AUDIO_CONTAINER = 270;
const WIDTH_WAVESURFER = 181;
const MIC_BADGE_SIZE = 18;
const MIC_ICON_SIZE = 14;
const DOT_SIZE = 8;

const TIMESTAMP_LEFT_OFFSET_RECEIVED = 45;
const TIMESTAMP_LEFT_OFFSET_SENT = 85;

const speeds = [1, 1.5, 2];

const initialOptions = {
  height: 50,
  barWidth: 3,
  barGap: 1,
  barRadius: 3,
  cursorWidth: 0,
};

type AudioMessagePlayerProps = {
  showPlaybackSpeed: boolean;
  speed: number;
  conversationId?: string;
  onChangeSpeed: () => void;
  username: string;
  profilePicture?: string;
  isMessageForwarded: boolean;
  hasBeenVisible?: boolean;
  isLoggedUserMessage: boolean;
};

const AudioMessagePlayer: FC<AudioMessagePlayerProps> = props => {
  const {
    showPlaybackSpeed,
    speed,
    conversationId,
    onChangeSpeed,
    username,
    profilePicture,
    isMessageForwarded,
    hasBeenVisible,
    isLoggedUserMessage,
  } = props;
  const theme = useTheme();
  return (
    <Stack
      sx={{
        justifyContent: 'center',
        alignItems: 'center',
      }}
    >
      {!showPlaybackSpeed && !!hasBeenVisible && (
        <Stack sx={{ position: 'relative', display: 'inline-flex' }}>
          <LazySecureImage
            conversationId={conversationId}
            url={profilePicture}
            isAvatar
            imageClass={BUNNY_IMAGE_CLASSES.AVATAR}
            avatarProps={{
              text: !isMessageForwarded ? getInitials(username) : undefined,
              Icon: isMessageForwarded ? IconMicrophone : undefined,
              sx: {
                background: isMessageForwarded
                  ? theme.palette.new.background.feedback.success
                  : theme.palette.new.background.layout.default,
                color: isMessageForwarded
                  ? theme.palette.new.text.feedback.success
                  : theme.palette.new.text.neutral.default,
              },
            }}
          />
          {!isMessageForwarded && (
            <Stack
              sx={{
                position: 'absolute',
                bottom: -2,
                left: !isLoggedUserMessage ? -4 : undefined,
                right: isLoggedUserMessage ? -4 : undefined,
                width: MIC_BADGE_SIZE,
                height: MIC_BADGE_SIZE,
                borderRadius: '50%',
                alignItems: 'center',
                justifyContent: 'center',
                backgroundColor: 'transparent',
                color: !isLoggedUserMessage
                  ? theme.palette.new.graphics.brand[500]
                  : theme.palette.new.icon.neutral.lighter,
              }}
            >
              <IconMicrophoneFilled size={MIC_ICON_SIZE} />
            </Stack>
          )}
        </Stack>
      )}
      {showPlaybackSpeed && (
        <HuChips
          label={`${speed}x`}
          onClick={onChangeSpeed}
          size="small"
          sx={{
            minWidth: '40px',
            borderRadius: '100px',
            backgroundColor: theme.palette.new.graphics.brand[500],

            color: 'white',
            border: 0,
          }}
        />
      )}
    </Stack>
  );
};

const LazyAudio: FC<LazyAudioProps> = props => {
  const {
    audio,
    conversationId,
    huData,
    isLoggedUserMessage,
    username,
    isMessageForwarded,
    nextMessage,
    messageTs = '',
  } = props;
  const [speed, setSpeed] = useState(1);
  const { id } = useParams();
  const containerRef = useRef(null);
  const theme = useTheme();

  const { targetRef, hasBeenVisible } = useIntersectionObserver();

  const { audioToPlay, setNextAudioToPlay, audioPlaying, setAudioPlaying } =
    useAudioManager();
  const showPlaybackSpeed = audioPlaying.messageTs === messageTs;

  // Only fetch the audio URL when the element has been visible
  const { data } = useGetCorrectAttachmentUrl({
    url: audio.url,
    conversationId,
    enabledToShowError: false,
    enabled: !!audio?.url && !audio.isLoading && hasBeenVisible,
  });

  const getAudioUrl = () => {
    if (!hasBeenVisible) {
      return '';
    }
    if (!audio?.isLoading && hasBeenVisible) {
      return data;
    }
    return audio?.url;
  };

  const { wavesurfer, isPlaying, currentTime, isReady } = useWavesurfer({
    ...initialOptions,
    container: containerRef,
    waveColor: theme.palette.new.text.neutral.lighter,
    progressColor: !isLoggedUserMessage
      ? theme.palette.new.graphics.brand[500]
      : theme.palette.new.icon.neutral.lighter,
    url: getAudioUrl(),
  });

  useEffect(() => {
    if (!wavesurfer || !wavesurfer.on || !hasBeenVisible) return;

    const handleFinish = () => {
      // TODO: review this logic when pagination is changed
      if (!id || !nextMessage || !nextMessage.attachments?.length) {
        setAudioPlaying({ messageTs: null, isPlaying: false });
        return;
      }
      const attachment = nextMessage.attachments[0];
      if (isAudioAttachment(attachment)) {
        setAudioPlaying({ messageTs: nextMessage.ts, isPlaying: false });
        setNextAudioToPlay(nextMessage.ts);
        return;
      }
      setAudioPlaying({ messageTs: null, isPlaying: false });
    };

    wavesurfer.on('finish', handleFinish);

    return () => {
      wavesurfer.un('finish', handleFinish);
    };
  }, [
    wavesurfer,
    hasBeenVisible,
    id,
    nextMessage?.ts,
    setAudioPlaying,
    setNextAudioToPlay,
  ]);

  useEffect(() => {
    if (!wavesurfer || !hasBeenVisible) return;

    if (audioPlaying.messageTs !== messageTs && audioPlaying.isPlaying) {
      wavesurfer.pause();
    }
  }, [
    wavesurfer,
    audioPlaying.messageTs,
    audioPlaying.isPlaying,
    hasBeenVisible,
    messageTs,
  ]);

  useEffect(() => {
    if (!wavesurfer || !isReady || !hasBeenVisible) return;

    if (audioToPlay === messageTs) {
      wavesurfer.play();
      setAudioPlaying({ messageTs, isPlaying: true });
      setNextAudioToPlay('');
    }
  }, [audioToPlay, hasBeenVisible, isReady, setAudioPlaying, wavesurfer]);

  const onPlayPause = async () => {
    if (wavesurfer && hasBeenVisible) {
      wavesurfer.playPause();
      if (!isPlaying) {
        setNextAudioToPlay(messageTs);
      }
      setAudioPlaying({
        messageTs,
        isPlaying: !isPlaying,
      });
    }
  };

  const onChangeSpeed = () => {
    if (speed === speeds[speeds.length - 1]) {
      wavesurfer?.setPlaybackRate(1, true);
      setSpeed(1);
      return;
    }
    const spIndex = speeds.indexOf(speed);
    setSpeed(speeds[spIndex + 1]);
    wavesurfer?.setPlaybackRate(speeds[spIndex + 1], true);
  };

  const dotProgress = Math.min(
    currentTime / (wavesurfer?.getDuration() || 1),
    1,
  );

  return (
    <div ref={targetRef}>
      <Stack
        sx={{
          flexDirection: 'row',
          alignItems: 'center',
          justifyContent: 'flex-start',
          width: WIDTH_AUDIO_CONTAINER,
          gap: 0.5,
        }}
      >
        {isLoggedUserMessage && (
          <AudioMessagePlayer
            conversationId={conversationId}
            hasBeenVisible={hasBeenVisible}
            showPlaybackSpeed={showPlaybackSpeed}
            isLoggedUserMessage={isLoggedUserMessage}
            speed={speed}
            onChangeSpeed={onChangeSpeed}
            username={username}
            profilePicture={huData?.picture_asset_url}
            isMessageForwarded={isMessageForwarded}
          />
        )}

        <Stack sx={{ alignItems: 'center' }}>
          <Stack
            sx={{
              height: '40px',
              width: '40px',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            {!isReady || audio?.isLoading ? (
              <CircularProgress size={24} />
            ) : (
              <IconButton
                variant="tertiary"
                onClick={onPlayPause}
                sx={{
                  color: theme.palette.new.icon.neutral.lighter,
                  '& svg': {
                    stroke: theme.palette.new.icon.neutral.lighter,
                  },
                  '&:hover, &:focus, &:active': {
                    color: theme.palette.new.icon.neutral.lighter,
                    '& svg': {
                      stroke: theme.palette.new.icon.neutral.lighter,
                    },
                  },
                }}
              >
                {!isPlaying ? <IconPlayerPlayFilled /> : <IconPlayerPause />}
              </IconButton>
            )}
          </Stack>
          <Typography
            variant="globalXXS"
            sx={{
              position: 'absolute',
              bottom: 8,
              left: !isLoggedUserMessage
                ? TIMESTAMP_LEFT_OFFSET_RECEIVED
                : TIMESTAMP_LEFT_OFFSET_SENT,
            }}
            color={theme.palette.new.text.neutral.lighter}
          >
            {!isPlaying
              ? formatTotalTimeAudio(audio?.audio_duration_ms ?? 0)
              : formatTimeInPlayAudio(currentTime)}
          </Typography>
        </Stack>
        <Stack
          ref={containerRef}
          sx={{ position: 'relative', width: WIDTH_AUDIO }}
        >
          {isReady && (
            <Stack
              sx={{
                position: 'absolute',
                top: '50%',
                transform: 'translateY(-50%)',
                left: `${dotProgress * (WIDTH_WAVESURFER - DOT_SIZE)}px`,
                width: DOT_SIZE,
                height: DOT_SIZE,
                borderRadius: '50%',
                backgroundColor: !isLoggedUserMessage
                  ? theme.palette.new.graphics.brand[500]
                  : theme.palette.new.icon.neutral.lighter,
                pointerEvents: 'none',
                zIndex: 2,
              }}
            />
          )}
        </Stack>
        {!isLoggedUserMessage && (
          <AudioMessagePlayer
            hasBeenVisible={hasBeenVisible}
            showPlaybackSpeed={showPlaybackSpeed}
            isLoggedUserMessage={isLoggedUserMessage}
            speed={speed}
            onChangeSpeed={onChangeSpeed}
            username={username}
            profilePicture={huData?.picture_asset_url}
            isMessageForwarded={isMessageForwarded}
          />
        )}
      </Stack>
    </div>
  );
};

export default memo(LazyAudio);
