import type { FC } from 'react' import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createPortal } from 'react-dom' import Textarea from 'react-textarea-autosize' import { RiSendPlane2Fill } from '@remixicon/react' import { useParams } from 'next/navigation' import { useReactFlow, useViewport } from 'reactflow' import cn from '@/utils/classnames' import Button from '@/app/components/base/button' import Avatar from '@/app/components/base/avatar' import { useAppContext } from '@/context/app-context' import { type UserProfile, fetchMentionableUsers } from '@/service/workflow-comment' type CommentInputProps = { position: { x: number; y: number } onSubmit: (content: string, mentionedUserIds: string[]) => void onCancel: () => void } export const CommentInput: FC = memo(({ position, onSubmit, onCancel }) => { const [content, setContent] = useState('') const textareaRef = useRef(null) const { userProfile } = useAppContext() const { flowToScreenPosition } = useReactFlow() const viewport = useViewport() const params = useParams() const appId = params.appId as string const [mentionUsers, setMentionUsers] = useState([]) const [showMentionDropdown, setShowMentionDropdown] = useState(false) const [mentionQuery, setMentionQuery] = useState('') const [mentionPosition, setMentionPosition] = useState(0) const [selectedMentionIndex, setSelectedMentionIndex] = useState(0) const [mentionedUserIds, setMentionedUserIds] = useState([]) const screenPosition = useMemo(() => { return flowToScreenPosition(position) }, [position.x, position.y, viewport.x, viewport.y, viewport.zoom, flowToScreenPosition]) const loadMentionableUsers = useCallback(async () => { if (!appId) return try { const users = await fetchMentionableUsers(appId) setMentionUsers(users) } catch (error) { console.error('Failed to load mentionable users:', error) } }, [appId]) useEffect(() => { loadMentionableUsers() }, [loadMentionableUsers]) useEffect(() => { const handleGlobalKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { e.preventDefault() e.stopPropagation() onCancel() } } document.addEventListener('keydown', handleGlobalKeyDown, true) return () => { document.removeEventListener('keydown', handleGlobalKeyDown, true) } }, [onCancel]) const filteredMentionUsers = useMemo(() => { if (!mentionQuery) return mentionUsers return mentionUsers.filter(user => user.name.toLowerCase().includes(mentionQuery.toLowerCase()) || user.email.toLowerCase().includes(mentionQuery.toLowerCase()), ) }, [mentionUsers, mentionQuery]) const dropdownPosition = useMemo(() => { if (!showMentionDropdown || !textareaRef.current) return { x: 0, y: 0 } const textareaRect = textareaRef.current.getBoundingClientRect() return { x: textareaRect.left, y: textareaRect.bottom + 4, } }, [showMentionDropdown]) const handleContentChange = useCallback((value: string) => { setContent(value) // Delay getting cursor position to ensure the textarea has updated setTimeout(() => { const cursorPosition = textareaRef.current?.selectionStart || 0 const textBeforeCursor = value.slice(0, cursorPosition) const mentionMatch = textBeforeCursor.match(/@(\w*)$/) if (mentionMatch) { setMentionQuery(mentionMatch[1]) setMentionPosition(cursorPosition - mentionMatch[0].length) setShowMentionDropdown(true) setSelectedMentionIndex(0) } else { setShowMentionDropdown(false) } }, 0) }, []) const handleMentionButtonClick = useCallback((e: React.MouseEvent) => { e.preventDefault() e.stopPropagation() console.log('Mention button clicked!') const textarea = textareaRef.current if (!textarea) return const cursorPosition = textarea.selectionStart || 0 const newContent = `${content.slice(0, cursorPosition)}@${content.slice(cursorPosition)}` setContent(newContent) setTimeout(() => { const newCursorPos = cursorPosition + 1 textarea.setSelectionRange(newCursorPos, newCursorPos) textarea.focus() setMentionQuery('') setMentionPosition(cursorPosition) setShowMentionDropdown(true) setSelectedMentionIndex(0) }, 0) }, [content]) const insertMention = useCallback((user: UserProfile) => { const textarea = textareaRef.current if (!textarea) return const beforeMention = content.slice(0, mentionPosition) const afterMention = content.slice(textarea.selectionStart || 0) const newContent = `${beforeMention}@${user.name} ${afterMention}` setContent(newContent) setShowMentionDropdown(false) setMentionedUserIds(prev => [...prev, user.id]) setTimeout(() => { const newCursorPos = mentionPosition + user.name.length + 2 // @ + name + space textarea.setSelectionRange(newCursorPos, newCursorPos) textarea.focus() }, 0) }, [content, mentionPosition]) const handleSubmit = useCallback((e?: React.MouseEvent) => { if (e) { e.preventDefault() e.stopPropagation() } console.log('Submit button clicked!') try { if (content.trim()) { onSubmit(content.trim(), mentionedUserIds) setContent('') setMentionedUserIds([]) } } catch (error) { console.error('Error in CommentInput handleSubmit:', error) } }, [content, mentionedUserIds, onSubmit]) const handleKeyDown = useCallback((e: React.KeyboardEvent) => { if (showMentionDropdown) { if (e.key === 'ArrowDown') { e.preventDefault() setSelectedMentionIndex(prev => prev < filteredMentionUsers.length - 1 ? prev + 1 : 0, ) } else if (e.key === 'ArrowUp') { e.preventDefault() setSelectedMentionIndex(prev => prev > 0 ? prev - 1 : filteredMentionUsers.length - 1, ) } else if (e.key === 'Enter') { e.preventDefault() if (filteredMentionUsers[selectedMentionIndex]) insertMention(filteredMentionUsers[selectedMentionIndex]) return } else if (e.key === 'Escape') { e.preventDefault() setShowMentionDropdown(false) return } } if (e.key === 'Enter' && !e.shiftKey && !showMentionDropdown) { e.preventDefault() handleSubmit() } }, [showMentionDropdown, filteredMentionUsers, selectedMentionIndex, insertMention, handleSubmit]) return (