'use client';

import { useMessages } from '@ably/chat/react';
import clsx from 'clsx';
import { useCallback, useEffect, useRef, useState } from 'react';

import { useStores } from '@/app/(root)/AppProviders';
import LivingRadioModal from '@/app/(root)/live-radio/LivingRadioModal';
import { toast } from '@/components/toast/Toast';
import { GearIcon, UserIcon } from '@/icons';
import { useApiClient } from '@/lib/apiClient';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { FALLBACK_IMAGE_URL } from '@/utils/constants';

import VoteCountdown from './VoteCountdown';
import { SONG_STYLE_ICONS, STYLE_COLORS, STYLE_COLOR_NAMES } from './constants';
import { SongData, VoteStatus } from './interfaces';

interface VoteForVibeProps {
  currentSong: SongData | null;
  voteStatus: VoteStatus | null;
  setVoteStatus: (status: VoteStatus | null) => void;
  refetchVoteStatus: () => void;
  userVotes: Record<string, number>;
  setUserVotes: (
    votes:
      | Record<string, number>
      | ((prev: Record<string, number>) => Record<string, number>)
  ) => void;
  isVoting: boolean;
  setIsVoting: (voting: boolean) => void;
  updateButtonPositions?: (positions: { [key: number]: DOMRect }) => void;
  onUserVote?: (
    profileUrl: string,
    styleIndex: number,
    buttonRect: DOMRect
  ) => void;
  stationId: string;
}

const VoteForVibe = ({
  currentSong,
  voteStatus,
  setVoteStatus,
  refetchVoteStatus,
  userVotes,
  setUserVotes,
  isVoting,
  setIsVoting,
  updateButtonPositions,
  onUserVote,
  stationId,
}: VoteForVibeProps) => {
  const apiClient = useApiClient();
  const { session } = useStores();
  const { sendMessage } = useMessages();
  const [showLivingRadioModal, setShowLivingRadioModal] = useState(false);
  const buttonRefs = useRef<{ [key: number]: HTMLButtonElement | null }>({});

  // Rate limiting state
  const [isInCooldown, setIsInCooldown] = useState(false);
  const cooldownTimerRef = useRef<NodeJS.Timeout | null>(null);

  // Cleanup timers on unmount
  useEffect(() => {
    return () => {
      if (cooldownTimerRef.current) clearTimeout(cooldownTimerRef.current);
    };
  }, []);

  // Update button positions when buttons change
  useEffect(() => {
    if (!updateButtonPositions || !voteStatus) return;

    const updatePositions = () => {
      const positions: { [key: number]: DOMRect } = {};

      voteStatus.styles.forEach((_, index) => {
        const button = buttonRefs.current[index];
        if (button) {
          positions[index] = button.getBoundingClientRect();
        }
      });

      updateButtonPositions(positions);
    };

    // Initial update
    updatePositions();

    // Update on window resize or scroll
    window.addEventListener('resize', updatePositions);
    window.addEventListener('scroll', updatePositions, true);

    return () => {
      window.removeEventListener('resize', updatePositions);
      window.removeEventListener('scroll', updatePositions, true);
    };
  }, [voteStatus, updateButtonPositions]);

  const submitVote = useCallback(
    async (styleIndex: number): Promise<void> => {
      const { error } = await apiClient.POST(
        '/api/living_radio/{station_id}/vote',
        {
          params: { path: { station_id: stationId } },
          body: { style_index: styleIndex },
        }
      );
      if (error) {
        toast({
          title: 'Failed to submit vote',
          description:
            'Apologies, we were unable to submit your vote. You may be voting too often! Please try again.',
          status: 'error',
          duration: 4000,
          isClosable: true,
        });
        // Throw error so the calling code knows the vote failed (we catch this)
        throw new Error('Vote submission failed');
      }
    },
    [apiClient, stationId]
  );

  const vote = useCallback(
    async (style: string) => {
      if (!voteStatus || !voteStatus.vote_session_uuid) return;

      // Check if in cooldown period
      if (isInCooldown) {
        return;
      }

      // Check if voting is closed
      if (voteStatus.vote_closed) {
        toast({
          title: 'Voting has ended',
          description: 'The voting session for this song has closed.',
          status: 'info',
          duration: 3000,
          isClosable: true,
        });
        return;
      }
      const styleIndex = voteStatus.styles.indexOf(style);
      if (styleIndex === -1) return;
      // Check if user is logged in
      if (!session.userId) {
        // Log attempt to vote when not logged in
        logWebUserEvent({
          actionName: 'LivingRadioVoteAttemptedNotLoggedIn',
          principalObjectType: 'livingRadioVote',
          principalObjectValue: voteStatus.vote_session_uuid,
          context: {
            stationId: stationId,
            sessionId: voteStatus.vote_session_uuid,
            style,
            styleIndex,
          },
        });
        document.exitFullscreen();
        setShowLivingRadioModal(true);
        return;
      }

      const sessionId = voteStatus.vote_session_uuid;
      const previousVoteIndex = userVotes[sessionId];
      const isNewVote = previousVoteIndex === undefined;
      const isSwitching = !isNewVote && previousVoteIndex !== styleIndex;

      // Don't do anything if voting for the same option
      if (!isNewVote && previousVoteIndex === styleIndex) return;

      // Start cooldown immediately when changing styles
      if (isSwitching) {
        setIsInCooldown(true);

        // Clear cooldown after 1 second
        cooldownTimerRef.current = setTimeout(() => {
          setIsInCooldown(false);
        }, 2000);
      }

      // Trigger floating animation immediately
      const buttonRect =
        buttonRefs.current[styleIndex]?.getBoundingClientRect();
      if (buttonRect && onUserVote) {
        onUserVote(
          session.user?.avatar_image_url || FALLBACK_IMAGE_URL,
          styleIndex,
          buttonRect
        );
      }

      // Optimistically update vote counts
      const optimisticVotes = [...voteStatus.votes];

      // If user had a previous vote, decrease that count
      if (isSwitching && previousVoteIndex !== undefined) {
        optimisticVotes[previousVoteIndex] = Math.max(
          0,
          optimisticVotes[previousVoteIndex] - 1
        );
      }

      // Increase the count for the new vote
      optimisticVotes[styleIndex] = optimisticVotes[styleIndex] + 1;

      // Update the UI optimistically
      setIsVoting(true);
      setVoteStatus(
        voteStatus
          ? {
              ...voteStatus,
              votes: optimisticVotes,
            }
          : null
      );

      // Update local storage with the user's vote for this session
      setUserVotes((prev) => ({
        ...prev,
        [sessionId]: styleIndex,
      }));

      try {
        // Backend expects 1-4, but indexOf returns 0-3, so add 1
        await submitVote(styleIndex + 1);

        // Log successful vote submission
        logWebUserEvent({
          actionName: 'LivingRadioVoteSubmitted',
          principalObjectType: 'livingRadioVote',
          principalObjectValue: sessionId,
          context: {
            userId: session.userId || '',
            stationId: stationId,
            sessionId,
            styleOptions: voteStatus.styles,
            style,
            styleIndex,
            isNewVote,
            isSwitching,
            previousStyle:
              isSwitching && previousVoteIndex !== undefined
                ? voteStatus.styles[previousVoteIndex]
                : undefined,
            previousStyleIndex: isSwitching ? previousVoteIndex : undefined,
            voteCount: optimisticVotes[styleIndex],
          },
        });

        // Only send vote message to chat after successful API call
        const previousStyle =
          isSwitching && previousVoteIndex !== undefined
            ? voteStatus.styles[previousVoteIndex]
            : null;

        // Get colors for styles
        const styleColor =
          STYLE_COLOR_NAMES[styleIndex % STYLE_COLOR_NAMES.length];
        const previousStyleColor =
          isSwitching && previousVoteIndex !== undefined
            ? STYLE_COLOR_NAMES[previousVoteIndex % STYLE_COLOR_NAMES.length]
            : null;

        // Generate text for the vote message
        const voteText =
          isSwitching && previousStyle
            ? `switched from ${previousStyle} to ${style}`
            : `voted for ${style}`;

        await sendMessage({
          text: voteText, // Provide actual text to avoid API error
          metadata: {
            type: 'vote',
            userId: session.userId,
            style,
            styleIndex,
            styleColor,
            previousStyle,
            previousStyleIndex: isSwitching ? previousVoteIndex : null,
            previousStyleColor,
            isNewVote,
            isSwitching,
          },
        });

        // Refetch vote status after voting to get the real counts
        setTimeout(() => {
          refetchVoteStatus();
        }, 2000);
      } catch (error) {
        // Log vote failure
        logWebUserEvent({
          actionName: 'LivingRadioVoteFailed',
          principalObjectType: 'livingRadioVote',
          principalObjectValue: sessionId,
          context: {
            userId: session.userId || '',
            stationId: stationId,
            sessionId,
            style,
            styleIndex,
            errorMessage:
              error instanceof Error ? error.message : 'Vote submission failed',
          },
        });

        // Revert optimistic update on error
        setVoteStatus(voteStatus); // Revert to original vote status

        // Revert the user vote in localStorage
        setUserVotes((prev) => {
          const newVotes = { ...prev };
          if (previousVoteIndex !== undefined) {
            newVotes[sessionId] = previousVoteIndex;
          } else {
            delete newVotes[sessionId];
          }
          return newVotes;
        });
      } finally {
        setIsVoting(false);
      }
    },
    [
      voteStatus,
      userVotes,
      submitVote,
      refetchVoteStatus,
      setUserVotes,
      setIsVoting,
      setVoteStatus,
      session,
      sendMessage,
      onUserVote,
      stationId,
      isInCooldown,
    ]
  );

  if (!voteStatus) {
    return (
      <div className='mb-4 min-h-[80px] rounded-2xl bg-background-glass-thick p-4 md:mb-6 md:min-h-[100px] md:p-6'>
        <div className='mb-3 text-sm font-medium text-foreground-secondary md:mb-4 md:text-base'>
          Vote for the vibe
        </div>
        <div className='py-6 text-center text-sm text-white/60 md:py-8 md:text-base'>
          Loading voting options...
        </div>
      </div>
    );
  }

  return (
    <div className='relative mb-4 min-h-[80px] rounded-2xl bg-background-glass-thick p-4 md:mb-6 md:min-h-[100px] md:p-6'>
      <div className='mb-3 text-sm font-medium text-foreground-secondary md:mb-4 md:text-base'>
        {voteStatus.vote_closed ? 'Voting has ended' : 'Vote for the vibe'}
      </div>

      <div className='mb-3 grid grid-cols-2 gap-2 md:mb-4 md:flex md:flex-row md:gap-1'>
        {voteStatus.styles.map((style: string, index: number) => {
          const colorClass = STYLE_COLORS[index % STYLE_COLORS.length];
          const voteCount = voteStatus.votes[index];

          // Check if this option is selected by the user
          const isSelected = Boolean(
            voteStatus.vote_session_uuid &&
              userVotes[voteStatus.vote_session_uuid] === index
          );

          // Check if this is the winning style when voting is closed
          const isWinner =
            voteStatus.vote_closed && voteStatus.winning_style === style;

          // Get the appropriate icon for this style
          const IconComponent =
            SONG_STYLE_ICONS[style as keyof typeof SONG_STYLE_ICONS] ||
            GearIcon;

          return (
            <div key={style} className='relative md:flex-1'>
              <button
                ref={(el) => {
                  buttonRefs.current[index] = el;
                }}
                onClick={() => {
                  if (
                    isVoting ||
                    isSelected ||
                    voteStatus.vote_closed ||
                    isInCooldown
                  )
                    return;
                  vote(style);
                }}
                disabled={voteStatus.vote_closed || isInCooldown}
                className={clsx(
                  'flex w-full min-w-0 flex-row items-center justify-start gap-2 rounded-[8px] px-2 py-2 transition-all duration-200 md:flex-col md:justify-center md:gap-3 md:px-1 md:py-4',
                  {
                    'bg-white/20 ring-2 ring-white/80': isWinner,
                    'bg-white/20 ring-2 ring-white/30': isSelected && !isWinner,
                    'cursor-not-allowed bg-white/5':
                      voteStatus.vote_closed && !isWinner && !isSelected,
                    'cursor-pointer bg-white/5 hover:bg-white/10':
                      !voteStatus.vote_closed &&
                      !isSelected &&
                      !isWinner &&
                      !isInCooldown,
                    'cursor-not-allowed opacity-50':
                      isInCooldown && !isSelected,
                  }
                )}
              >
                <IconComponent
                  className={clsx(
                    'hidden h-5 w-5 shrink-0 text-white transition-opacity md:block md:h-8 md:w-8',
                    {
                      'opacity-100': isWinner || isSelected,
                      'opacity-50': !isWinner && !isSelected,
                    }
                  )}
                />
                {/* Mobile: style name in middle, desktop: style and votes together below icon */}
                <span
                  className={clsx(
                    colorClass,
                    'flex-1 truncate text-left text-xs font-semibold md:hidden md:flex-none md:text-center md:text-sm'
                  )}
                >
                  {style}
                </span>
                {/* Desktop: style name and vote count in same row */}
                <div className='hidden items-center gap-2 md:flex'>
                  <span className={clsx(colorClass, 'text-xs font-semibold')}>
                    {style}
                  </span>
                  <span className='flex items-center rounded-md bg-white/10 px-1.5 py-0.5 text-[10px] font-medium text-white md:px-2 md:text-[11px]'>
                    <UserIcon className='mr-1 h-3 w-3 md:h-3.5 md:w-3.5' />
                    {voteCount}
                  </span>
                </div>
                {/* Mobile: vote count on right */}
                <span className='flex items-center rounded-md bg-white/10 px-1.5 py-0.5 text-[10px] font-medium text-white md:hidden'>
                  <UserIcon className='mr-1 h-3 w-3' />
                  {voteCount}
                </span>
              </button>
            </div>
          );
        })}
      </div>

      <VoteCountdown
        currentSong={currentSong}
        voteClosed={voteStatus.vote_closed || false}
      />

      {/* Living Radio Modal */}
      <LivingRadioModal
        isOpen={showLivingRadioModal}
        onClose={() => setShowLivingRadioModal(false)}
        trigger='vote'
        stationId={stationId}
      />
    </div>
  );
};

export default VoteForVibe;
