import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Box, IconButton, Text, Tooltip, TooltipProvider, color } from 'folds';
import { Icon, Icons } from '../../components/icons';
import { useAtom } from 'jotai';
import { useCall } from './useCall';
import { useRoomCallMembers } from './useRoomCallMembers';
import { CallState, CallType } from './types';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { getMemberDisplayName, getMemberAvatarMxc } from '../../utils/room';
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { UserAvatar } from '../../components/user-avatar';
import { useSelectedRoom } from '../../hooks/router/useSelectedRoom';
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import { UnreadBadge } from '../../components/unread-badge';
import { useRoomUnread } from '../../state/hooks/unread';
import { roomToUnreadAtom } from '../../state/room/roomToUnread';
import { useSetting } from '../../state/hooks/settings';
import { settingsAtom } from '../../state/settings';
import { pendingUndockDragAtom, PendingDragState } from './pendingDragState';
import { RemoteCursorOverlay, useScreenShareCursor } from './RemoteCursorOverlay';
import * as css from './CallOverlay.css';

/** Drag position state */
interface DragPosition {
  x: number;
  y: number;
}

/** Threshold in pixels for docking detection */
const DOCK_THRESHOLD = 50;
/** Combined width of space panel (68px) + room list panel (256px) for dock detection */
const ROOM_LIST_EDGE = 324;

/** Dock zone type */
type DockZone = 'none' | 'right' | 'sidebar';

/** Custom hook for draggable functionality with viewport constraints and docking detection */
function useDraggable(
  onDockRight?: () => void,
  onDockSidebar?: () => void,
  onUndockRequest?: () => void,
  isDocked?: boolean,
  pendingDrag?: PendingDragState | null,
  clearPendingDrag?: () => void
) {
  const [position, setPosition] = useState<DragPosition>({ x: 0, y: 0 });
  const [isDragging, setIsDragging] = useState(false);
  const [nearDockZone, setNearDockZone] = useState<DockZone>('none');
  const dragRef = useRef<HTMLDivElement>(null);
  const dragStartRef = useRef<{ x: number; y: number; startX: number; startY: number } | null>(null);

  // Check for pending drag state from undocking
  useEffect(() => {
    if (pendingDrag && Date.now() - pendingDrag.timestamp < 500) {
      // Calculate where the floating panel should be positioned
      // so the mouse is at the same relative position as when dragging started
      const panelWidth = dragRef.current?.offsetWidth ?? 400;
      const panelHeight = dragRef.current?.offsetHeight ?? 300;
      
      // The floating panel renders centered (transform: translate(-50%, -50%))
      // from right: 20px, bottom: 20px by default (CSS position)
      // We need to calculate the position offset to put the panel under the mouse
      // at the same offset as when the drag started
      
      // Target: mouse should be at offsetX from left edge, offsetY from top edge
      // Panel default position: right: 20px, bottom: 20px (centered via transform)
      // Default center: (window.innerWidth - 20 - panelWidth/2, window.innerHeight - 20 - panelHeight/2)
      
      const defaultCenterX = window.innerWidth - 20 - panelWidth / 2;
      const defaultCenterY = window.innerHeight - 20 - panelHeight / 2;
      
      // Where we want the panel's top-left corner to be:
      const targetLeft = pendingDrag.startX - pendingDrag.offsetX;
      const targetTop = pendingDrag.startY - pendingDrag.offsetY;
      
      // Where the panel's top-left corner would be by default:
      const defaultLeft = defaultCenterX - panelWidth / 2;
      const defaultTop = defaultCenterY - panelHeight / 2;
      
      // Calculate the offset needed
      const offsetX = targetLeft - defaultLeft;
      const offsetY = targetTop - defaultTop;
      
      setPosition({ x: offsetX, y: offsetY });
      setIsDragging(true);
      dragStartRef.current = {
        x: offsetX,
        y: offsetY,
        startX: pendingDrag.startX,
        startY: pendingDrag.startY,
      };
      clearPendingDrag?.();
    }
  }, [pendingDrag, clearPendingDrag]);

  const handleMouseDown = useCallback((e: React.MouseEvent) => {
    if ((e.target as HTMLElement).closest('button')) return;
    e.preventDefault();
    setIsDragging(true);
    dragStartRef.current = {
      x: position.x,
      y: position.y,
      startX: e.clientX,
      startY: e.clientY,
    };
  }, [position]);

  // Clamp position to viewport bounds
  const clampToViewport = useCallback(() => {
    if (!dragRef.current) return;
    const rect = dragRef.current.getBoundingClientRect();
    const padding = 16;
    
    let newX = position.x;
    let newY = position.y;
    let needsUpdate = false;
    
    if (rect.left < padding) {
      newX = position.x + (padding - rect.left);
      needsUpdate = true;
    }
    if (rect.right > window.innerWidth - padding) {
      newX = position.x - (rect.right - window.innerWidth + padding);
      needsUpdate = true;
    }
    if (rect.top < padding) {
      newY = position.y + (padding - rect.top);
      needsUpdate = true;
    }
    if (rect.bottom > window.innerHeight - padding) {
      newY = position.y - (rect.bottom - window.innerHeight + padding);
      needsUpdate = true;
    }
    
    if (needsUpdate) {
      setPosition({ x: newX, y: newY });
    }
  }, [position]);

  useEffect(() => {
    const handleResize = () => {
      clampToViewport();
    };
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, [clampToViewport]);

  useEffect(() => {
    if (!isDragging) return undefined;

    const handleMouseMove = (e: MouseEvent) => {
      if (!dragStartRef.current || !dragRef.current) return;
      
      // Check dock zones
      const nearRightEdge = e.clientX > window.innerWidth - DOCK_THRESHOLD;
      const nearSidebarZone = e.clientX < ROOM_LIST_EDGE + DOCK_THRESHOLD && 
                              e.clientY > window.innerHeight - 150;
      
      if (!isDocked) {
        if (nearSidebarZone) {
          setNearDockZone('sidebar');
        } else if (nearRightEdge) {
          setNearDockZone('right');
        } else {
          setNearDockZone('none');
        }
      }
      
      // If docked and dragging away from edge, trigger undock
      if (isDocked) {
        const dragDistance = Math.abs(e.clientX - dragStartRef.current.startX);
        if (dragDistance > DOCK_THRESHOLD) {
          onUndockRequest?.();
          setIsDragging(false);
          dragStartRef.current = null;
          return;
        }
      }
      
      const deltaX = e.clientX - dragStartRef.current.startX;
      const deltaY = e.clientY - dragStartRef.current.startY;
      
      const rect = dragRef.current.getBoundingClientRect();
      const padding = 16;
      
      let newX = dragStartRef.current.x + deltaX;
      let newY = dragStartRef.current.y + deltaY;
      
      const newLeft = rect.left - position.x + newX;
      const newRight = rect.right - position.x + newX;
      const newTop = rect.top - position.y + newY;
      const newBottom = rect.bottom - position.y + newY;
      
      if (newLeft < padding) {
        newX += padding - newLeft;
      }
      if (newRight > window.innerWidth - padding) {
        newX -= newRight - window.innerWidth + padding;
      }
      if (newTop < padding) {
        newY += padding - newTop;
      }
      if (newBottom > window.innerHeight - padding) {
        newY -= newBottom - window.innerHeight + padding;
      }
      
      setPosition({ x: newX, y: newY });
    };

    const handleMouseUp = (e: MouseEvent) => {
      // Check which dock zone to use
      const nearRightEdge = e.clientX > window.innerWidth - DOCK_THRESHOLD;
      const nearSidebarZone = e.clientX < ROOM_LIST_EDGE + DOCK_THRESHOLD && 
                              e.clientY > window.innerHeight - 150;
      
      if (!isDocked) {
        if (nearSidebarZone) {
          onDockSidebar?.();
        } else if (nearRightEdge) {
          onDockRight?.();
        }
      }
      setIsDragging(false);
      setNearDockZone('none');
      dragStartRef.current = null;
    };

    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);

    return () => {
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
  }, [isDragging, position, isDocked, onDockRight, onDockSidebar, onUndockRequest]);

  return { position, isDragging, nearDockZone, handleMouseDown, dragRef };
}

/**
 * Custom Screen share icon SVG (Phosphor-style)
 */
function ScreenShareIcon() {
  return (
    <svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor">
      <path d="M21 16V8a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h5v2H8v2h8v-2h-2v-2h5a2 2 0 0 0 2-2zm-2 0H5V8h14v8z" />
      <path d="M12 9l-4 4h3v3h2v-3h3l-4-4z" />
    </svg>
  );
}

/**
 * Custom Screen share off icon SVG (Phosphor-style)
 */
function ScreenShareOffIcon() {
  return (
    <svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor">
      <path d="M21 16V8a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h5v2H8v2h8v-2h-2v-2h5a2 2 0 0 0 2-2zm-2 0H5V8h14v8z" />
      <path d="M2 4l20 16" stroke="currentColor" strokeWidth="2" />
    </svg>
  );
}

/** Fullscreen icon */
function FullscreenIcon() {
  return (
    <svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor">
      <path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
    </svg>
  );
}

/** Exit fullscreen icon */
function ExitFullscreenIcon() {
  return (
    <svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor">
      <path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z" />
    </svg>
  );
}

/** Picture-in-Picture icon */
function PipIcon() {
  return (
    <svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor">
      <path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z" />
    </svg>
  );
}

/**
 * Formats call duration in MM:SS format
 */
function formatDuration(seconds: number): string {
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}

/** Screen share entry type */
export interface ScreenShareEntry {
  /** Unique identifier for the screen share */
  id: string;
  /** Whether this is the local user's screen share */
  isLocal: boolean;
  /** Matrix user ID of the participant sharing */
  participantId: string;
  /** Room ID the call is in (for cursor resolution) */
  roomId: string;
  /** LiveKit participant identity */
  livekitId?: string;
  /** The underlying track (LiveKit RemoteTrack) */
  track?: unknown;
  /** The MediaStream for the screen share */
  stream?: MediaStream;
}

/** Props for ScreenShareItem component */
interface ScreenShareItemProps {
  /** The screen share entry to display */
  share: ScreenShareEntry;
}

/**
 * Individual screen share display component
 * Handles attaching the video stream/track properly
 */
export function ScreenShareItem({ share }: ScreenShareItemProps) {
  const videoContainerRef = useRef<HTMLDivElement>(null);
  const [showControls, setShowControls] = useState(false);
  const [isPoppedOut, setIsPoppedOut] = useState(false);
  const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const { handleMouseMove, handleMouseLeave } = useScreenShareCursor(share.participantId);

  useEffect(() => {
    const container = videoContainerRef.current;
    if (!container) return undefined;

    // Always render video in main window (even when popped out - just hidden)
    // This keeps the track alive

    // Handle local screenshare (MediaStream)
    if (share.isLocal && share.stream) {
      const videoEl = document.createElement('video');
      videoEl.className = css.ScreenShareVideo;
      videoEl.autoplay = true;
      videoEl.playsInline = true;
      videoEl.muted = true;
      videoEl.srcObject = share.stream;

      container.innerHTML = '';
      container.appendChild(videoEl);

      return () => {
        videoEl.srcObject = null;
        if (videoEl.parentNode) {
          videoEl.parentNode.removeChild(videoEl);
        }
      };
    }

    // Handle remote screenshare (LiveKit track)
    if (!share.isLocal && share.track) {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const track = share.track as any;

      if (typeof track.attach === 'function') {
        const videoEl = track.attach() as HTMLVideoElement;
        videoEl.className = css.ScreenShareVideo;

        container.innerHTML = '';
        container.appendChild(videoEl);

        return () => {
          if (typeof track.detach === 'function') {
            track.detach(videoEl);
          }
          if (videoEl.parentNode) {
            videoEl.parentNode.removeChild(videoEl);
          }
        };
      }
    }

    return undefined;
  }, [share.isLocal, share.stream, share.track]);

  const handlePopOut = useCallback(() => {
    const popoutWindow = window.open(
      '',
      '_blank',
      'width=1200,height=800,frame=false,titleBarStyle=hidden'
    );
    
    if (!popoutWindow) return;
    
    popoutWindow.document.write(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>Screen Share</title>
          <style>
            * {
              margin: 0;
              padding: 0;
              box-sizing: border-box;
            }
            html, body {
              width: 100%;
              height: 100%;
              overflow: hidden;
              background: #000;
              user-select: none;
              position: relative;
            }
            #videoContainer {
              width: 100%;
              height: 100%;
            }
            #videoContainer video {
              width: 100%;
              height: 100%;
              object-fit: contain;
              display: block;
              -webkit-app-region: drag;
              cursor: move;
            }
            #closeButton {
              position: absolute;
              top: 12px;
              right: 12px;
              width: 32px;
              height: 32px;
              background: rgba(0, 0, 0, 0.7);
              border: none;
              border-radius: 8px;
              color: #fff;
              font-size: 20px;
              cursor: pointer;
              display: none;
              align-items: center;
              justify-content: center;
              z-index: 1000;
              -webkit-app-region: no-drag;
              transition: background 0.2s;
            }
            #closeButton:hover {
              background: rgba(255, 0, 0, 0.8);
            }
            #closeButton.visible {
              display: flex;
            }
          </style>
        </head>
        <body>
          <div id="videoContainer"></div>
          <button id="closeButton" title="Close">×</button>
          <script>
            let hideTimeout = null;
            const closeButton = document.getElementById('closeButton');
            
            function showCloseButton() {
              if (hideTimeout) {
                clearTimeout(hideTimeout);
                hideTimeout = null;
              }
              closeButton.classList.add('visible');
              
              hideTimeout = setTimeout(() => {
                closeButton.classList.remove('visible');
                hideTimeout = null;
              }, 2000);
            }
            
            // Show on any mouse movement
            document.addEventListener('mousemove', showCloseButton, true);
            
            // Keep visible when hovering over button
            closeButton.addEventListener('mouseenter', () => {
              if (hideTimeout) {
                clearTimeout(hideTimeout);
                hideTimeout = null;
              }
              closeButton.classList.add('visible');
            });
            
            // Start hide timer when leaving button
            closeButton.addEventListener('mouseleave', () => {
              hideTimeout = setTimeout(() => {
                closeButton.classList.remove('visible');
                hideTimeout = null;
              }, 2000);
            });
            
            // Close window on click
            closeButton.addEventListener('click', () => {
              window.close();
            });
            
            // Keep video playing - prevent pause from visibility changes or focus loss
            function keepVideoPlaying() {
              const video = document.querySelector('video');
              if (video && video.paused && video.srcObject) {
                video.play().catch(() => {});
              }
            }
            
            // Check every 500ms if video got paused
            setInterval(keepVideoPlaying, 500);
            
            // Also resume on visibility change
            document.addEventListener('visibilitychange', () => {
              if (!document.hidden) {
                keepVideoPlaying();
              }
            });
            
            // Resume on window focus
            window.addEventListener('focus', keepVideoPlaying);
          </script>
        </body>
      </html>
    `);
    popoutWindow.document.close();
    
    // Wait for document to be ready, attach video FIRST (while still attached to main), THEN hide main
    setTimeout(() => {
      const videoContainer = popoutWindow.document.getElementById('videoContainer');
      if (!videoContainer) return;
      
      // For local screenshare, create new video with the MediaStream
      if (share.isLocal && share.stream) {
        const videoEl = popoutWindow.document.createElement('video');
        videoEl.autoplay = true;
        videoEl.playsInline = true;
        videoEl.muted = true;
        videoEl.srcObject = share.stream;
        videoContainer.appendChild(videoEl);
        videoEl.play().catch(() => {});
        
        // Now that pop-out has the stream, hide main window
        setIsPoppedOut(true);
      }
      
      // For remote screenshare, use LiveKit track's attach method (creates fresh video)
      if (!share.isLocal && share.track) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const track = share.track as any;
        if (typeof track.attach === 'function') {
          // track.attach() creates a brand new video element attached to the track
          // Track is now attached to BOTH main window and pop-out
          const videoEl = track.attach() as HTMLVideoElement;
          videoEl.style.width = '100%';
          videoEl.style.height = '100%';
          videoEl.style.objectFit = 'contain';
          videoEl.style.display = 'block';
          videoEl.style.cursor = 'move';
          // @ts-expect-error webkit property
          videoEl.style.webkitAppRegion = 'drag';
          videoContainer.appendChild(videoEl);
          
          // Now that pop-out has the track, hide main window (which will detach from main)
          setIsPoppedOut(true);
        }
      }
    }, 100);
    
    // Show preview again when pop-out window is closed
    popoutWindow.addEventListener('beforeunload', () => {
      // For remote tracks, detach from pop-out
      if (!share.isLocal && share.track) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const track = share.track as any;
        if (typeof track.detach === 'function') {
          const videoContainer = popoutWindow.document.getElementById('videoContainer');
          if (videoContainer) {
            const videoEl = videoContainer.querySelector('video');
            if (videoEl) {
              track.detach(videoEl);
            }
          }
        }
      }
      setIsPoppedOut(false);
    });
  }, [share.isLocal, share.stream, share.track]);

  const handleMouseMoveWrapper = useCallback((e: React.MouseEvent) => {
    // Clear any pending hide timeout
    if (hideTimeoutRef.current) {
      clearTimeout(hideTimeoutRef.current);
      hideTimeoutRef.current = null;
    }
    setShowControls(true);
    handleMouseMove(e);
  }, [handleMouseMove]);

  const handleMouseLeaveWrapper = useCallback((e: React.MouseEvent) => {
    // Delay hiding controls to prevent flickering
    hideTimeoutRef.current = setTimeout(() => {
      setShowControls(false);
    }, 300);
    handleMouseLeave(e);
  }, [handleMouseLeave]);

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

  return (
    <div 
      className={css.ScreenShareContainer} 
      style={{ 
        position: isPoppedOut ? 'absolute' : 'relative',
        width: isPoppedOut ? '1px' : undefined,
        height: isPoppedOut ? '1px' : undefined,
        opacity: isPoppedOut ? 0 : 1,
        pointerEvents: isPoppedOut ? 'none' : 'auto',
        overflow: isPoppedOut ? 'hidden' : 'visible',
        left: isPoppedOut ? '-9999px' : undefined,
      }}
    >
      {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
      <div 
        ref={videoContainerRef} 
        className={css.ScreenShareVideoContainer} 
        onMouseMove={handleMouseMoveWrapper}
        onMouseLeave={handleMouseLeaveWrapper}
        style={{ cursor: 'pointer' }}
      />
      
      {/* Control buttons overlay */}
      {showControls && (
        <Box 
          style={{ 
            position: 'absolute',
            top: '12px',
            right: '12px',
            display: 'flex',
            gap: '8px',
            zIndex: 10,
            pointerEvents: 'auto',
          }}
          onMouseEnter={() => {
            // Keep controls visible when hovering over them
            if (hideTimeoutRef.current) {
              clearTimeout(hideTimeoutRef.current);
              hideTimeoutRef.current = null;
            }
            setShowControls(true);
          }}
          onMouseLeave={() => {
            // Hide controls after a delay when leaving the buttons
            hideTimeoutRef.current = setTimeout(() => {
              setShowControls(false);
            }, 300);
          }}
        >
          <TooltipProvider
            tooltip={
              <Tooltip variant="Secondary">
                <Text size="T200">Pop Out</Text>
              </Tooltip>
            }
            position="Bottom"
            align="Center"
          >
            {(triggerRef) => (
              <IconButton
                ref={triggerRef}
                variant="Secondary"
                size="300"
                radii="300"
                onClick={handlePopOut}
                aria-label="Open in new window"
              >
                <Icon size="200" src={Icons.External} />
              </IconButton>
            )}
          </TooltipProvider>
        </Box>
      )}
      
      <RemoteCursorOverlay shareId={share.participantId} roomId={share.roomId} />
    </div>
  );
}

/**
 * Component that displays active call controls and video streams
 */
export function CallOverlay() {
  const { activeCall, endCall, toggleMute, toggleDeafen, toggleVideo, toggleScreenShare } = useCall();
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const selectedRoomId = useSelectedRoom();
  const [duration, setDuration] = useState(0);
  const [isTogglingScreenShare, setIsTogglingScreenShare] = useState(false);
  const [isFullscreen, setIsFullscreen] = useState(false);
  const [isPip, setIsPip] = useState(false);
  const [showParticipants, setShowParticipants] = useState(true);
  const [collapsedScreenShares, setCollapsedScreenShares] = useState<Set<string>>(new Set());
  const localVideoRef = useRef<HTMLVideoElement>(null);
  const remoteVideoRef = useRef<HTMLVideoElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const { navigateRoom } = useRoomNavigate();
  
  // Docking state
  const [isDocked, setIsDocked] = useSetting(settingsAtom, 'isCallPanelDocked');
  const [, setDockPosition] = useSetting(settingsAtom, 'callPanelDockPosition');
  
  // Pending drag state from undocking
  const [pendingDrag, setPendingDrag] = useAtom(pendingUndockDragAtom);
  
  const clearPendingDrag = useCallback(() => {
    setPendingDrag(null);
  }, [setPendingDrag]);
  
  const handleDockRight = useCallback(() => {
    setDockPosition('right');
    setIsDocked(true);
  }, [setIsDocked, setDockPosition]);
  
  const handleDockSidebar = useCallback(() => {
    setDockPosition('sidebar');
    setIsDocked(true);
  }, [setIsDocked, setDockPosition]);
  
  const handleUndockRequest = useCallback(() => {
    setIsDocked(false);
  }, [setIsDocked]);
  
  const { position, isDragging, nearDockZone, handleMouseDown, dragRef } = useDraggable(
    handleDockRight,
    handleDockSidebar,
    handleUndockRequest,
    isDocked,
    pendingDrag,
    clearPendingDrag
  );
  
  // Get unread notifications for the call room
  const unread = useRoomUnread(activeCall?.roomId ?? '', roomToUnreadAtom);
  
  // Get call members from Matrix state events (provides real Matrix user IDs)
  const { callMembers } = useRoomCallMembers(activeCall?.roomId ?? '');
  const myUserId = mx.getUserId() ?? '';

  // Memoize the screenshare list to prevent re-renders causing flickering
  const allScreenShares = useMemo((): ScreenShareEntry[] => {
    const shares: ScreenShareEntry[] = [];
    
    // Add local screenshare first if active
    if (activeCall?.isScreenSharing && activeCall?.localScreenStream) {
      shares.push({
        id: 'local',
        isLocal: true,
        participantId: myUserId,
        roomId: activeCall.roomId,
        stream: activeCall.localScreenStream,
      });
    }
    
    // Add remote screenshares
    const remoteScreenTracks = activeCall?.remoteScreenTracks 
      ? Array.from(activeCall.remoteScreenTracks.values())
      : [];
    
    remoteScreenTracks.forEach((trackInfo) => {
      // Try to extract deviceId from LiveKit identity (format: "deviceId_timestamp")
      const livekitId = trackInfo.participantId;
      const underscoreIndex = livekitId.lastIndexOf('_');
      const possibleDeviceId = underscoreIndex > 0 ? livekitId.substring(0, underscoreIndex) : livekitId;
      
      // Look up Matrix user ID by matching device ID from call members
      const matchingMember = callMembers.find((m) => m.deviceId === possibleDeviceId);
      const matrixUserId = matchingMember?.userId ?? livekitId;
      
      shares.push({
        id: livekitId,
        isLocal: false,
        participantId: matrixUserId,
        roomId: activeCall?.roomId ?? '',
        livekitId,
        track: trackInfo.track,
        stream: trackInfo.stream,
      });
    });
    
    return shares;
  }, [activeCall?.isScreenSharing, activeCall?.localScreenStream, activeCall?.remoteScreenTracks, activeCall?.roomId, callMembers, myUserId]);

  useEffect(() => {
    if (!activeCall || activeCall.state !== CallState.Connected) {
      setDuration(0);
      return undefined;
    }

    const { startTime } = activeCall;
    const updateDuration = () => {
      const elapsed = Math.floor((Date.now() - startTime) / 1000);
      setDuration(elapsed);
    };

    updateDuration();
    const interval = setInterval(updateDuration, 1000);

    return () => clearInterval(interval);
  }, [activeCall]);

  useEffect(() => {
    if (localVideoRef.current && activeCall?.localStream) {
      localVideoRef.current.srcObject = activeCall.localStream;
    }
  }, [activeCall?.localStream]);

  useEffect(() => {
    if (remoteVideoRef.current && activeCall?.remoteStreams.size) {
      const [firstStream] = activeCall.remoteStreams.values();
      if (firstStream) {
        remoteVideoRef.current.srcObject = firstStream;
      }
    }
  }, [activeCall?.remoteStreams]);

  // Fullscreen toggle handler
  const handleToggleFullscreen = useCallback(async () => {
    if (!containerRef.current) return;
    
    try {
      if (!isFullscreen) {
        await containerRef.current.requestFullscreen();
        setIsFullscreen(true);
      } else if (document.fullscreenElement) {
        await document.exitFullscreen();
        setIsFullscreen(false);
      }
    } catch (err) {
      // eslint-disable-next-line no-console
      console.error('Fullscreen error:', err);
    }
  }, [isFullscreen]);

  // Pop-out window handler
  const handlePopOut = useCallback(() => {
    if (!activeCall) return;
    
    // Get room inside callback to avoid initialization order issues
    const callRoom = mx.getRoom(activeCall.roomId);
    
    const popoutWindow = window.open(
      '',
      '_blank',
      'width=1200,height=800,menubar=no,toolbar=no,location=no,status=no'
    );
    
    if (popoutWindow && activeCall.remoteStreams.size > 0) {
      const [firstStream] = activeCall.remoteStreams.values();
      
      popoutWindow.document.write(`
        <!DOCTYPE html>
        <html>
          <head>
            <title>Video Call - ${callRoom?.name ?? 'Call'}</title>
            <style>
              body {
                margin: 0;
                padding: 0;
                background: #000;
                display: flex;
                align-items: center;
                justify-content: center;
                height: 100vh;
                overflow: hidden;
              }
              video {
                max-width: 100%;
                max-height: 100%;
                width: 100%;
                height: 100%;
                object-fit: contain;
              }
            </style>
          </head>
          <body>
            <video id="remoteVideo" autoplay playsinline></video>
            <script>
              const video = document.getElementById('remoteVideo');
              // Stream will be set from parent window
            </script>
          </body>
        </html>
      `);
      popoutWindow.document.close();
      
      // Wait for window to load, then set the video stream
      setTimeout(() => {
        const videoEl = popoutWindow.document.getElementById('remoteVideo');
        if (videoEl) {
          videoEl.srcObject = firstStream;
        }
      }, 100);
    }
  }, [activeCall, mx]);

  // PiP toggle handler
  const handleTogglePip = useCallback(async () => {
    const videoEl = remoteVideoRef.current;
    if (!videoEl) return;
    
    try {
      if (!isPip && document.pictureInPictureEnabled) {
        await videoEl.requestPictureInPicture();
        setIsPip(true);
      } else if (document.pictureInPictureElement) {
        await document.exitPictureInPicture();
        setIsPip(false);
      }
    } catch (err) {
      // eslint-disable-next-line no-console
      console.error('PiP error:', err);
    }
  }, [isPip]);

  // Listen for fullscreen change events (only for the call overlay itself, not screenshares)
  useEffect(() => {
    const handleFullscreenChange = () => {
      // Only set fullscreen state if the call overlay container is fullscreened
      setIsFullscreen(document.fullscreenElement === containerRef.current);
    };
    document.addEventListener('fullscreenchange', handleFullscreenChange);
    return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);
  }, []);

  // Listen for PiP change events
  useEffect(() => {
    const videoEl = remoteVideoRef.current;
    if (!videoEl) return undefined;
    
    const handlePipEnter = () => setIsPip(true);
    const handlePipLeave = () => setIsPip(false);
    
    videoEl.addEventListener('enterpictureinpicture', handlePipEnter);
    videoEl.addEventListener('leavepictureinpicture', handlePipLeave);
    
    return () => {
      videoEl.removeEventListener('enterpictureinpicture', handlePipEnter);
      videoEl.removeEventListener('leavepictureinpicture', handlePipLeave);
    };
  }, [activeCall]);

  // Assign both refs to the container - must be before early return
  const setContainerRef = useCallback((el: HTMLDivElement | null) => {
    (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = el;
    (dragRef as React.MutableRefObject<HTMLDivElement | null>).current = el;
  }, [dragRef]);

  if (!activeCall || activeCall.state === CallState.Idle || activeCall.state === CallState.Ended) {
    return null;
  }

  // If docked and not fullscreen, don't render as overlay - it's rendered in the layout instead
  if (isDocked && !isFullscreen) {
    return null;
  }

  const isConnecting = activeCall.state === CallState.Connecting;
  const isVideoCall = activeCall.callType === CallType.Video;

  // Get room for participant info
  const room = mx.getRoom(activeCall.roomId);

  // Build participant list from Matrix state events (provides real user IDs)
  // callMembers has the actual Matrix user IDs from org.matrix.msc3401.call.member events
  const allParticipants = callMembers.map((m) => m.userId);
  const participantCount = allParticipants.length;

  /**
   * Gets avatar URL for a participant
   */
  const getParticipantAvatar = (participantId: string): string | undefined => {
    if (!room) return undefined;
    const mxcUrl = getMemberAvatarMxc(room, participantId);
    if (!mxcUrl) return undefined;
    return mxcUrlToHttp(mx, mxcUrl, useAuthentication, 48, 48, 'crop') ?? undefined;
  };

  /**
   * Gets display name for a participant
   */
  const getParticipantName = (participantId: string): string => {
    if (!room) return getMxIdLocalPart(participantId) ?? participantId;
    return getMemberDisplayName(room, participantId) ?? getMxIdLocalPart(participantId) ?? participantId;
  };

  /**
   * Checks if a participant is currently speaking
   * Handles mapping between Matrix user IDs and LiveKit identities
   */
  const isParticipantSpeaking = (userId: string): boolean => {
    if (!activeCall) return false;
    
    // Direct check for Matrix user ID (works for local user)
    if (activeCall.activeSpeakers.has(userId)) return true;
    
    // For remote users, activeSpeakers contains LiveKit identities (deviceId_timestamp)
    // We need to find the deviceId for this user and check if any matching identity is speaking
    const member = callMembers.find((m) => m.userId === userId);
    if (!member) return false;
    
    // Check if any active speaker has this user's deviceId
    return Array.from(activeCall.activeSpeakers).some((speakerId) => {
      const underscoreIndex = speakerId.lastIndexOf('_');
      const deviceId = underscoreIndex > 0 ? speakerId.substring(0, underscoreIndex) : speakerId;
      return deviceId === member.deviceId;
    });
  };

  /**
   * Checks if a participant is currently muted
   * Handles mapping between Matrix user IDs and LiveKit identities
   */
  const isParticipantMuted = (userId: string): boolean => {
    if (!activeCall) return false;
    
    // For local user, check activeCall.isMuted
    if (userId === myUserId) return activeCall.isMuted;
    
    // For remote users, mutedParticipants contains LiveKit identities (deviceId_timestamp)
    // We need to find the deviceId for this user and check if any matching identity is muted
    const member = callMembers.find((m) => m.userId === userId);
    if (!member) return false;
    
    // Check if any muted participant has this user's deviceId
    return Array.from(activeCall.mutedParticipants).some((mutedId) => {
      const underscoreIndex = mutedId.lastIndexOf('_');
      const deviceId = underscoreIndex > 0 ? mutedId.substring(0, underscoreIndex) : mutedId;
      return deviceId === member.deviceId;
    });
  };

  const handleEndCall = () => {
    endCall();
  };

  const handleToggleMute = () => {
    toggleMute();
  };

  const handleToggleDeafen = () => {
    toggleDeafen();
  };

  const handleToggleVideo = () => {
    toggleVideo();
  };

  const handleToggleScreenShare = async () => {
    if (isTogglingScreenShare) return;
    setIsTogglingScreenShare(true);
    try {
      await toggleScreenShare();
    } finally {
      setIsTogglingScreenShare(false);
    }
  };

  // Determine container class based on state
  const containerClass = [
    css.CallOverlayContainer,
    isFullscreen && css.CallOverlayFullscreen,
    isDocked && !isFullscreen && css.CallOverlayDocked,
    isDragging && css.CallOverlayDragging,
  ].filter(Boolean).join(' ');

  return (
    <>
      {/* Dock indicators shown when dragging near dock zones */}
      {nearDockZone === 'right' && !isDocked && <div className={css.DockIndicator} />}
      {nearDockZone === 'sidebar' && !isDocked && <div className={css.SidebarDockIndicator} />}
      <div
        ref={setContainerRef}
        className={containerClass}
        style={!isFullscreen && !isDocked ? {
          transform: `translate(${position.x}px, ${position.y}px)`,
        } : undefined}
      >
        {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
      <div
        className={css.CallOverlayHeader}
        onMouseDown={handleMouseDown}
        style={{ cursor: isDragging ? 'grabbing' : 'grab' }}
      >
        <Box alignItems="Center" gap="200" style={{ minWidth: 0, flex: 1 }}>
          <Icon
            size="200"
            src={isVideoCall ? Icons.VideoCamera : Icons.Phone}
          />
          <Text size="T300" truncate>
            {isConnecting ? 'Connecting...' : `In Call - ${room?.name ?? 'Unknown Room'}`}
            {selectedRoomId === activeCall.roomId && (
              <TooltipProvider
                position="Top"
                offset={4}
                tooltip={<Tooltip><Text>Current Room</Text></Tooltip>}
              >
                {(triggerRef) => (
                  <Icon 
                    ref={triggerRef}
                    size="100" 
                    src={Icons.Check} 
                    style={{ marginLeft: '4px', opacity: 0.7, verticalAlign: 'middle' }} 
                  />
                )}
              </TooltipProvider>
            )}
          </Text>
          {unread && unread.total > 0 && (
            <UnreadBadge highlight={unread.highlight > 0} count={unread.total} />
          )}
          {selectedRoomId !== activeCall.roomId && (
            <TooltipProvider
              position="Top"
              offset={4}
              tooltip={<Tooltip><Text>Go to Room</Text></Tooltip>}
            >
              {(triggerRef) => (
                <IconButton 
                  ref={triggerRef} 
                  onClick={() => navigateRoom(activeCall.roomId)} 
                  variant="Secondary" 
                  size="300"
                  style={{ padding: '2px', marginLeft: '4px' }}
                >
                  <Icon size="100" src={Icons.Link} />
                </IconButton>
              )}
            </TooltipProvider>
          )}
        </Box>
        <Box alignItems="Center" gap="100">
          <Text size="T200" className={css.CallDuration}>
            {formatDuration(duration)}
          </Text>
          {isVideoCall && (
            <>
              <TooltipProvider
                position="Top"
                offset={4}
                tooltip={<Tooltip><Text>Pop Out</Text></Tooltip>}
              >
                {(triggerRef) => (
                  <IconButton ref={triggerRef} onClick={handlePopOut} variant="Secondary" size="300">
                    <Icon size="200" src={Icons.External} />
                  </IconButton>
                )}
              </TooltipProvider>
              <TooltipProvider
                position="Top"
                offset={4}
                tooltip={<Tooltip><Text>{isPip ? 'Exit PiP' : 'Picture-in-Picture'}</Text></Tooltip>}
              >
                {(triggerRef) => (
                  <IconButton ref={triggerRef} onClick={handleTogglePip} variant="Secondary" size="300">
                    <Box as="span" style={{ width: '1rem', height: '1rem', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                      <PipIcon />
                    </Box>
                  </IconButton>
                )}
              </TooltipProvider>
              <TooltipProvider
                position="Top"
                offset={4}
                tooltip={<Tooltip><Text>{isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}</Text></Tooltip>}
              >
                {(triggerRef) => (
                  <IconButton ref={triggerRef} onClick={handleToggleFullscreen} variant="Secondary" size="300">
                    <Box as="span" style={{ width: '1rem', height: '1rem', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                      {isFullscreen ? <ExitFullscreenIcon /> : <FullscreenIcon />}
                    </Box>
                  </IconButton>
                )}
              </TooltipProvider>
            </>
          )}
        </Box>
      </div>

      {/* Participants Section */}
      <div className={css.ParticipantsSection}>
        <button
          type="button"
          className={css.ParticipantsHeader}
          onClick={() => setShowParticipants(!showParticipants)}
        >
          <Box alignItems="Center" gap="200">
            <Icon size="100" src={Icons.User} />
            <Text size="T200" className={css.ParticipantsHeaderText}>
              {participantCount} {participantCount === 1 ? 'Participant' : 'Participants'}
            </Text>
          </Box>
          <Icon
            size="100"
            src={showParticipants ? Icons.ChevronTop : Icons.ChevronBottom}
          />
        </button>
        {showParticipants && (
          <div className={css.ParticipantsList}>
            {allParticipants.map((participantId) => {
              const isMe = participantId === myUserId;
              const displayName = getParticipantName(participantId);
              const avatarUrl = getParticipantAvatar(participantId);
              const isSpeaking = isParticipantSpeaking(participantId);
              const isMuted = isParticipantMuted(participantId);
              // Get screenshares for this participant
              const participantScreenShares = allScreenShares.filter(
                (share) => share.participantId === participantId
              );
              
              return (
                <div key={participantId} className={css.ParticipantItem}>
                    <div className={`${css.ParticipantAvatarWrapper} ${isSpeaking ? css.ParticipantSpeaking : ''}`}>
                      <UserAvatar
                        className={css.ParticipantAvatar}
                        userId={participantId}
                        src={avatarUrl}
                        alt={displayName}
                        renderFallback={() => (
                          <Text size="H6" style={{ color: color.Surface.Container }}>
                            {displayName.charAt(0).toUpperCase()}
                          </Text>
                        )}
                      />
                    </div>
                    <Box direction="Column" grow="Yes" style={{ minWidth: 0 }}>
                      <Text size="T200" className={css.ParticipantName} truncate>
                        {displayName}
                        {isMe && <span className={css.YouBadge}> (You)</span>}
                      </Text>
                      <Text size="T200" className={css.ParticipantId} truncate>
                        {participantId}
                      </Text>
                    </Box>
                    {isMuted && (
                      <Icon size="100" src={Icons.MicMute} className={css.ParticipantMutedIcon} />
                    )}
                    {participantScreenShares.length > 0 && (
                      <TooltipProvider
                        position="Top"
                        offset={4}
                        tooltip={<Tooltip><Text>{collapsedScreenShares.has(participantScreenShares[0].id) ? 'Show Screen' : 'Hide Screen'}</Text></Tooltip>}
                      >
                        {(triggerRef) => (
                          <IconButton 
                            ref={triggerRef} 
                            onClick={() => {
                              setCollapsedScreenShares((prev) => {
                                const next = new Set(prev);
                                // Toggle all screenshares for this participant
                                participantScreenShares.forEach((share) => {
                                  if (next.has(share.id)) {
                                    next.delete(share.id);
                                  } else {
                                    next.add(share.id);
                                  }
                                });
                                return next;
                              });
                            }}
                            variant="Secondary" 
                            size="300"
                            style={{ padding: '2px' }}
                          >
                            <ScreenShareIcon />
                          </IconButton>
                        )}
                      </TooltipProvider>
                    )}
                  </div>
              );
            })}
          </div>
        )}
      </div>

      {/* Screen shares rendered outside participant list */}
      {allScreenShares
        .filter((share) => !collapsedScreenShares.has(share.id))
        .map((share) => (
          <ScreenShareItem key={share.id} share={share} />
        ))}

      {isVideoCall && (
        <div className={css.VideoContainer}>
          {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
          <video
            ref={remoteVideoRef}
            autoPlay
            playsInline
            controls
            className={css.VideoElement}
          />
          <div className={css.LocalVideo}>
            {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
            <video
              ref={localVideoRef}
              autoPlay
              playsInline
              muted
              controls
              className={css.VideoElement}
            />
          </div>
        </div>
      )}

      <div className={css.CallOverlayControls}>
        <TooltipProvider
          position="Top"
          offset={4}
          tooltip={
            <Tooltip>
              <Text>{activeCall.isMuted ? 'Unmute' : 'Mute'}</Text>
            </Tooltip>
          }
        >
          {(triggerRef) => (
            <IconButton
              ref={triggerRef}
              onClick={handleToggleMute}
              variant={activeCall.isMuted ? 'Critical' : 'Secondary'}
            >
              <Icon
                size="300"
                src={activeCall.isMuted ? Icons.MicMute : Icons.Mic}
              />
            </IconButton>
          )}
        </TooltipProvider>

        <TooltipProvider
          position="Top"
          offset={4}
          tooltip={
            <Tooltip>
              <Text>{activeCall.isDeafened ? 'Undeafen' : 'Deafen'}</Text>
            </Tooltip>
          }
        >
          {(triggerRef) => (
            <IconButton
              ref={triggerRef}
              onClick={handleToggleDeafen}
              variant={activeCall.isDeafened ? 'Critical' : 'Secondary'}
            >
              <Icon
                size="300"
                src={activeCall.isDeafened ? Icons.VolumeMute : Icons.VolumeHigh}
              />
            </IconButton>
          )}
        </TooltipProvider>

        {isVideoCall && (
          <TooltipProvider
            position="Top"
            offset={4}
            tooltip={
              <Tooltip>
                <Text>{activeCall.isVideoEnabled ? 'Turn Off Camera' : 'Turn On Camera'}</Text>
              </Tooltip>
            }
          >
            {(triggerRef) => (
              <IconButton
                ref={triggerRef}
                onClick={handleToggleVideo}
                variant={activeCall.isVideoEnabled ? 'Secondary' : 'Critical'}
              >
                <Icon
                  size="300"
                  src={activeCall.isVideoEnabled ? Icons.VideoCamera : Icons.VideoCameraMute}
                />
              </IconButton>
            )}
          </TooltipProvider>
        )}

        <TooltipProvider
          position="Top"
          offset={4}
          tooltip={
            <Tooltip>
              <Text>{activeCall.isScreenSharing ? 'Stop Screen Share' : 'Share Screen'}</Text>
            </Tooltip>
          }
        >
          {(triggerRef) => (
            <IconButton
              ref={triggerRef}
              onClick={handleToggleScreenShare}
              variant={activeCall.isScreenSharing ? 'Primary' : 'Secondary'}
              disabled={isTogglingScreenShare}
            >
              <Box
                as="span"
                style={{ 
                  width: '1.5rem', 
                  height: '1.5rem', 
                  display: 'flex', 
                  alignItems: 'center', 
                  justifyContent: 'center' 
                }}
              >
                {activeCall.isScreenSharing ? <ScreenShareOffIcon /> : <ScreenShareIcon />}
              </Box>
            </IconButton>
          )}
        </TooltipProvider>

        <TooltipProvider
          position="Top"
          offset={4}
          tooltip={
            <Tooltip>
              <Text>End Call</Text>
            </Tooltip>
          }
        >
          {(triggerRef) => (
            <IconButton
              ref={triggerRef}
              onClick={handleEndCall}
              variant="Critical"
            >
              <Icon size="300" src={Icons.Phone} />
            </IconButton>
          )}
        </TooltipProvider>
      </div>
    </div>
    </>
  );
}
