import { useState, useRef, useEffect, useMemo } from 'react';
import { getWorkNotes, createWorkNoteMultipart, getWorkflowDetails, downloadWorkNoteAttachment, getWorkNoteAttachmentPreviewUrl, addSpectator, addApproverAtLevel } from '@/services/workflowApi';
import { sanitizeHTML } from '@/utils/sanitizer';
import { getPublicConfigurations, AdminConfiguration } from '@/services/adminApi';
import { toast } from 'sonner';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { getSocket, joinRequestRoom, leaveRequestRoom } from '@/utils/socket';
import { useParams } from 'react-router-dom';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Badge } from '@/components/ui/badge';
import { Textarea } from '@/components/ui/textarea';
import { FilePreview } from '@/components/common/FilePreview';
import { ActionStatusModal } from '@/components/common/ActionStatusModal';
import { AddSpectatorModal } from '@/components/participant/AddSpectatorModal';
import { AddApproverModal } from '@/components/participant/AddApproverModal';
import { formatDateTime } from '@/utils/dateFormatter';
import {
  Send,
  Smile,
  Paperclip,
  Users,
  FileText,
  Download,
  Eye,
  MessageSquare,
  Clock,
  Search,
  AtSign,
  Plus,
  Activity,
  Flag,
  X,
  FileSpreadsheet,
  Image,
  UserPlus,
  AlertCircle
} from 'lucide-react';

interface Message {
  id: string;
  user: {
    name: string;
    avatar: string;
    role: string;
  };
  content: string;
  timestamp: string;
  mentions?: string[];
  isSystem?: boolean;
  attachments?: {
    name: string;
    url: string;
    type: string;
  }[];
  reactions?: {
    emoji: string;
    users: string[];
  }[];
  isHighPriority?: boolean;
  isCurrentUser?: boolean;
}

interface Participant {
  name: string;
  avatar: string;
  role: string;
  status: 'online' | 'away' | 'offline';
  email: string;
  lastSeen?: string;
  permissions: string[];
}

interface WorkNoteChatProps {
  requestId: string;
  messages?: any[]; // optional external messages
  onSend?: (messageHtml: string, files: File[]) => Promise<void> | void;
  skipSocketJoin?: boolean; // Set to true when embedded in RequestDetail (to avoid double join)
  requestTitle?: string; // Optional title for display
  onAttachmentsExtracted?: (attachments: any[]) => void; // Callback to pass attachments to parent
  isInitiator?: boolean; // Whether current user is the initiator
  isSpectator?: boolean; // Whether current user is a spectator (view-only)
  currentLevels?: any[]; // Current approval levels for add approver modal
  onAddApprover?: (email: string, tatHours: number, level: number) => Promise<void>; // Callback to add approver
  maxApprovalLevels?: number; // Maximum allowed approval levels from system policy
  onPolicyViolation?: (violations: Array<{ type: string; message: string; currentValue?: number; maxValue?: number }>) => void; // Callback for policy violations
}

// All data is now fetched from backend - no hardcoded mock data

// Utility functions
const getStatusColor = (status: string) => {
  switch (status) {
    case 'online': return 'bg-green-500';
    case 'away': return 'bg-yellow-500';
    case 'offline': return 'bg-gray-400';
    default: return 'bg-gray-400';
  }
};

const getStatusText = (status: string) => {
  switch (status) {
    case 'online': return 'Online';
    case 'away': return 'Away';
    case 'offline': return 'Offline';
    default: return 'Unknown';
  }
};

const formatMessage = (content: string) => {
  // Enhanced mention highlighting - Blue color with extra bold font for high visibility
  const formattedContent = content
    .replace(/@(\w+(?:\s+\w+)?)(?=\s|$|[.,!?;:]|@)/g, (match, mention, offset, string) => {
      const afterPos = offset + match.length;
      const afterChar = string[afterPos];

      // Valid mention if followed by: space, punctuation, @ (another mention), or end of string
      if (!afterChar || /\s|[.,!?;:]|@/.test(afterChar)) {
        return '<span class="inline-flex items-center px-2.5 py-0.5 rounded-md bg-blue-50 text-blue-800 font-black text-base border-2 border-blue-400 shadow-sm">@' + mention + '</span>';
      }

      return match;
    })
    .replace(/\n/g, '<br />');

  return sanitizeHTML(formattedContent);
};

const formatFileSize = (bytes: number): string => {
  if (bytes < 1024) return bytes + ' B';
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
  return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
};

// File icon components using Lucide React
const FileIcon = ({ type }: { type: string }) => {
  const iconClass = "w-4 h-4";
  const lowerType = type.toLowerCase();
  if (lowerType.includes('pdf')) return <FileText className={`${iconClass} text-red-600`} />;
  if (lowerType.includes('excel') || lowerType.includes('spreadsheet') || lowerType.includes('xlsx')) return <FileSpreadsheet className={`${iconClass} text-green-600`} />;
  if (lowerType.includes('powerpoint') || lowerType.includes('presentation') || lowerType.includes('pptx')) return <FileText className={`${iconClass} text-orange-600`} />;
  if (lowerType.includes('word') || lowerType.includes('document') || lowerType.includes('docx')) return <FileText className={`${iconClass} text-blue-600`} />;
  if (lowerType.includes('image') || lowerType.includes('png') || lowerType.includes('jpg') || lowerType.includes('jpeg') || lowerType.includes('gif') || lowerType.includes('webp')) return <Image className={`${iconClass} text-purple-600`} />;
  return <Paperclip className={`${iconClass} text-gray-600`} />;
};

export function WorkNoteChat({ requestId, messages: externalMessages, onSend, skipSocketJoin = false, requestTitle, onAttachmentsExtracted, isInitiator = false, isSpectator = false, currentLevels = [], onAddApprover, maxApprovalLevels, onPolicyViolation }: WorkNoteChatProps) {
  const routeParams = useParams<{ requestId: string }>();
  const effectiveRequestId = requestId || routeParams.requestId || '';
  const [message, setMessage] = useState('');
  const [searchTerm, setSearchTerm] = useState('');
  const [showEmojiPicker, setShowEmojiPicker] = useState(false);
  const [messages, setMessages] = useState<Message[]>([]);
  const [showSidebar, setShowSidebar] = useState(false);
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
  const [currentUserId, setCurrentUserId] = useState<string | null>(null);
  const [previewFile, setPreviewFile] = useState<{ fileName: string; fileType: string; fileUrl: string; fileSize?: number; attachmentId?: string } | null>(null);
  const [showAddSpectatorModal, setShowAddSpectatorModal] = useState(false);
  const [showAddApproverModal, setShowAddApproverModal] = useState(false);
  const [showActionStatusModal, setShowActionStatusModal] = useState(false);
  const [actionStatus, setActionStatus] = useState<{ success: boolean; title: string; message: string }>({
    success: true,
    title: '',
    message: ''
  });
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const socketRef = useRef<any>(null);
  const participantsLoadedRef = useRef(false);

  // Document policy state
  const [documentPolicy, setDocumentPolicy] = useState<{
    maxFileSizeMB: number;
    allowedFileTypes: string[];
  }>({
    maxFileSizeMB: 10,
    allowedFileTypes: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'jpg', 'jpeg', 'png', 'gif']
  });

  // Document validation error modal
  const [documentErrorModal, setDocumentErrorModal] = useState<{
    open: boolean;
    errors: Array<{ fileName: string; reason: string }>;
  }>({
    open: false,
    errors: []
  });

  // Component render - logging removed for performance

  // Get request info (from props, all data comes from backend now)
  const requestInfo = useMemo(() => {
    return {
      id: effectiveRequestId,
      title: requestTitle || 'Request Details',
    };
  }, [effectiveRequestId, requestTitle]);

  const [participants, setParticipants] = useState<Participant[]>([]);
  const onlineParticipants = participants.filter(p => p.status === 'online');
  const filteredMessages = messages.filter(msg =>
    msg.content.toLowerCase().includes(searchTerm.toLowerCase()) ||
    msg.user.name.toLowerCase().includes(searchTerm.toLowerCase())
  );

  // Determine if current user is a spectator (when not passed as prop)
  const effectiveIsSpectator = useMemo(() => {
    // If isSpectator is explicitly passed as prop, use it
    if (isSpectator !== undefined) {
      return isSpectator;
    }
    // Otherwise, determine from participants list
    if (!currentUserId || participants.length === 0) {
      return false;
    }
    return participants.some((p: any) => {
      const pUserId = (p as any).userId || (p as any).user_id;
      const pRole = (p.role || '').toString().toUpperCase();
      const pType = ((p as any).participantType || (p as any).participant_type || '').toString().toUpperCase();
      return pUserId === currentUserId && (pRole === 'SPECTATOR' || pType === 'SPECTATOR');
    });
  }, [isSpectator, currentUserId, participants]);

  // Log when participants change - logging removed for performance
  useEffect(() => {
    // Participants state changed - logging removed
  }, [participants]);

  // Load initial messages from backend (only if not provided by parent)
  useEffect(() => {
    if (!effectiveRequestId || !currentUserId) return;
    if (externalMessages) return; // Skip if parent is providing messages

    (async () => {
      try {
        const rows = await getWorkNotes(effectiveRequestId);
        const mapped = Array.isArray(rows) ? rows.map((m: any) => {
          const noteUserId = m.userId || m.user_id;
          return {
            id: m.noteId || m.id || String(Math.random()),
            user: {
              name: m.userName || 'User',
              avatar: (m.userName || 'U').slice(0, 2).toUpperCase(),
              role: m.userRole || 'Participant'
            },
            content: m.message || '',
            timestamp: m.createdAt || new Date().toISOString(),
            isCurrentUser: noteUserId === currentUserId,
            attachments: Array.isArray(m.attachments) ? m.attachments.map((a: any) => ({
              attachmentId: a.attachmentId || a.attachment_id,
              name: a.fileName || a.file_name || a.name,
              fileName: a.fileName || a.file_name || a.name,
              url: a.storageUrl || a.storage_url || a.url || '#',
              type: a.fileType || a.file_type || a.type || 'file',
              fileType: a.fileType || a.file_type || a.type || 'file',
              fileSize: a.fileSize || a.file_size
            })) : undefined
          };
        }) : [];
        setMessages(mapped as any);
      } catch (error) {
        console.error('[WorkNoteChat] Failed to load messages:', error);
      }
    })();
  }, [effectiveRequestId, currentUserId, externalMessages]);

  // Extract all shared files from messages with attachments
  const sharedFiles = useMemo(() => {
    const files: any[] = [];
    messages.forEach((msg) => {
      if (msg.attachments && msg.attachments.length > 0) {
        msg.attachments.forEach((attachment: any) => {
          files.push({
            attachmentId: attachment.attachmentId || attachment.attachment_id,
            name: attachment.fileName || attachment.file_name || attachment.name || 'Untitled',
            fileName: attachment.fileName || attachment.file_name || attachment.name || 'Untitled',
            size: attachment.fileSize || attachment.file_size,
            type: attachment.fileType || attachment.file_type || attachment.type || 'file',
            uploadedBy: msg.user.name,
            uploadedAt: msg.timestamp,
            url: attachment.storageUrl || attachment.storage_url || attachment.url || '#'
          });
        });
      }
    });
    return files;
  }, [messages]);

  // Notify parent when attachments change
  useEffect(() => {
    if (onAttachmentsExtracted && sharedFiles.length >= 0) {
      onAttachmentsExtracted(sharedFiles);
    }
  }, [sharedFiles, onAttachmentsExtracted]);

  // Get all existing participants for validation
  const existingParticipants = useMemo(() => {
    return participants.map(p => ({
      email: (p.email || '').toLowerCase(),
      participantType: p.role === 'Initiator' ? 'INITIATOR' :
        p.role === 'Approver' ? 'APPROVER' :
          p.role === 'Spectator' ? 'SPECTATOR' :
            'PARTICIPANT',
      name: p.name
    }));
  }, [participants]);

  // Helper to format participant role (same as formatParticipantRole but for use in mapping)
  const getFormattedRole = (role: string | undefined): string => {
    if (!role) return 'Participant';
    const roleUpper = role.toUpperCase();
    switch (roleUpper) {
      case 'INITIATOR': return 'Initiator';
      case 'APPROVER': return 'Approver';
      case 'SPECTATOR': return 'Spectator';
      default: return role.charAt(0).toUpperCase() + role.slice(1).toLowerCase();
    }
  };

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  };

  useEffect(() => {
    scrollToBottom();
  }, [messages]);

  // Helper to format participant role
  const formatParticipantRole = (type: string): string => {
    const typeUpper = type.toUpperCase();
    switch (typeUpper) {
      case 'INITIATOR': return 'Initiator';
      case 'APPROVER': return 'Approver';
      case 'SPECTATOR': return 'Spectator';
      default: return type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();
    }
  };

  // Load participants from backend workflow details
  useEffect(() => {
    // Skip if participants are already loaded (prevents resetting on tab switch)
    if (participantsLoadedRef.current) {
      return;
    }

    if (!effectiveRequestId) {
      return;
    }

    (async () => {
      try {
        const details = await getWorkflowDetails(effectiveRequestId);
        const rows = Array.isArray(details?.participants) ? details.participants : [];

        if (rows.length === 0) {
          return;
        }

        const mapped: Participant[] = rows.map((p: any) => {
          const participantType = p.participantType || p.participant_type || 'participant';
          const userId = p.userId || p.user_id || '';
          return {
            name: p.userName || p.user_name || p.user_email || p.userEmail || 'User',
            avatar: (p.userName || p.user_name || p.user_email || 'U').toString().split(' ').map((s: string) => s[0]).filter(Boolean).join('').slice(0, 2).toUpperCase(),
            role: formatParticipantRole(participantType.toString()),
            status: 'offline', // will be updated by presence events
            email: p.userEmail || p.user_email || '',
            permissions: ['read', 'write', 'mention'], // default permissions, can be enhanced later
            userId: userId // store userId for presence matching
          } as any;
        });

        // Participants loaded - logging removed
        participantsLoadedRef.current = true;
        setParticipants(mapped);

        // Request online users immediately after setting participants with retries
        let retryCount = 0;
        const maxRetries = 3;
        const requestOnlineUsers = () => {
          if (socketRef.current && socketRef.current.connected) {
            // Requesting online users - logging removed
            socketRef.current.emit('request:online-users', { requestId: effectiveRequestId });
            retryCount++;
            // Retry a few times to ensure we get the list
            if (retryCount < maxRetries) {
              setTimeout(requestOnlineUsers, 500);
            }
          } else {
            // Socket not ready - retrying silently
            retryCount++;
            if (retryCount < maxRetries) {
              setTimeout(requestOnlineUsers, 200);
            }
          }
        };
        setTimeout(requestOnlineUsers, 100); // Initial delay to ensure state is updated

      } catch (error) {
        console.error('[WorkNoteChat] ❌ Failed to load participants:', error);
      }
    })();
  }, [effectiveRequestId]);

  // Reset participants loaded flag when request changes
  useEffect(() => {
    return () => {
      // Don't reset on unmount, only on request change
      if (effectiveRequestId) {
        participantsLoadedRef.current = false;
      }
    };
  }, [effectiveRequestId]);

  // Load current user ID on mount
  useEffect(() => {
    const userDataStr = localStorage.getItem('userData');
    if (userDataStr) {
      try {
        const userData = JSON.parse(userDataStr);
        const userId = userData?.id || userData?.userId || userData?.user_id || null;
        setCurrentUserId(userId);
      } catch (err) {
        console.error('[WorkNoteChat] Failed to parse userData:', err);
      }
    }
  }, []);

  // Fetch document policy on mount
  useEffect(() => {
    const loadDocumentPolicy = async () => {
      try {
        const configs = await getPublicConfigurations('DOCUMENT_POLICY');
        const configMap: Record<string, string> = {};
        configs.forEach((c: AdminConfiguration) => {
          configMap[c.configKey] = c.configValue;
        });

        const maxFileSizeMB = parseInt(configMap['MAX_FILE_SIZE_MB'] || '10');
        const allowedFileTypesStr = configMap['ALLOWED_FILE_TYPES'] || 'pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif';
        const allowedFileTypes = allowedFileTypesStr.split(',').map(ext => ext.trim().toLowerCase());

        setDocumentPolicy({
          maxFileSizeMB,
          allowedFileTypes
        });
      } catch (error) {
        console.error('Failed to load document policy:', error);
        // Use defaults if loading fails
      }
    };

    loadDocumentPolicy();
  }, []);

  // Realtime updates via Socket.IO (standalone usage OR when embedded in RequestDetail)
  useEffect(() => {
    if (!currentUserId) return; // Wait for currentUserId to be loaded

    let joinedId = effectiveRequestId;
    (async () => {
      try {
        const details = await getWorkflowDetails(effectiveRequestId);
        if (details?.workflow?.requestId) {
          joinedId = details.workflow.requestId; // join by UUID to match server emits
        }
      } catch { }
      try {
        // Get socket using helper function (handles VITE_BASE_URL or VITE_API_BASE_URL)
        const s = getSocket(); // Uses getSocketBaseUrl() helper internally

        // Only join room if not skipped (standalone mode)
        if (!skipSocketJoin) {
          joinRequestRoom(s, joinedId, currentUserId);

          // Mark self as online immediately after joining room
          setParticipants(prev => {
            const updated = prev.map(p =>
              (p as any).userId === currentUserId ? { ...p, status: 'online' as const } : p
            );
            return updated;
          });
        } else {
          // Still mark self as online even when embedded (parent handles socket but we track presence)
          setParticipants(prev => {
            const updated = prev.map(p =>
              (p as any).userId === currentUserId ? { ...p, status: 'online' as const } : p
            );
            return updated;
          });
        }

        // Handle new work notes
        const noteHandler = (payload: any) => {
          const n = payload?.note || payload;
          if (!n) {
            return;
          }

          const noteId = n.noteId || n.id;

          // Prevent duplicates: check if message with same noteId already exists
          setMessages(prev => {
            if (prev.some(m => m.id === noteId)) {
              return prev; // Already exists, don't add
            }

            const userName = n.userName || n.user_name || 'User';
            const userRole = n.userRole || n.user_role; // Get role directly from backend
            const participantRole = getFormattedRole(userRole);
            const noteUserId = n.userId || n.user_id;

            const newMessage = {
              id: noteId || String(Date.now()),
              user: {
                name: userName,
                avatar: userName.split(' ').map((s: string) => s[0]).filter(Boolean).join('').slice(0, 2).toUpperCase(),
                role: participantRole
              },
              content: n.message || '',
              timestamp: n.createdAt || new Date().toISOString(),
              isCurrentUser: noteUserId === currentUserId,
              attachments: Array.isArray(n.attachments) ? n.attachments.map((a: any) => ({
                attachmentId: a.attachmentId || a.attachment_id,
                name: a.fileName || a.file_name || a.name,
                fileName: a.fileName || a.file_name || a.name,
                url: a.storageUrl || a.storage_url || a.url || '#',
                type: a.fileType || a.file_type || a.type || 'file',
                fileType: a.fileType || a.file_type || a.type || 'file',
                fileSize: a.fileSize || a.file_size
              })) : undefined
            } as any;

            return [...prev, newMessage];
          });
        };

        // Handle presence: user joined
        const presenceJoinHandler = (data: { userId: string; requestId: string }) => {
          setParticipants(prev => {
            if (prev.length === 0) {
              return prev;
            }
            const participant = prev.find(p => (p as any).userId === data.userId);
            if (!participant) {
              return prev;
            }
            const updated = prev.map(p =>
              (p as any).userId === data.userId ? { ...p, status: 'online' as const } : p
            );
            return updated;
          });
        };

        // Handle presence: user left
        const presenceLeaveHandler = (data: { userId: string; requestId: string }) => {
          // Never mark self as offline in own browser
          if (data.userId === currentUserId) {
            return;
          }

          setParticipants(prev => {
            if (prev.length === 0) {
              return prev;
            }
            const participant = prev.find(p => (p as any).userId === data.userId);
            if (!participant) {
              return prev;
            }
            const updated = prev.map(p =>
              (p as any).userId === data.userId ? { ...p, status: 'offline' as const } : p
            );
            return updated;
          });
        };

        // Handle initial online users list
        const presenceOnlineHandler = (data: { requestId: string; userIds: string[] }) => {
          setParticipants(prev => {
            if (prev.length === 0) {
              return prev;
            }

            // Updating online status - logging removed
            const updated = prev.map(p => {
              const pUserId = (p as any).userId || '';
              const isCurrentUserSelf = pUserId === currentUserId;

              // Always keep self as online in own browser
              if (isCurrentUserSelf) {
                return { ...p, status: 'online' as const };
              }

              const isOnline = data.userIds.includes(pUserId);
              return { ...p, status: isOnline ? 'online' as const : 'offline' as const };
            });
            // Online status updated - logging removed
            return updated;
          });
        };

        // Handle socket reconnection
        const connectHandler = () => {
          // Mark self as online on connection
          setParticipants(prev => {
            const updated = prev.map(p =>
              (p as any).userId === currentUserId ? { ...p, status: 'online' as const } : p
            );
            return updated;
          });

          // Rejoin room if needed
          if (!skipSocketJoin) {
            joinRequestRoom(s, joinedId, currentUserId);
          }

          // Request online users on connection with multiple retries
          if (participantsLoadedRef.current) {
            s.emit('request:online-users', { requestId: joinedId });

            // Send additional requests with delay to ensure we get the response
            setTimeout(() => s.emit('request:online-users', { requestId: joinedId }), 300);
            setTimeout(() => s.emit('request:online-users', { requestId: joinedId }), 800);
          }
        };

        // Add error and diagnostic handlers
        const errorHandler = (error: any) => {
          console.error('[WorkNoteChat] ❌ Socket error:', error);
        };

        const disconnectHandler = (reason: string) => {
          console.warn('[WorkNoteChat] ⚠️ Socket disconnected:', reason);
          // Mark all other users as offline on disconnect
          setParticipants(prev => prev.map(p =>
            (p as any).userId === currentUserId ? p : { ...p, status: 'offline' as const }
          ));
        };

        // Debug: Log ALL events received from server for this request
        const anyEventHandler = (eventName: string) => {
          if (eventName.includes('presence') || eventName.includes('worknote') || eventName.includes('request')) {
            // Socket event received - logging removed
          }
        };

        // Attaching socket listeners - logging removed
        s.on('connect', connectHandler);
        s.on('disconnect', disconnectHandler);
        s.on('error', errorHandler);
        s.on('worknote:new', noteHandler);
        s.on('presence:join', presenceJoinHandler);
        s.on('presence:leave', presenceLeaveHandler);
        s.on('presence:online', presenceOnlineHandler);
        s.onAny(anyEventHandler); // Debug: catch all events
        // Socket listeners attached - logging removed

        // Store socket in ref for coordination with participants loading
        socketRef.current = s;

        // Always request online users after socket is ready
        if (s.connected) {
          if (participantsLoadedRef.current) {
            // Requesting online users with retries - logging removed
            s.emit('request:online-users', { requestId: joinedId });
            setTimeout(() => {
              s.emit('request:online-users', { requestId: joinedId });
            }, 300);
            setTimeout(() => {
              s.emit('request:online-users', { requestId: joinedId });
            }, 800);
            setTimeout(() => {
              s.emit('request:online-users', { requestId: joinedId });
            }, 1500);
          }
        }

        // cleanup
        const cleanup = () => {
          s.off('connect', connectHandler);
          s.off('disconnect', disconnectHandler);
          s.off('error', errorHandler);
          s.off('worknote:new', noteHandler);
          s.off('presence:join', presenceJoinHandler);
          s.off('presence:leave', presenceLeaveHandler);
          s.off('presence:online', presenceOnlineHandler);
          s.offAny(anyEventHandler);
          // Only leave room if we joined it
          if (!skipSocketJoin) {
            leaveRequestRoom(s, joinedId);
          }
          socketRef.current = null;
          // Socket cleanup completed - logging removed
        };
        (window as any).__wn_cleanup = cleanup;
      } catch { }
    })();
    return () => {
      try { (window as any).__wn_cleanup?.(); } catch { }
    };
  }, [effectiveRequestId, currentUserId, skipSocketJoin]);

  const handleSendMessage = async () => {
    if (message.trim() || selectedFiles.length > 0) {
      // Extract mentions from message
      const mentions = extractMentions(message);

      // Find mentioned user IDs from participants
      const mentionedUserIds = mentions
        .map(mentionedName => {
          const participant = participants.find(p =>
            p.name.toLowerCase().includes(mentionedName.toLowerCase())
          );
          return (participant as any)?.userId;
        })
        .filter(Boolean);

      // Message sending - logging removed

      const attachments = selectedFiles.map(file => ({
        name: file.name,
        url: URL.createObjectURL(file),
        type: file.type.split('/')[1] || 'file'
      }));

      const newMessage: Message = {
        id: Date.now().toString(),
        user: { name: 'You', avatar: 'YO', role: 'Current User' },
        content: message,
        timestamp: new Date().toLocaleString('en-US', {
          month: 'short',
          day: 'numeric',
          year: 'numeric',
          hour: 'numeric',
          minute: 'numeric',
          hour12: true
        }),
        mentions: mentions,
        isHighPriority: message.includes('!important') || message.includes('urgent'),
        attachments: attachments.length > 0 ? attachments : undefined,
        isCurrentUser: true
      };

      // If external onSend provided, delegate to caller (RequestDetail will POST and refresh)
      if (onSend) {
        try { await onSend(message, selectedFiles); } catch { /* ignore */ }
      } else {
        // Fallback: call backend directly with mentions
        try {
          await createWorkNoteMultipart(
            effectiveRequestId,
            {
              message,
              mentions: mentionedUserIds // Send mentioned user IDs to backend
            },
            selectedFiles
          );
          const rows = await getWorkNotes(effectiveRequestId);
          const mapped = Array.isArray(rows) ? rows.map((m: any) => {
            const noteUserId = m.userId || m.user_id;
            return {
              id: m.noteId || m.id || String(Math.random()),
              user: {
                name: m.userName || 'User',
                avatar: (m.userName || 'U').slice(0, 2).toUpperCase(),
                role: m.userRole || 'Participant'
              },
              content: m.message || '',
              timestamp: m.createdAt || new Date().toISOString(),
              isCurrentUser: noteUserId === currentUserId,
              attachments: Array.isArray(m.attachments) ? m.attachments.map((a: any) => ({
                attachmentId: a.attachmentId || a.attachment_id,
                name: a.fileName || a.file_name || a.name,
                fileName: a.fileName || a.file_name || a.name,
                url: a.storageUrl || a.storage_url || a.url || '#',
                type: a.fileType || a.file_type || a.type || 'file',
                fileType: a.fileType || a.file_type || a.type || 'file',
                fileSize: a.fileSize || a.file_size
              })) : undefined
            };
          }) : [];
          setMessages(mapped as any);
        } catch {
          setMessages(prev => [...prev, newMessage]);
        }
      }
      setMessage('');
      setSelectedFiles([]);
    }
  };

  // If external messages provided, map them to local shape without disturbing UI
  useEffect(() => {
    if (externalMessages && Array.isArray(externalMessages)) {
      try {
        const mapped: Message[] = externalMessages
          .filter((m: any) => {
            // Filter out TAT breach activities (not important for work notes chat)
            const activityType = (m.type || '').toLowerCase();
            return activityType !== 'sla_warning';
          })
          .map((m: any) => {
            // Check if this is an activity (system message) or work note
            const isActivity = m.type || m.activityType || m.isSystem;

            if (isActivity) {
              // Map activity to system message
              return {
                id: m.id || `activity-${m.timestamp || Date.now()}-${Math.random()}`,
                user: { name: 'System', avatar: 'SY', role: 'System' },
                content: m.details || m.action || m.content || '',
                timestamp: m.timestamp || m.createdAt || m.created_at || new Date().toISOString(),
                isSystem: true,
                isCurrentUser: false
              };
            } else {
              // Map work note
              const userName = m.userName || m.user_name || m.user?.name || 'User';
              const userRole = m.userRole || m.user_role;
              const participantRole = getFormattedRole(userRole);
              const noteUserId = m.userId || m.user_id;

              return {
                id: m.noteId || m.note_id || m.id || String(Math.random()),
                user: {
                  name: userName,
                  avatar: userName.split(' ').map((s: string) => s[0]).filter(Boolean).join('').slice(0, 2).toUpperCase(),
                  role: participantRole
                },
                content: m.message || m.content || '',
                timestamp: m.createdAt || m.created_at || m.timestamp || new Date().toISOString(),
                isSystem: false,
                attachments: Array.isArray(m.attachments) ? m.attachments.map((a: any) => ({
                  attachmentId: a.attachmentId || a.attachment_id,
                  name: a.fileName || a.file_name || a.name,
                  fileName: a.fileName || a.file_name || a.name,
                  url: a.storageUrl || a.storage_url || a.url || '#',
                  type: a.fileType || a.file_type || a.type || 'file',
                  fileType: a.fileType || a.file_type || a.type || 'file',
                  fileSize: a.fileSize || a.file_size
                })) : undefined,
                isCurrentUser: noteUserId === currentUserId
              };
            }
          });

        // Sort by timestamp
        const sorted = mapped.sort((a, b) =>
          new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
        );

        // Messages mapped - logging removed
        setMessages(sorted);
      } catch (err) {
        console.error('[WorkNoteChat] Error mapping messages:', err);
      }
    } else {
      // Fallback: load from backend if parent didn't pass messages
      (async () => {
        try {
          const rows = await getWorkNotes(effectiveRequestId);
          const mapped = Array.isArray(rows) ? rows.map((m: any) => {
            const userName = m.userName || m.user_name || 'User';
            const userRole = m.userRole || m.user_role; // Get role directly from backend
            const participantRole = getFormattedRole(userRole);
            const noteUserId = m.userId || m.user_id;

            return {
              id: m.noteId || m.note_id || m.id || String(Math.random()),
              user: {
                name: userName,
                avatar: userName.split(' ').map((s: string) => s[0]).filter(Boolean).join('').slice(0, 2).toUpperCase(),
                role: participantRole
              },
              content: m.message || '',
              timestamp: m.createdAt || m.created_at || new Date().toISOString(),
              attachments: Array.isArray(m.attachments) ? m.attachments.map((a: any) => ({
                attachmentId: a.attachmentId || a.attachment_id,
                name: a.fileName || a.file_name || a.name,
                fileName: a.fileName || a.file_name || a.name,
                url: a.storageUrl || a.storage_url || a.url || '#',
                type: a.fileType || a.file_type || a.type || 'file',
                fileType: a.fileType || a.file_type || a.type || 'file',
                fileSize: a.fileSize || a.file_size
              })) : undefined,
              isCurrentUser: noteUserId === currentUserId
            };
          }) : [];
          setMessages(mapped as any);
        } catch (err) {
          console.error('[WorkNoteChat] Error loading work notes:', err);
        }
      })();
    }
  }, [externalMessages, effectiveRequestId, participants]);

  const validateFile = (file: File): { valid: boolean; reason?: string } => {
    // Check file size
    const maxSizeBytes = documentPolicy.maxFileSizeMB * 1024 * 1024;
    if (file.size > maxSizeBytes) {
      return {
        valid: false,
        reason: `File size exceeds the maximum allowed size of ${documentPolicy.maxFileSizeMB}MB. Current size: ${(file.size / (1024 * 1024)).toFixed(2)}MB`
      };
    }

    // Check file extension
    const fileName = file.name.toLowerCase();
    const fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1);

    if (!documentPolicy.allowedFileTypes.includes(fileExtension)) {
      return {
        valid: false,
        reason: `File type "${fileExtension}" is not allowed. Allowed types: ${documentPolicy.allowedFileTypes.join(', ')}`
      };
    }

    return { valid: true };
  };

  const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (!e.target.files || e.target.files.length === 0) return;

    const filesArray = Array.from(e.target.files);

    // Validate all files
    const validationErrors: Array<{ fileName: string; reason: string }> = [];
    const validFiles: File[] = [];

    filesArray.forEach(file => {
      const validation = validateFile(file);
      if (!validation.valid) {
        validationErrors.push({
          fileName: file.name,
          reason: validation.reason || 'Unknown validation error'
        });
      } else {
        validFiles.push(file);
      }
    });

    // If there are validation errors, show modal
    if (validationErrors.length > 0) {
      setDocumentErrorModal({
        open: true,
        errors: validationErrors
      });
    }

    // Add only valid files
    if (validFiles.length > 0) {
      setSelectedFiles(prev => [...prev, ...validFiles]);
      if (validFiles.length < filesArray.length) {
        toast.warning(`${validFiles.length} of ${filesArray.length} file(s) were added. ${validationErrors.length} file(s) were rejected.`);
      } else {
        toast.success(`${validFiles.length} file(s) added successfully`);
      }
    }

    // Reset file input
    if (e.target) {
      e.target.value = '';
    }
  };

  const handleRemoveFile = (index: number) => {
    setSelectedFiles(prev => prev.filter((_, i) => i !== index));
    // Reset file input to allow reselecting the same file
    if (fileInputRef.current) {
      fileInputRef.current.value = '';
    }
  };

  const handleEmojiSelect = (emoji: string) => {
    setMessage(prev => prev + emoji);
    setShowEmojiPicker(false);
  };

  const handleAttachmentClick = () => {
    fileInputRef.current?.click();
  };

  // Handler for adding spectator
  const handleAddSpectator = async (email: string) => {
    try {
      await addSpectator(effectiveRequestId, email);
      // Refresh participants list
      const details = await getWorkflowDetails(effectiveRequestId);
      const rows = Array.isArray(details?.participants) ? details.participants : [];
      if (rows.length) {
        const mapped: Participant[] = rows.map((p: any) => {
          const participantType = p.participantType || p.participant_type || 'participant';
          const userId = p.userId || p.user_id || '';
          const userName = p.userName || p.user_name || p.userEmail || p.user_email || 'User';
          const userEmail = p.userEmail || p.user_email || '';
          const initials = userName.split(' ').map((s: string) => s[0]).filter(Boolean).join('').slice(0, 2).toUpperCase();

          return {
            name: userName,
            avatar: initials,
            role: formatParticipantRole(participantType),
            status: 'offline' as const, // Default to offline, will be updated by presence events
            email: userEmail,
            lastSeen: undefined,
            permissions: ['read'],
            userId
          };
        });
        setParticipants(mapped);

        // Request updated online users list from server to get correct status
        if (socketRef.current && socketRef.current.connected) {
          socketRef.current.emit('request:online-users', { requestId: effectiveRequestId });
        }
      }
      setShowAddSpectatorModal(false);
      // Show success modal
      setActionStatus({
        success: true,
        title: 'Spectator Added',
        message: 'Spectator added successfully. They can now view this request.'
      });
      setShowActionStatusModal(true);
    } catch (error: any) {
      console.error('Failed to add spectator:', error);
      // Show error modal
      setActionStatus({
        success: false,
        title: 'Failed to Add Spectator',
        message: error?.response?.data?.error || 'Failed to add spectator. Please try again.'
      });
      setShowActionStatusModal(true);
      throw error;
    }
  };

  // Handler for adding approver
  const handleAddApproverInternal = async (email: string, tatHours: number, level: number) => {
    if (onAddApprover) {
      // Use parent's handler if provided
      await onAddApprover(email, tatHours, level);
      setShowAddApproverModal(false);
    } else {
      // Fallback: call API directly
      try {
        await addApproverAtLevel(effectiveRequestId, email, tatHours, level);
        // Refresh participants list
        const details = await getWorkflowDetails(effectiveRequestId);
        const rows = Array.isArray(details?.participants) ? details.participants : [];
        if (rows.length) {
          const mapped: Participant[] = rows.map((p: any) => {
            const participantType = p.participantType || p.participant_type || 'participant';
            const userId = p.userId || p.user_id || '';
            const userName = p.userName || p.user_name || p.userEmail || p.user_email || 'User';
            const userEmail = p.userEmail || p.user_email || '';
            const initials = userName.split(' ').map((s: string) => s[0]).filter(Boolean).join('').slice(0, 2).toUpperCase();

            return {
              name: userName,
              avatar: initials,
              role: formatParticipantRole(participantType),
              status: 'offline' as const,
              email: userEmail,
              lastSeen: undefined,
              permissions: ['read'],
              userId
            };
          });
          setParticipants(mapped);

          if (socketRef.current && socketRef.current.connected) {
            socketRef.current.emit('request:online-users', { requestId: effectiveRequestId });
          }
        }
        setShowAddApproverModal(false);
        // Show success modal
        setActionStatus({
          success: true,
          title: 'Approver Added',
          message: `Approver added successfully at Level ${level} with ${tatHours}h TAT`
        });
        setShowActionStatusModal(true);
      } catch (error: any) {
        console.error('Failed to add approver:', error);
        // Show error modal
        setActionStatus({
          success: false,
          title: 'Failed to Add Approver',
          message: error?.response?.data?.error || 'Failed to add approver. Please try again.'
        });
        setShowActionStatusModal(true);
        throw error;
      }
    }
  };

  // Emoji picker data - Expanded collection
  const emojiList = [
    // Smileys & Emotions
    '😊', '😂', '🤣', '😁', '😃', '😄', '😅', '😆', '😉', '😌',
    '😍', '🥰', '😘', '😗', '😙', '😚', '🙂', '🤗', '🤩', '🤔',
    '🤨', '😐', '😑', '😶', '🙄', '😏', '😒', '🙃', '😬', '🤐',
    '😴', '😪', '😵', '🤯', '🤪', '😜', '😝', '😛', '🤤', '😋',
    '😎', '🤓', '🧐', '😕', '😟', '🙁', '☹️', '😮', '😯', '😲',
    '😳', '🥺', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱',
    '😖', '😣', '😞', '😓', '😩', '😫', '🥱', '😤', '😡', '😠',
    '🤬', '😈', '👿', '💀', '☠️', '💩', '🤡', '👹', '👺', '👻',

    // Gestures & Body
    '👋', '🤚', '🖐️', '✋', '🖖', '👌', '🤌', '🤏', '✌️', '🤞',
    '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '👍',
    '👎', '✊', '👊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝',
    '🙏', '💪', '🦾', '🦿', '🦵', '🦶', '👂', '🦻', '👃', '🧠',

    // Hearts & Love
    '❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔',
    '❣️', '💕', '💞', '💓', '💗', '💖', '💘', '💝', '💟', '❤️‍🔥',

    // Work & Office
    '💼', '📊', '📈', '📉', '💻', '⌨️', '🖥️', '🖨️', '🖱️', '💾',
    '💿', '📱', '☎️', '📞', '📟', '📠', '📧', '✉️', '📨', '📩',
    '📮', '📪', '📫', '📬', '📭', '📄', '📃', '📑', '📝', '✏️',
    '✒️', '🖊️', '🖋️', '📏', '📐', '📌', '📍', '🗂️', '📁', '📂',

    // Success & Achievement
    '✅', '✔️', '☑️', '🎯', '🎖️', '🏆', '🥇', '🥈', '🥉', '⭐',
    '🌟', '✨', '💫', '🔥', '💥', '⚡', '💯', '🎉', '🎊', '🎈',

    // Alerts & Symbols
    '⚠️', '🚫', '❌', '⛔', '🚷', '🚯', '🚱', '🚳', '🔞', '📵',
    '❗', '❓', '❕', '❔', '‼️', '⁉️', '💢', '💬', '💭', '🗯️',

    // Time & Calendar
    '⏰', '⏱️', '⏲️', '⏳', '⌛', '📅', '📆', '🗓️', '📇', '🕐',
    '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚',

    // Actions & Arrows
    '🚀', '🎯', '🎲', '🎰', '🧩', '🔍', '🔎', '🔑', '🗝️', '🔒',
    '🔓', '🔐', '🔏', '🔔', '🔕', '📣', '📢', '💡', '🔦', '🏮',
    '➕', '➖', '✖️', '➗', '♾️', '‼️', '⁉️', '❓', '❔', '❕',
    '🔄', '🔃', '🔂', '▶️', '⏸️', '⏯️', '⏹️', '⏺️', '⏭️', '⏮️',
    '⏩', '⏪', '⏫', '⏬', '◀️', '🔼', '🔽', '➡️', '⬅️', '⬆️',
    '⬇️', '↗️', '↘️', '↙️', '↖️', '↕️', '↔️', '↪️', '↩️', '⤴️'
  ];

  const extractMentions = (text: string): string[] => {
    // Use the SAME regex pattern as formatMessage to ensure consistency
    // Only one space allowed: @word or @word word (first name + last name)
    const mentionRegex = /@(\w+(?:\s+\w+)?)(?=\s|$|[.,!?;:]|@)/g;
    const mentions: string[] = [];
    let match;
    while ((match = mentionRegex.exec(text)) !== null) {
      if (match[1]) {
        // Check if this is a valid mention (followed by space, punctuation, @, or end)
        const afterPos = match.index + match[0].length;
        const afterText = text.slice(afterPos);
        const afterChar = text[afterPos];

        // Valid if followed by: @ (another mention), space, punctuation, or end
        if (afterText.startsWith('@') || !afterChar || /\s|[.,!?;:]|@/.test(afterChar)) {
          mentions.push(match[1].trim());
        }
      }
    }
    return mentions;
  };

  const handleKeyPress = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSendMessage();
    }
  };

  const addReaction = (messageId: string, emoji: string) => {
    setMessages(prev => prev.map(msg => {
      if (msg.id === messageId) {
        const reactions = msg.reactions || [];
        const existingReaction = reactions.find(r => r.emoji === emoji);

        if (existingReaction) {
          if (existingReaction.users.includes('You')) {
            existingReaction.users = existingReaction.users.filter(u => u !== 'You');
            if (existingReaction.users.length === 0) {
              return { ...msg, reactions: reactions.filter(r => r.emoji !== emoji) };
            }
          } else {
            existingReaction.users.push('You');
          }
        } else {
          reactions.push({ emoji, users: ['You'] });
        }

        return { ...msg, reactions };
      }
      return msg;
    }));
  };

  return (
    <div className="h-full flex flex-col bg-gray-50 overflow-hidden">
      {/* Header - Fixed at top */}
      <div className="bg-white border-b border-gray-200 px-3 sm:px-6 py-4 flex-shrink-0">
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-2 sm:gap-4 min-w-0 flex-1">
            <div className="w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg shrink-0">
              <MessageSquare className="w-5 h-5 sm:w-6 sm:h-6 text-white" />
            </div>
            <div className="min-w-0 flex-1">
              <h1 className="text-lg sm:text-2xl font-bold text-gray-900">Work Notes</h1>
              <div className="flex items-center gap-2 mt-1">
                <p className="text-gray-600 text-sm sm:text-base truncate">{requestInfo.title}</p>
                <Badge variant="outline" className="text-xs shrink-0">
                  {requestId}
                </Badge>
              </div>
            </div>
          </div>

          <div className="flex items-center gap-3">
            <div className="flex items-center gap-2 text-sm text-gray-600">
              <div className="flex -space-x-2">
                {onlineParticipants.slice(0, 3).map((participant, index) => (
                  <Avatar key={index} className="h-8 w-8 ring-2 ring-white shadow-sm">
                    <AvatarFallback className="bg-blue-500 text-white text-xs font-semibold">
                      {participant.avatar}
                    </AvatarFallback>
                  </Avatar>
                ))}
                {onlineParticipants.length > 3 && (
                  <div className="h-8 w-8 rounded-full bg-gray-100 ring-2 ring-white flex items-center justify-center text-xs font-medium text-gray-600">
                    +{onlineParticipants.length - 3}
                  </div>
                )}
              </div>

            </div>
            <Button
              variant="outline"
              size="sm"
              onClick={() => setShowSidebar(!showSidebar)}
              className="lg:hidden"
            >
              <Users className="h-4 w-4" />
            </Button>
          </div>
        </div>
      </div>

      <div className="flex-1 flex overflow-hidden relative">
        {/* Main Chat Area */}
        <div className="flex-1 flex flex-col min-w-0">
          {/* Search Bar - Fixed */}
          <div className="bg-white border-b border-gray-200 px-2 sm:px-3 lg:px-6 py-2 sm:py-3 flex-shrink-0">
            <div className="relative">
              <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
              <Input
                placeholder="Search messages..."
                value={searchTerm}
                onChange={(e) => setSearchTerm(e.target.value)}
                className="pl-10 bg-gray-50 border-gray-200 h-9 sm:h-10"
              />
            </div>
          </div>

          {/* Messages Area - Fixed height with proper scrolling */}
          <div className="flex-1 overflow-y-auto overflow-x-hidden px-2 sm:px-3 lg:px-6 py-2 sm:py-4 min-h-0">
            <div className="space-y-3 sm:space-y-6 max-w-full">
              {filteredMessages.map((msg) => {
                // Check if this message is from the current user
                const isCurrentUser = (msg as any).isCurrentUser || false;

                return (
                  <div key={msg.id} className={`flex gap-2 sm:gap-3 lg:gap-4 ${msg.isSystem ? 'justify-center' : isCurrentUser ? 'justify-end' : ''}`}>
                    {!msg.isSystem && !isCurrentUser && (
                      <Avatar className="h-8 w-8 sm:h-10 sm:w-10 lg:h-12 lg:w-12 flex-shrink-0 ring-1 sm:ring-2 ring-white shadow-sm">
                        <AvatarFallback className={`text-white font-semibold text-xs sm:text-sm ${msg.user.role === 'Initiator' ? 'bg-green-600' :
                          msg.user.role === 'Current User' ? 'bg-blue-500' :
                            msg.user.role === 'System' ? 'bg-gray-500' :
                              'bg-slate-600'
                          }`}>
                          {msg.user.avatar}
                        </AvatarFallback>
                      </Avatar>
                    )}

                    <div className={`${isCurrentUser ? 'max-w-[70%]' : 'flex-1'} min-w-0 ${msg.isSystem ? 'text-center max-w-xs sm:max-w-md mx-auto' : ''}`}>
                      {msg.isSystem ? (
                        <div className="inline-flex items-center gap-2 sm:gap-3 px-3 sm:px-4 py-1.5 sm:py-2 bg-gray-100 rounded-full">
                          <Activity className="w-3 h-3 sm:w-4 sm:h-4 text-gray-500 flex-shrink-0" />
                          <span className="text-xs sm:text-sm text-gray-700">{msg.content}</span>
                          <span className="text-xs text-gray-500 hidden sm:inline">{formatDateTime(msg.timestamp)}</span>
                        </div>
                      ) : (
                        <div>
                          {/* Message Header */}
                          <div className={`flex items-center gap-2 sm:gap-3 mb-1 sm:mb-2 flex-wrap ${isCurrentUser ? 'justify-end' : ''}`}>
                            <span className="font-semibold text-gray-900 text-sm sm:text-base truncate">
                              {msg.user.name} {isCurrentUser && <span className="text-xs text-gray-500 font-normal">(you)</span>}
                            </span>
                            <Badge variant="outline" className="text-xs flex-shrink-0">
                              {msg.user.role}
                            </Badge>
                            <span className="text-xs text-gray-500 flex items-center gap-1 flex-shrink-0">
                              <Clock className="w-3 h-3" />
                              {formatDateTime(msg.timestamp)}
                            </span>
                            {msg.isHighPriority && (
                              <Badge variant="destructive" className="text-xs flex-shrink-0">
                                <Flag className="w-3 h-3 mr-1" />
                                Priority
                              </Badge>
                            )}
                          </div>

                          {/* Message Content */}
                          <div className={`rounded-lg border p-3 sm:p-4 shadow-sm ${isCurrentUser ? 'bg-blue-50 border-blue-200' : 'bg-white border-gray-200'}`}>
                            <div
                              className="text-gray-800 leading-relaxed text-sm sm:text-base"
                              dangerouslySetInnerHTML={{ __html: formatMessage(msg.content) }}
                            />

                            {/* Attachments */}
                            {msg.attachments && msg.attachments.length > 0 && (
                              <div className="mt-2 sm:mt-3 pt-2 sm:pt-3 border-t border-gray-100">
                                <div className="space-y-2">
                                  {msg.attachments.map((attachment: any, index) => {
                                    const fileSize = attachment.fileSize || attachment.file_size;
                                    const fileName = attachment.fileName || attachment.file_name || attachment.name;
                                    const fileType = attachment.fileType || attachment.file_type || attachment.type || '';
                                    const attachmentId = attachment.attachmentId || attachment.attachment_id;

                                    return (
                                      <div key={index} className="flex items-center gap-2 sm:gap-3 p-2 sm:p-3 bg-gray-50 rounded-lg border border-gray-200 hover:bg-gray-100 transition-colors">
                                        <div className="flex-shrink-0">
                                          <FileIcon type={fileType} />
                                        </div>
                                        <div className="flex-1 min-w-0">
                                          <p className="text-xs sm:text-sm font-medium text-gray-700 truncate">
                                            {fileName}
                                          </p>
                                          {fileSize && (
                                            <p className="text-xs text-gray-500">
                                              {formatFileSize(fileSize)}
                                            </p>
                                          )}
                                        </div>

                                        {/* Preview button for images and PDFs */}
                                        {attachmentId && (() => {
                                          const type = (fileType || '').toLowerCase();
                                          return type.includes('image') || type.includes('pdf') ||
                                            type.includes('jpg') || type.includes('jpeg') ||
                                            type.includes('png') || type.includes('gif');
                                        })() && (
                                            <Button
                                              variant="ghost"
                                              size="sm"
                                              className="h-6 w-6 sm:h-8 sm:w-8 p-0 flex-shrink-0 hover:bg-purple-100 hover:text-purple-600"
                                              onClick={(e) => {
                                                e.preventDefault();
                                                e.stopPropagation();
                                                const previewUrl = getWorkNoteAttachmentPreviewUrl(attachmentId);
                                                setPreviewFile({
                                                  fileName,
                                                  fileType,
                                                  fileUrl: previewUrl,
                                                  fileSize,
                                                  attachmentId
                                                });
                                              }}
                                              title="Preview file"
                                            >
                                              <Eye className="w-3 h-3 sm:w-4 sm:h-4" />
                                            </Button>
                                          )}

                                        {/* Download button */}
                                        <Button
                                          variant="ghost"
                                          size="sm"
                                          className="h-6 w-6 sm:h-8 sm:w-8 p-0 flex-shrink-0 hover:bg-blue-100 hover:text-blue-600"
                                          onClick={async (e) => {
                                            e.preventDefault();
                                            e.stopPropagation();

                                            if (!attachmentId) {
                                              toast.error('Cannot download: Attachment ID missing');
                                              return;
                                            }

                                            try {
                                              await downloadWorkNoteAttachment(attachmentId);
                                            } catch (error) {
                                              toast.error('Failed to download file');
                                            }
                                          }}
                                          title="Download file"
                                        >
                                          <Download className="w-3 h-3 sm:w-4 sm:h-4" />
                                        </Button>
                                      </div>
                                    );
                                  })}
                                </div>
                              </div>
                            )}

                            {/* Reactions */}
                            {msg.reactions && msg.reactions.length > 0 && (
                              <div className="flex items-center gap-1 sm:gap-2 mt-2 sm:mt-3 pt-2 sm:pt-3 border-t border-gray-100 flex-wrap">
                                {msg.reactions.map((reaction, index) => (
                                  <button
                                    key={index}
                                    onClick={() => addReaction(msg.id, reaction.emoji)}
                                    className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs sm:text-sm transition-colors flex-shrink-0 ${reaction.users.includes('You')
                                      ? 'bg-blue-100 text-blue-800 border border-blue-200'
                                      : 'bg-gray-100 text-gray-700 hover:bg-gray-200'
                                      }`}
                                  >
                                    <span>{reaction.emoji}</span>
                                    <span className="text-xs font-medium">{reaction.users.length}</span>
                                  </button>
                                ))}
                                <Button
                                  variant="ghost"
                                  size="sm"
                                  className="h-6 w-6 sm:h-7 sm:w-7 p-0 flex-shrink-0"
                                  onClick={() => setShowEmojiPicker(!showEmojiPicker)}
                                >
                                  <Plus className="w-2 h-2 sm:w-3 sm:h-3" />
                                </Button>
                              </div>
                            )}
                          </div>
                        </div>
                      )}
                    </div>

                    {!msg.isSystem && isCurrentUser && (
                      <Avatar className="h-8 w-8 sm:h-10 sm:w-10 lg:h-12 lg:w-12 flex-shrink-0 ring-1 sm:ring-2 ring-white shadow-sm">
                        <AvatarFallback className="bg-blue-500 text-white font-semibold text-xs sm:text-sm">
                          {msg.user.avatar}
                        </AvatarFallback>
                      </Avatar>
                    )}
                  </div>
                );
              })}

              <div ref={messagesEndRef} />
            </div>
          </div>

          {/* Message Input - Fixed at bottom */}
          <div className="bg-white border-t border-gray-200 p-2 sm:p-3 lg:p-6 flex-shrink-0">
            <div className="max-w-full">
              {/* Hidden File Input */}
              <input
                type="file"
                ref={fileInputRef}
                onChange={handleFileSelect}
                className="hidden"
                multiple
                accept={documentPolicy.allowedFileTypes.map(ext => `.${ext}`).join(',')}
              />

              {/* Selected Files Preview - Scrollable if many files */}
              {selectedFiles.length > 0 && (
                <div className="mb-3 space-y-2 max-h-32 overflow-y-auto pr-2">
                  {selectedFiles.map((file, index) => (
                    <div key={index} className="flex items-center gap-2 p-2 bg-blue-50 rounded-lg border border-blue-200">
                      <div className="flex-shrink-0">
                        <FileIcon type={file.type.split('/')[1] || 'file'} />
                      </div>
                      <span className="text-sm text-gray-700 flex-1 truncate min-w-0">{file.name}</span>
                      <span className="text-xs text-gray-500 flex-shrink-0">{(file.size / 1024).toFixed(1)} KB</span>
                      <Button
                        variant="ghost"
                        size="sm"
                        onClick={() => handleRemoveFile(index)}
                        className="h-6 w-6 p-0 hover:bg-red-100 flex-shrink-0"
                      >
                        <X className="h-3 w-3 text-red-600" />
                      </Button>
                    </div>
                  ))}
                </div>
              )}

              {/* Textarea with Mention Dropdown and Emoji Picker */}
              <div className="relative mb-2">
                {/* Mention Suggestions Dropdown - Shows above textarea */}
                {(() => {
                  // Find the last @ symbol that hasn't been completed (doesn't have a space after a name)
                  const lastAtIndex = message.lastIndexOf('@');
                  const hasAt = lastAtIndex >= 0;

                  if (!hasAt) return null;

                  // Get text after the last @
                  const textAfterAt = message.slice(lastAtIndex + 1);

                  // Check if this mention is already completed
                  // A completed mention looks like: "@username " (ends with space after name)
                  // An incomplete mention looks like: "@" or "@user" (no space after, or just typed @)
                  const trimmedAfterAt = textAfterAt.trim();
                  const endsWithSpace = textAfterAt.endsWith(' ');
                  const hasNonSpaceChars = trimmedAfterAt.length > 0;

                  // Don't show dropdown if:
                  // 1. Text after @ is too long (>20 chars) - probably not a mention
                  // 2. Text after @ ends with space AND has characters (completed mention like "@user ")
                  // 3. Text after @ contains space in the middle (like "@user name" - multi-word name already typed)
                  const containsSpaceInMiddle = trimmedAfterAt.includes(' ') && !endsWithSpace;
                  const isCompletedMention = endsWithSpace && hasNonSpaceChars;

                  // Show dropdown if:
                  // - Has @ symbol
                  // - Text after @ is not too long
                  // - Mention is not completed (doesn't end with space after a name)
                  // - Doesn't contain space in middle (not a multi-word name being typed)
                  const shouldShowDropdown = hasAt &&
                    textAfterAt.length <= 20 &&
                    !containsSpaceInMiddle &&
                    !isCompletedMention;

                  if (!shouldShowDropdown) return null;

                  // Use trimmed text for search (ignore trailing spaces)
                  const searchTerm = trimmedAfterAt.toLowerCase();
                  const filteredParticipants = participants.filter(p => {
                    // Exclude current user from mention suggestions
                    const isCurrentUserInList = (p as any).userId === currentUserId;
                    if (isCurrentUserInList) return false;

                    // Filter by search term (empty search term shows all)
                    if (searchTerm) {
                      return p.name.toLowerCase().includes(searchTerm);
                    }
                    return true; // Show all if no search term
                  });

                  return (
                    <div className="absolute bottom-full left-0 mb-2 bg-white border-2 border-blue-300 rounded-lg shadow-2xl p-3 z-[100] w-full sm:max-w-md">
                      <p className="text-sm font-semibold text-gray-900 mb-2">💬 Mention someone</p>
                      <div className="max-h-60 overflow-y-auto space-y-1">
                        {filteredParticipants.length > 0 ? (
                          filteredParticipants.map((participant, idx) => (
                            <button
                              key={idx}
                              type="button"
                              onClick={(e) => {
                                e.preventDefault();
                                e.stopPropagation();
                                // Find the last @ and replace everything from @ to end with the new mention
                                const lastAt = message.lastIndexOf('@');
                                const before = message.slice(0, lastAt);
                                // Add the mention with a space after for easy continuation
                                setMessage(before + '@' + participant.name + ' ');
                              }}
                              className="w-full flex items-center gap-3 p-3 hover:bg-blue-50 rounded-lg text-left transition-colors border border-transparent hover:border-blue-200"
                            >
                              <Avatar className="h-10 w-10">
                                <AvatarFallback className={`text-white text-sm font-semibold ${participant.role === 'Initiator' ? 'bg-green-600' :
                                  participant.role === 'Approver' ? 'bg-purple-600' :
                                    'bg-blue-500'
                                  }`}>
                                  {participant.avatar}
                                </AvatarFallback>
                              </Avatar>
                              <div className="flex-1 min-w-0">
                                <p className="text-sm font-semibold text-gray-900">{participant.name}</p>
                                <p className="text-xs text-gray-600">{participant.role}</p>
                              </div>
                            </button>
                          ))
                        ) : (
                          <p className="text-sm text-gray-500 text-center py-4">
                            {searchTerm ? `No participants found matching "${searchTerm}"` : 'No other participants available'}
                          </p>
                        )}
                      </div>
                    </div>
                  );
                })()}

                <Textarea
                  placeholder={effectiveIsSpectator ? "Spectators cannot send messages" : "Type your message... Use @username to mention someone"}
                  value={message}
                  onChange={(e) => setMessage(e.target.value)}
                  onKeyPress={handleKeyPress}
                  className="min-h-[50px] sm:min-h-[60px] resize-none border-gray-200 focus:ring-blue-500 focus:border-blue-500 w-full text-sm"
                  rows={2}
                  disabled={effectiveIsSpectator}
                />

                {/* Emoji Picker Popup */}
                {showEmojiPicker && (
                  <div className="absolute bottom-full left-0 mb-2 bg-white border border-gray-200 rounded-lg shadow-xl p-3 z-50 w-full sm:w-96 max-h-80 overflow-y-auto">
                    <div className="flex items-center justify-between mb-3 sticky top-0 bg-white pb-2 border-b">
                      <span className="text-sm font-semibold text-gray-700">Pick an emoji</span>
                      <Button
                        variant="ghost"
                        size="sm"
                        onClick={() => setShowEmojiPicker(false)}
                        className="h-6 w-6 p-0"
                      >
                        <X className="h-3 w-3" />
                      </Button>
                    </div>
                    <div className="grid grid-cols-8 sm:grid-cols-10 gap-1">
                      {emojiList.map((emoji, index) => (
                        <button
                          key={index}
                          onClick={() => handleEmojiSelect(emoji)}
                          className="text-xl sm:text-2xl hover:bg-gray-100 rounded p-1 transition-colors flex items-center justify-center"
                          title={emoji}
                        >
                          {emoji}
                        </button>
                      ))}
                    </div>
                  </div>
                )}
              </div>

              {/* Action Buttons Row - Always visible */}
              <div className="flex items-center justify-between gap-2 flex-shrink-0">
                {/* Left side - Action buttons */}
                <div className="flex items-center gap-1 sm:gap-2 flex-shrink-0">
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={handleAttachmentClick}
                    disabled={effectiveIsSpectator}
                    title={effectiveIsSpectator ? "Spectators cannot attach files" : "Attach file"}
                  >
                    <Paperclip className="h-4 w-4" />
                  </Button>
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={() => setShowEmojiPicker(!showEmojiPicker)}
                    disabled={effectiveIsSpectator}
                    title={effectiveIsSpectator ? "Spectators cannot add emojis" : "Add emoji"}
                  >
                    <Smile className="h-4 w-4" />
                  </Button>
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={() => setMessage(prev => prev + '@')}
                    disabled={effectiveIsSpectator}
                    title={effectiveIsSpectator ? "Spectators cannot mention users" : "Mention someone"}
                  >
                    <AtSign className="h-4 w-4" />
                  </Button>
                </div>

                {/* Right side - Character count and Send button */}
                <div className="flex items-center gap-2 ml-auto flex-shrink-0">
                  <span className="text-xs text-gray-500 hidden md:inline whitespace-nowrap">
                    {message.length}/2000
                  </span>
                  <Button
                    onClick={handleSendMessage}
                    disabled={(!message.trim() && selectedFiles.length === 0) || effectiveIsSpectator}
                    className="bg-blue-600 hover:bg-blue-700 h-8 sm:h-9 px-3 sm:px-4 disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0"
                    size="sm"
                    title={effectiveIsSpectator ? "Spectators cannot send messages" : "Send"}
                  >
                    <Send className="h-4 w-4 sm:mr-2" />
                    <span className="hidden sm:inline">Send</span>
                  </Button>
                </div>
              </div>
            </div>
          </div>
        </div>

        {/* Mobile Sidebar Overlay */}
        {showSidebar && (
          <div
            className="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden"
            onClick={() => setShowSidebar(false)}
          />
        )}

        {/* Participants Sidebar */}
        <div className={`
          w-72 sm:w-80 bg-white border-l border-gray-200 flex flex-col
          lg:relative lg:translate-x-0 lg:shadow-none
          ${showSidebar ? 'fixed right-0 top-0 bottom-0 z-50 shadow-xl' : 'hidden lg:flex'}
        `}>
          <div className="p-4 sm:p-6 border-b border-gray-200 flex-1 flex flex-col min-h-0">
            <div className="flex items-center justify-between mb-4 flex-shrink-0">
              <h3 className="text-base sm:text-lg font-semibold text-gray-900">Participants</h3>
              <Button
                variant="ghost"
                size="sm"
                onClick={() => setShowSidebar(false)}
                className="lg:hidden h-8 w-8 p-0"
              >
                <X className="h-4 w-4" />
              </Button>
            </div>
            <div className="space-y-3 sm:space-y-4 overflow-y-auto flex-1 pr-2">
              {participants.map((participant, index) => {
                const isCurrentUser = (participant as any).userId === currentUserId;
                return (
                  <div key={index} className="flex items-center gap-3">
                    <div className="relative">
                      <Avatar className="h-9 w-9 sm:h-10 sm:w-10">
                        <AvatarFallback className={`text-white font-semibold text-sm ${participant.role === 'Initiator' ? 'bg-green-600' :
                          isCurrentUser ? 'bg-blue-500' : 'bg-slate-600'
                          }`}>
                          {participant.avatar}
                        </AvatarFallback>
                      </Avatar>
                      <div className={`absolute -bottom-1 -right-1 w-3 h-3 rounded-full border-2 border-white ${getStatusColor(participant.status)}`}></div>
                    </div>
                    <div className="flex-1 min-w-0">
                      <p className="font-medium text-gray-900 truncate text-sm sm:text-base">
                        {participant.name} {isCurrentUser && <span className="text-xs text-gray-500 font-normal">(you)</span>}
                      </p>
                      <div className="flex items-center gap-2">
                        <p className="text-xs text-gray-500">{participant.role}</p>
                        <span className="text-xs text-gray-400">•</span>
                        <p className="text-xs text-gray-500">{getStatusText(participant.status)}</p>
                      </div>
                      {participant.lastSeen && participant.status === 'offline' && (
                        <p className="text-xs text-gray-400">{participant.lastSeen}</p>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>

          {/* Quick Actions Section - Hide for spectators */}
          {!effectiveIsSpectator && (
            <div className="p-4 sm:p-6 flex-shrink-0">
              <h4 className="font-semibold text-gray-900 mb-3 text-sm sm:text-base">Quick Actions</h4>
              <div className="space-y-2">
                {/* Only initiator can add approvers */}
                {isInitiator && (
                  <Button
                    variant="outline"
                    size="sm"
                    className="w-full justify-start gap-2 h-9 text-sm"
                    onClick={() => setShowAddApproverModal(true)}
                  >
                    <UserPlus className="h-4 w-4" />
                    Add Approver
                  </Button>
                )}
                <Button
                  variant="outline"
                  size="sm"
                  className="w-full justify-start gap-2 h-9 text-sm"
                  onClick={() => setShowAddSpectatorModal(true)}
                >
                  <Eye className="h-4 w-4" />
                  Add Spectator
                </Button>
                {/* <Button variant="outline" size="sm" className="w-full justify-start gap-2 h-9 text-sm">
                  <Bell className="h-4 w-4" />
                  Manage Notifications
                </Button>
                <Button variant="outline" size="sm" className="w-full justify-start gap-2 h-9 text-sm">
                  <Archive className="h-4 w-4" />
                  Archive Chat
                </Button> */}
              </div>
            </div>
          )}
        </div>
      </div>

      {/* File Preview Modal */}
      {previewFile && (
        <FilePreview
          fileName={previewFile.fileName}
          fileType={previewFile.fileType}
          fileUrl={previewFile.fileUrl}
          fileSize={previewFile.fileSize}
          attachmentId={previewFile.attachmentId}
          onDownload={downloadWorkNoteAttachment}
          open={!!previewFile}
          onClose={() => setPreviewFile(null)}
        />
      )}

      {/* Add Spectator Modal - Hide for spectators */}
      {!effectiveIsSpectator && (
        <AddSpectatorModal
          open={showAddSpectatorModal}
          onClose={() => setShowAddSpectatorModal(false)}
          onConfirm={handleAddSpectator}
          requestIdDisplay={effectiveRequestId}
          requestTitle={requestInfo.title}
          existingParticipants={existingParticipants}
        />
      )}

      {/* Add Approver Modal - Hide for spectators */}
      {!effectiveIsSpectator && isInitiator && (
        <AddApproverModal
          open={showAddApproverModal}
          onClose={() => setShowAddApproverModal(false)}
          onConfirm={handleAddApproverInternal}
          requestIdDisplay={effectiveRequestId}
          requestTitle={requestInfo.title}
          existingParticipants={existingParticipants}
          currentLevels={currentLevels}
          maxApprovalLevels={maxApprovalLevels}
          onPolicyViolation={onPolicyViolation}
        />
      )}

      {/* Document Validation Error Modal */}
      <Dialog open={documentErrorModal.open} onOpenChange={(open) => setDocumentErrorModal(prev => ({ ...prev, open }))}>
        <DialogContent className="sm:max-w-lg">
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2">
              <AlertCircle className="w-5 h-5 text-red-600" />
              Document Upload Policy Violation
            </DialogTitle>
            <DialogDescription asChild>
              <div className="space-y-3">
                <p className="text-gray-700">
                  The following file(s) could not be uploaded due to policy violations:
                </p>
                <div className="space-y-2 max-h-60 overflow-y-auto">
                  {documentErrorModal.errors.map((error, index) => (
                    <div key={index} className="bg-red-50 border border-red-200 rounded-lg p-3">
                      <p className="font-medium text-red-900 text-sm">{error.fileName}</p>
                      <p className="text-xs text-red-700 mt-1">{error.reason}</p>
                    </div>
                  ))}
                </div>
                <div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
                  <p className="text-sm text-blue-800 font-semibold mb-1">Document Policy:</p>
                  <ul className="text-xs text-blue-700 space-y-1 list-disc list-inside">
                    <li>Maximum file size: {documentPolicy.maxFileSizeMB}MB</li>
                    <li>Allowed file types: {documentPolicy.allowedFileTypes.join(', ')}</li>
                  </ul>
                </div>
              </div>
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button
              onClick={() => setDocumentErrorModal({ open: false, errors: [] })}
              className="w-full sm:w-auto"
            >
              OK
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Action Status Modal - Success/Error feedback for adding approver/spectator */}
      <ActionStatusModal
        open={showActionStatusModal}
        onClose={() => setShowActionStatusModal(false)}
        success={actionStatus.success}
        title={actionStatus.title}
        message={actionStatus.message}
      />
    </div>
  );
}