import React, { useCallback, useMemo } from 'react';
import { useVoiceStore } from '../../stores/voiceStore';
import { CallTimer } from './CallTimer';
import { useSpaceStore } from '../../stores/spaceStore';
import { useAuthStore } from '../../stores/authStore';
import { useContextMenuStore, type ContextMenuItem } from '../../stores/contextMenuStore';
import { buildVoiceModMenuItems, VolumeSliderItem } from './voiceMenuItems';
import { VoiceUserRow } from './VoiceUserRow';

const EMPTY_VOICE_USERS: string[] = [];

interface VoiceChannelProps {
  channelId: string;
  channelName: string;
  onClick: () => void;
  locked?: boolean;
  canManage?: boolean;
  onSettingsClick?: () => void;
  voiceUserHandlers?: (userId: string, channelId: string) => {
    draggable: boolean;
    isBeingDragged: boolean;
    onDragStart: (e: React.DragEvent) => void;
    onDragEnd: (e: React.DragEvent) => void;
  };
  dropZone?: {
    onDragOver: (e: React.DragEvent) => void;
    onDragEnter: (e: React.DragEvent) => void;
    onDragLeave: (e: React.DragEvent) => void;
    onDrop: (e: React.DragEvent) => void;
    isDragOver: boolean;
    isValidTarget: boolean;
  };
}

/** Wrapper component for the volume slider so it can use hooks (useState). */
export function VoiceChannel({ channelId, channelName, onClick, locked, canManage, onSettingsClick, voiceUserHandlers, dropZone }: VoiceChannelProps) {
  // Present only while someone is in the channel; the server drops the room
  // when it empties, which is what makes the next call start from zero.
  const callStartedAt = useVoiceStore((s) => s.voiceRoomStarts.get(channelId));
  const serverVoiceUsers = useVoiceStore((s) => s.voiceUsers.get(channelId)) ?? EMPTY_VOICE_USERS;
  const currentVoiceChannel = useVoiceStore((s) => s.currentVoiceChannelId);
  const participants = useVoiceStore((s) => s.participants);
  const isLiveKitConnected = useVoiceStore((s) => s.isLiveKitConnected);

  // For OUR channel: LiveKit participants are the single source of truth.
  // For other channels: use server-provided voiceUsers (only available source).
  const voiceUsers = useMemo(() => {
    if (currentVoiceChannel === channelId && isLiveKitConnected && participants.length > 0) {
      return [...new Set(participants.map(p => p.userId))];
    }
    return serverVoiceUsers;
  }, [currentVoiceChannel, channelId, isLiveKitConnected, participants, serverVoiceUsers]);
  const localIsDeafened = useVoiceStore((s) => s.isDeafened);
  const localIsMuted = useVoiceStore((s) => s.isMuted);
  const voiceUserStates = useVoiceStore((s) => s.voiceUserStates);
  const spaceMutedUserIds = useVoiceStore((s) => s.spaceMutedUserIds);
  const spaceDeafenedUserIds = useVoiceStore((s) => s.spaceDeafenedUserIds);
  const permissionMutedUserIds = useVoiceStore((s) => s.permissionMutedUserIds);
  const participantMutes = useVoiceStore((s) => s.participantMutes);
  const unwatchedCameras = useVoiceStore((s) => s.unwatchedCameras);
  const speakingUserIds = useVoiceStore((s) => s.speakingUserIds);
  const currentUserId = useVoiceStore((s) => {
    const local = s.participants.find(p => p.isLocal);
    return local?.userId ?? null;
  });
  const members = useSpaceStore((s) => s.members);
  const channelToSpaceMap = useSpaceStore((s) => s.channelToSpaceMap);
  const myUser = useAuthStore((s) => s.user);
  const isActive = currentVoiceChannel === channelId;

  const openContextMenu = useContextMenuStore((s) => s.open);

  const handleContextMenu = useCallback(
    (e: React.MouseEvent, userId: string) => {
      if (userId === myUser?.id) return;
      e.preventDefault();
      e.stopPropagation();

      // Build moderation items
      const modItems = buildVoiceModMenuItems(userId, channelId);

      const items: ContextMenuItem[] = [...modItems];

      // Separator after mod items
      if (modItems.length > 0) {
        items.push({ key: 'mod-end-sep', type: 'separator' });
      }

      // Mute User checkbox
      items.push({
        key: 'mute-user',
        type: 'checkbox',
        label: 'Mute User',
        subscribe: useVoiceStore.subscribe,
        getChecked: () => useVoiceStore.getState().participantMutes.get(userId) ?? false,
        onChange: (checked) => useVoiceStore.getState().setParticipantMute(userId, checked),
      });

      items.push({ key: 'vol-sep', type: 'separator' });

      // Volume slider
      items.push({
        key: 'volume',
        type: 'custom',
        render: () => React.createElement(VolumeSliderItem, { userId }),
      });

      openContextMenu({ x: e.clientX, y: e.clientY }, items);
    },
    [myUser?.id, channelId, openContextMenu],
  );

  return (
    <div
      onDragOver={dropZone?.onDragOver}
      onDragEnter={dropZone?.onDragEnter}
      onDragLeave={dropZone?.onDragLeave}
      onDrop={dropZone?.onDrop}
      className={dropZone?.isDragOver && dropZone?.isValidTarget ? 'rounded-[8px] ring-1 ring-accent-mint/40' : ''}
    >
      <button
        onClick={onClick}
        className={`relative w-full flex items-center gap-1.5 px-[10px] h-8 rounded-[6px] group transition-colors ${
          locked
            ? 'text-txt-tertiary/50 cursor-not-allowed'
            : isActive
              ? 'bg-surface-elevated text-txt-primary'
              : 'text-txt-tertiary hover:text-txt-secondary hover:bg-interactive-hover'
        }`}
        title={locked ? "You don't have permission to connect to this channel" : undefined}
      >
        {isActive && !locked && (
          <div
            className="absolute -left-[2px] top-1/2 -translate-y-1/2 w-[3px] bg-white rounded-r-full"
            style={{ height: '55%', opacity: 0.7 }}
          />
        )}
        {locked ? (
          <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0 text-[#6e6e7a]/50">
            <path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z" />
          </svg>
        ) : (
          <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" className="flex-shrink-0 text-[#6e6e7a]">
            <path d="M11 5L6 9H2V15H6L11 19V5ZM15.54 8.46C16.48 9.4 17 10.67 17 12S16.48 14.6 15.54 15.54L14.12 14.12C14.69 13.55 15 12.79 15 12S14.69 10.45 14.12 9.88L15.54 8.46ZM19.07 4.93C20.91 6.77 22 9.28 22 12C22 14.72 20.91 17.23 19.07 19.07L17.66 17.66C19.11 16.21 20 14.21 20 12C20 9.79 19.11 7.79 17.66 6.34L19.07 4.93Z" />
          </svg>
        )}
        <span className="truncate text-[15px] font-medium flex-1 text-left">{channelName}</span>
        {callStartedAt !== undefined && (
          <CallTimer
            startedAt={callStartedAt}
            className="flex-shrink-0 text-[11px] text-txt-tertiary font-medium"
          />
        )}
        {canManage && (
          <svg
            width="16"
            height="16"
            viewBox="0 0 24 24"
            fill="currentColor"
            className="flex-shrink-0 opacity-0 group-hover:opacity-100 text-txt-tertiary hover:text-txt-primary transition-opacity"
            onClick={(e) => {
              e.stopPropagation();
              onSettingsClick?.();
            }}
          >
            <path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z" />
          </svg>
        )}
      </button>

      {/* Connected users */}
      {voiceUsers.length > 0 && (
        <div className="ml-9 mt-0.5 space-y-0.5">
          {voiceUsers.map((userId) => {
            const member = members.find(m => m.userId === userId);
            const participant = participants.find(p => p.userId === userId);
            const displayName = member?.user.displayName ?? member?.user.username ?? participant?.username ?? userId;
            const avatar = member?.user.avatar ?? null;
            const avatarColor = member?.user.avatarColor;
            const wsStatus = voiceUserStates.get(userId);
            const isParticipantDeafened = userId === currentUserId
              ? localIsDeafened
              : (participant?.isDeafened ?? wsStatus?.isDeafened ?? false);
            const isMuted = userId === currentUserId
              ? localIsMuted
              : (participant?.isMuted ?? wsStatus?.isMuted ?? false);
            const hasCamera = participant?.isCameraOn ?? wsStatus?.isCameraOn ?? false;
            const isScreenSharing = participant?.isScreenSharing ?? wsStatus?.isScreenSharing ?? false;
            const spaceId = channelToSpaceMap.get(channelId);
            const isSpaceMuted = spaceMutedUserIds.has(`${spaceId}:${userId}`);
            const isSpaceDeafened = spaceDeafenedUserIds.has(`${spaceId}:${userId}`);
            const isPermissionMuted = permissionMutedUserIds.has(`${spaceId}:${userId}`);

            const userDrag = voiceUserHandlers?.(userId, channelId);
            const isDraggable = !!(userDrag?.draggable && userId !== myUser?.id);
            const isBeingDragged = userDrag?.isBeingDragged ?? false;

            return (
              <div
                key={userId}
                className={`px-[10px] py-1 rounded-[6px] hover:bg-interactive-hover transition-colors ${
                  isDraggable ? 'cursor-grab active:cursor-grabbing' : ''
                } ${isBeingDragged ? 'opacity-50' : ''}`}
                draggable={isDraggable}
                onDragStart={isDraggable ? userDrag!.onDragStart : undefined}
                onDragEnd={isDraggable ? userDrag!.onDragEnd : undefined}
                onContextMenu={(e) => handleContextMenu(e, userId)}
              >
                <VoiceUserRow
                  userId={member?.user.homeUserId ?? userId}
                  displayName={displayName}
                  user={member?.user}
                  avatar={avatar}
                  avatarColor={avatarColor ?? undefined}
                  isMuted={isMuted}
                  isDeafened={isParticipantDeafened}
                  isCameraOn={hasCamera}
                  isUnwatchedCamera={userId !== myUser?.id && unwatchedCameras.has(userId)}
                  isScreenSharing={isScreenSharing}
                  isServerMuted={isSpaceMuted}
                  isServerDeafened={isSpaceDeafened}
                  isPermissionMuted={isPermissionMuted}
                  isLocallyMuted={userId !== myUser?.id && (participantMutes.get(userId) ?? false)}
                  isSpeaking={speakingUserIds.has(userId)}
                />
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}
