import type { ProjectRepositoryItem, RoomResponse, RoomMemberResponse, RoomMessageResponse, RoomThreadResponse } from '@/client';
import { useRoom, type MessageWithMeta } from '@/contexts';
import { type RoomAiConfig } from '@/contexts/room-context';
import { useRoomDraft } from '@/hooks/useRoomDraft';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { buildMentionHtml } from '@/lib/mention-ast';
import { mentionSelectedIdxRef, mentionVisibleRef } from '@/lib/mention-refs';
import { MentionInput } from './MentionInput';
import { ChevronLeft, Hash, Send, Settings, Timer, Trash2, Users, X, Search, Bell } from 'lucide-react';
import {
  memo,
  useCallback,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
  type ReactNode,
} from 'react';
import { toast } from 'sonner';
import { RoomAiTasksPanel } from './RoomAiTasksPanel';
import { MentionPopover } from './MentionPopover';
import { RoomMessageEditDialog } from './RoomMessageEditDialog';
import { RoomMessageEditHistoryDialog } from './RoomMessageEditHistoryDialog';
import { RoomMessageList } from './RoomMessageList';
import { RoomParticipantsPanel } from './RoomParticipantsPanel';
import { RoomSettingsPanel } from './RoomSettingsPanel';
import { RoomMessageSearch } from './RoomMessageSearch';
import { RoomMentionPanel } from './RoomMentionPanel';
import { RoomThreadPanel } from './RoomThreadPanel';

const MENTION_PATTERN = /@([^:@\s]*)(:([^\s]*))?$/;


export interface ChatInputAreaHandle {
  insertMention: (id: string, label: string, type: 'user' | 'ai') => void;
  /** Insert a category prefix (e.g. 'ai') into the textarea and trigger React onChange */
  insertCategory: (category: string) => void;
}

interface ChatInputAreaProps {
  roomName: string;
  onSend: (content: string) => void;
  isSending: boolean;
  members: RoomMemberResponse[];
  repos?: ProjectRepositoryItem[];
  reposLoading?: boolean;
  aiConfigs?: RoomAiConfig[];
  aiConfigsLoading?: boolean;
  replyingTo?: { id: string; display_name?: string; content: string } | null;
  onCancelReply?: () => void;
  draft: string;
  onDraftChange: (content: string) => void;
  onClearDraft: () => void;
  ref?: React.Ref<ChatInputAreaHandle>;
}

const ChatInputArea = memo(function ChatInputArea({
  roomName,
  onSend,
  isSending,
  members,
  repos,
  reposLoading,
  aiConfigs,
  aiConfigsLoading,
  replyingTo,
  onCancelReply,
  draft,
  onDraftChange,
  onClearDraft,
  ref,
}: ChatInputAreaProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const cursorRef = useRef(0);
  const [showMentionPopover, setShowMentionPopover] = useState(false);

  const handleMentionSelect = useCallback((_newValue: string, _newCursorPos: number) => {
    if (!containerRef.current) return;
    const container = containerRef.current;
    const cursorPos = cursorRef.current;
    const textBefore = draft.substring(0, cursorPos);
    const atMatch = textBefore.match(MENTION_PATTERN);
    if (!atMatch) return;

    const [fullMatch] = atMatch;
    const startPos = cursorPos - fullMatch.length;

    const before = draft.substring(0, startPos);
    const after = draft.substring(cursorPos);

    const suggestion = mentionVisibleRef.current[mentionSelectedIdxRef.current];
    if (!suggestion || suggestion.type !== 'item') return;

    const html = buildMentionHtml(
      suggestion.category!,
      suggestion.mentionId!,
      suggestion.label,
    );
    const spacer = ' ';
    const newValue = before + html + spacer + after;
    const newCursorPos = startPos + html.length + spacer.length;

    onDraftChange(newValue);
    setShowMentionPopover(false);
    setTimeout(() => {
      const container = containerRef.current;
      if (!container) return;
      container.innerHTML = newValue
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/\n/g, '<br>');
      // Place cursor at newCursorPos
      const sel = window.getSelection();
      if (!sel) return;
      const range = document.createRange();
      let charCount = 0;
      let found = false;
      function walk(node: Node) {
        if (found) return;
        if (node.nodeType === Node.TEXT_NODE) {
          const t = node.textContent ?? '';
          if (charCount + t.length >= newCursorPos) {
            range.setStart(node, Math.min(newCursorPos - charCount, t.length));
            range.collapse(true);
            found = true;
            return;
          }
          charCount += t.length;
        } else if (node.nodeType === Node.ELEMENT_NODE) {
          for (const c of Array.from(node.childNodes)) {
            walk(c);
            if (found) return;
          }
        }
      }
      walk(container);
      if (!found) { range.selectNodeContents(container); range.collapse(false); }
      sel.removeAllRanges();
      sel.addRange(range);
      container.focus();
    }, 0);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // Uses draft from closure

  useImperativeHandle(ref, () => ({
    insertMention: (id: string, label: string) => {
      const cursorPos = cursorRef.current;
      const escapedLabel = label.replace(/</g, '&lt;').replace(/>/g, '&gt;');
      const escapedId = id.replace(/"/g, '&quot;');
      const mentionText = `<mention type="user" id="${escapedId}">${escapedLabel}</mention> `;
      const before = draft.substring(0, cursorPos);
      const after = draft.substring(cursorPos);
      const newValue = before + mentionText + after;
      onDraftChange(newValue);
      setShowMentionPopover(false);
      setTimeout(() => {
        containerRef.current?.focus();
      }, 0);
    },
    insertCategory: (category: string) => {
      const cursorPos = cursorRef.current;
      const textBefore = draft.substring(0, cursorPos);
      const atMatch = textBefore.match(MENTION_PATTERN);
      if (!atMatch) return;
      const [fullMatch] = atMatch;
      const startPos = cursorPos - fullMatch.length;
      const before = draft.substring(0, startPos);
      const afterPartial = draft.substring(startPos + fullMatch.length);
      const newValue = before + '@' + category + ':' + afterPartial;
      const newCursorPos = startPos + 1 + category.length + 1;
      onDraftChange(newValue);
      setShowMentionPopover(!!newValue.substring(0, newCursorPos).match(MENTION_PATTERN));
    },
  }));

  // Listen for mention-click events from message content (e.g. 🤖 AI button)
  useEffect(() => {
    const onMentionClick = (e: Event) => {
      const { type, id, label } = (e as CustomEvent<{ type: string; id: string; label: string }>).detail;
      const cursorPos = cursorRef.current;
      const textBefore = draft.substring(0, cursorPos);
      const html = buildMentionHtml(type as 'user' | 'repository' | 'ai', id, label);
      const spacer = ' ';
      const newValue = textBefore + html + spacer + draft.substring(cursorPos);
      const newCursorPos = cursorPos + html.length + spacer.length;
      onDraftChange(newValue);
      setTimeout(() => {
        const container = containerRef.current;
        if (!container) return;
        container.focus();
        const sel = window.getSelection();
        if (!sel) return;
        const range = document.createRange();
        let charCount = 0;
        let found = false;
        function walk(node: Node) {
          if (found) return;
          if (node.nodeType === Node.TEXT_NODE) {
            const t = node.textContent ?? '';
            if (charCount + t.length >= newCursorPos) {
              range.setStart(node, Math.min(newCursorPos - charCount, t.length));
              range.collapse(true);
              found = true;
              return;
            }
            charCount += t.length;
          } else if (node.nodeType === Node.ELEMENT_NODE) {
            for (const c of Array.from(node.childNodes)) {
              walk(c);
              if (found) return;
            }
          }
        }
        walk(container);
        if (!found) { range.selectNodeContents(container); range.collapse(false); }
        sel.removeAllRanges();
        sel.addRange(range);
      }, 0);
    };
    document.addEventListener('mention-click', onMentionClick);
    return () => document.removeEventListener('mention-click', onMentionClick);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <div className="border-t border-border/70 bg-background p-3">
      {replyingTo && (
        <div className="mb-2 flex items-center gap-2 rounded-md bg-muted/50 px-3 py-2 text-xs">
          <span className="font-medium text-foreground">Replying to {replyingTo.display_name}</span>
          <span className="truncate text-muted-foreground" title={replyingTo.content}>{replyingTo.content.length > 80 ? replyingTo.content.slice(0, 80) + '…' : replyingTo.content}</span>
          <button onClick={onCancelReply} className="ml-auto text-muted-foreground hover:text-foreground">
            <X className="h-3 w-3" />
          </button>
        </div>
      )}

      <div className="relative">
        <MentionInput
          ref={containerRef}
          value={draft}
          cursorRef={cursorRef}
          onChange={(v) => {
            onDraftChange(v);
            const textBefore = v.substring(0, cursorRef.current);
            if (textBefore.match(MENTION_PATTERN)) {
              setShowMentionPopover(true);
            } else {
              setShowMentionPopover(false);
            }
          }}
          onSend={() => {
            const content = draft.trim();
            if (content && !isSending) {
              onSend(content);
              onClearDraft();
            }
          }}
          placeholder={`Message #${roomName}...`}
        />

        <div className="absolute bottom-2 right-2 flex items-center gap-1">
          <Button
            size="sm"
            onClick={() => {
              const content = draft.trim();
              if (content && !isSending) {
                onSend(content);
                onClearDraft();
              }
            }}
            disabled={isSending}
            className="h-7 px-3"
          >
            <Send className="mr-1 h-3 w-3" />
            Send
          </Button>
        </div>
      </div>

      {showMentionPopover && (
        <MentionPopover
          members={members}
          repos={repos}
          reposLoading={reposLoading}
          aiConfigs={aiConfigs}
          aiConfigsLoading={aiConfigsLoading}
          inputValue={draft}
          cursorPosition={cursorRef.current}
          onSelect={handleMentionSelect}
          textareaRef={containerRef}
          onOpenChange={setShowMentionPopover}
          onCategoryEnter={(category: string) => {
            const cursorPos = cursorRef.current;
            const textBefore = draft.substring(0, cursorPos);
            const atMatch = textBefore.match(MENTION_PATTERN);
            if (!atMatch) return;
            const [fullMatch] = atMatch;
            const startPos = cursorPos - fullMatch.length;
            const before = draft.substring(0, startPos);
            const afterPartial = draft.substring(startPos + fullMatch.length);
            const newValue = before + '@' + category + ':' + afterPartial;
            const newCursorPos = startPos + 1 + category.length + 1;
            onDraftChange(newValue);
            setShowMentionPopover(!!newValue.substring(0, newCursorPos).match(MENTION_PATTERN));
          }}
        />
      )}
    </div>
  );
});

/** Animated slide panel - handles enter/exit animations */
function SlidePanel({
  open,
  width = 'w-[360px]',
  children,
}: {
  open: boolean;
  width?: string;
  children: ReactNode;
}) {
  const [visible, setVisible] = useState(open);
  const [animate, setAnimate] = useState(open);

  useEffect(() => {
    if (open) {
      setVisible(true);
      // Force reflow then animate in
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          setAnimate(true);
        });
      });
    } else {
      setAnimate(false);
      const timer = setTimeout(() => setVisible(false), 200);
      return () => clearTimeout(timer);
    }
  }, [open]);

  if (!visible) return null;

  return (
    <aside
      className={`${width} border-l border-border/70 bg-card/50 transition-all duration-200 ease-out ${
        animate ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'
      }`}
      style={{ flexShrink: 0 }}
    >
      {children}
    </aside>
  );
}

interface RoomChatPanelProps {
  room: RoomResponse;
  isAdmin: boolean;
  onClose: () => void;
  onDelete: () => void;
}

export function RoomChatPanel({ room, isAdmin, onClose, onDelete }: RoomChatPanelProps) {
  const {
    messages,
    members,
    membersLoading,
    sendMessage,
    editMessage,
    revokeMessage,
    updateRoom,
    wsStatus,
    wsError,
    wsClient,
    threads,
    refreshThreads,
    projectRepos,
    reposLoading,
    roomAiConfigs,
    aiConfigsLoading,
  } = useRoom();

  const messagesEndRef = useRef<HTMLDivElement>(null);
  const chatInputRef = useRef<ChatInputAreaHandle>(null);

  const [replyingTo, setReplyingTo] = useState<MessageWithMeta | null>(null);
  const [editingMessage, setEditingMessage] = useState<MessageWithMeta | null>(null);
  const [editDialogOpen, setEditDialogOpen] = useState(false);
  const [editHistoryDialogOpen, setEditHistoryDialogOpen] = useState(false);
  const [selectedMessageForHistory, setSelectedMessageForHistory] = useState<string>('');
  const [showAiTasks, setShowAiTasks] = useState(false);
  const [showParticipants, setShowParticipants] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [showSearch, setShowSearch] = useState(false);
  const [showMentions, setShowMentions] = useState(false);
  const [isUpdatingRoom, setIsUpdatingRoom] = useState(false);
  const [activeThread, setActiveThread] = useState<{ thread: RoomThreadResponse; parentMessage: MessageWithMeta } | null>(null);

  // Draft management
  const { draft, setDraft, clearDraft } = useRoomDraft(room.id);

  const isWsConnected = wsStatus === 'open';

  const connectionLabel = wsStatus === 'connecting'
    ? 'Connecting...'
    : wsStatus === 'closed'
    ? 'Disconnected'
    : wsStatus === 'error'
    ? (wsError ?? 'Connection error')
    : null;

  // Visual connection status dot (Discord-style)
  const statusDotColor = wsStatus === 'open'
    ? 'bg-green-500'
    : wsStatus === 'connecting'
    ? 'bg-yellow-400 animate-pulse'
    : 'bg-red-500';

  const handleSend = useCallback(
    (content: string) => {
      sendMessage(content, 'text', replyingTo?.id ?? undefined);
      setReplyingTo(null);
    },
    // sendMessage from useRoom is already stable; replyingTo changes trigger handleSend rebuild (acceptable)
    [sendMessage, replyingTo],
  );

  const handleEdit = useCallback((message: MessageWithMeta) => {
    setEditingMessage(message);
    setEditDialogOpen(true);
  }, []);

  const handleViewEditHistory = useCallback((message: MessageWithMeta) => {
    setSelectedMessageForHistory(message.id);
    setEditHistoryDialogOpen(true);
  }, []);

  const handleEditConfirm = useCallback(
    (newContent: string) => {
      if (!editingMessage) return;
      editMessage(editingMessage.id, newContent);
      setEditDialogOpen(false);
      setEditingMessage(null);
      toast.success('Message updated');
    },
    // Only rebuild when editingMessage.id actually changes, not on every new message
    [editingMessage?.id, editMessage],
  );

  const handleRevoke = useCallback(
    (message: MessageWithMeta) => {
      revokeMessage(message.id);
      toast.success('Message deleted');
    },
    [revokeMessage],
  );

  // Stable: chatInputRef is stable, no deps that change on message updates
  const handleMention = useCallback((id: string, label: string) => {
    chatInputRef.current?.insertMention(id, label, 'user');
  }, []);

  const handleSelectSearchResult = useCallback((message: RoomMessageResponse) => {
    toast.info(`Selected message from ${message.send_at}`);
    setShowSearch(false);
  }, []);

  const handleUpdateRoom = useCallback(
    async (name: string, isPublic: boolean) => {
      setIsUpdatingRoom(true);
      try {
        await updateRoom(room.id, name, isPublic);
      } finally {
        setIsUpdatingRoom(false);
      }
    },
    [room.id, updateRoom],
  );

  // Thread callbacks
  const handleOpenThread = useCallback((message: MessageWithMeta) => {
    if (!message.thread_id) return;
    const thread = threads.find(t => t.id === message.thread_id);
    if (thread) {
      setActiveThread({ thread, parentMessage: message });
    }
  }, [threads]);

  const handleCreateThread = useCallback(async (message: MessageWithMeta) => {
    if (!wsClient || message.thread_id) return;
    try {
      const thread = await wsClient.threadCreate(room.id, message.seq);
      setActiveThread({ thread, parentMessage: message });
      refreshThreads();
    } catch (err) {
      console.error('Failed to create thread:', err);
      toast.error('Failed to create thread');
    }
  }, [wsClient, room.id, refreshThreads]);

  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages.length]);

  useEffect(() => {
    setReplyingTo(null);
    setEditingMessage(null);
    setEditDialogOpen(false);
    setShowAiTasks(false);
    setShowParticipants(false);
    setShowSettings(false);
    setShowSearch(false);
    setShowMentions(false);
    setActiveThread(null);
  }, [room.id]);

  return (
    <section className="flex h-full min-w-0 flex-1 bg-background">
      <div className="flex min-w-0 flex-1 flex-col">
        <header className="flex h-12 items-center justify-between border-b border-border/70 px-4">
          <div className="min-w-0">
            <div className="flex items-center gap-2">
              <Hash className="h-5 w-5 text-muted-foreground" />
              <h1 className="truncate text-base font-semibold text-foreground">{room.room_name}</h1>
              <span
                className={cn('h-2 w-2 rounded-full', statusDotColor)}
                title={connectionLabel ?? 'Connected'}
              />
              {!room.public && (
                <span className="rounded bg-muted px-1.5 py-0.5 text-[11px] text-muted-foreground">
                  Private
                </span>
              )}
            </div>
            <p className="mt-0.5 text-xs text-muted-foreground">
              {members.length} member{members.length !== 1 ? 's' : ''}
              {messages.length > 0 && ` · ${messages.length} message${messages.length !== 1 ? 's' : ''}`}
              {!isWsConnected && connectionLabel && (
                <span className="ml-2">— {connectionLabel}</span>
              )}
            </p>
          </div>

          <div className="flex items-center gap-1">
            <Button
              variant="ghost"
              size="icon"
              className={cn('h-8 w-8', showMentions && 'bg-muted text-foreground')}
              onClick={() => {
                setShowMentions((v) => !v);
                setShowAiTasks(false);
                setShowParticipants(false);
                setShowSettings(false);
                setShowSearch(false);
              }}
              title="@ Mentions"
            >
              <Bell className="h-4 w-4" />
            </Button>

            <Button
              variant="ghost"
              size="icon"
              className={cn('h-8 w-8', showSearch && 'bg-muted text-foreground')}
              onClick={() => {
                setShowSearch((v) => !v);
                setShowAiTasks(false);
                setShowParticipants(false);
                setShowSettings(false);
                setShowMentions(false);
              }}
              title="Search messages"
            >
              <Search className="h-4 w-4" />
            </Button>

            <Button
              variant="ghost"
              size="icon"
              className={cn('h-8 w-8', showAiTasks && 'bg-muted text-foreground')}
              onClick={() => {
                setShowAiTasks((v) => !v);
                setShowParticipants(false);
                setShowSettings(false);
                setShowSearch(false);
                setShowMentions(false);
              }}
              title="AI tasks"
            >
              <Timer className="h-4 w-4" />
            </Button>

            <Button
              variant="ghost"
              size="icon"
              className={cn('h-8 w-8', showParticipants && 'bg-muted text-foreground')}
              onClick={() => {
                setShowParticipants((v) => !v);
                setShowAiTasks(false);
                setShowSettings(false);
                setShowSearch(false);
                setShowMentions(false);
              }}
              title="Members"
            >
              <Users className="h-4 w-4" />
            </Button>

            {isAdmin && (
              <Button
                variant="ghost"
                size="icon"
                className={cn('h-8 w-8', showSettings && 'bg-muted text-foreground')}
                onClick={() => {
                  setShowSettings((v) => !v);
                  setShowAiTasks(false);
                  setShowParticipants(false);
                  setShowSearch(false);
                  setShowMentions(false);
                }}
                title="Room settings"
              >
                <Settings className="h-4 w-4" />
              </Button>
            )}

            {isAdmin && (
              <Button
                variant="ghost"
                size="icon"
                className="h-8 w-8 text-muted-foreground hover:text-destructive"
                onClick={onDelete}
                title="Delete room"
              >
                <Trash2 className="h-4 w-4" />
              </Button>
            )}

            <Button variant="ghost" size="sm" className="h-8 md:hidden" onClick={onClose}>
              <ChevronLeft className="mr-1 h-4 w-4" />
              Back
            </Button>
          </div>
        </header>

        <div className="flex min-h-0 flex-1 flex-col">
          <RoomMessageList
            roomId={room.id}
            messages={messages}
            messagesEndRef={messagesEndRef}
            onInlineEdit={handleEdit}
            onViewHistory={handleViewEditHistory}
            onRevoke={handleRevoke}
            onReply={setReplyingTo}
            onMention={handleMention}
            onOpenThread={handleOpenThread}
            onCreateThread={handleCreateThread}
          />
        </div>

        <ChatInputArea
          ref={chatInputRef}
          roomName={room.room_name ?? 'room'}
          onSend={handleSend}
          isSending={false}
          members={members}
          repos={projectRepos}
          reposLoading={reposLoading}
          aiConfigs={roomAiConfigs}
          aiConfigsLoading={aiConfigsLoading}
          replyingTo={replyingTo ? { id: replyingTo.id, display_name: replyingTo.display_name ?? undefined, content: replyingTo.content } : null}
          onCancelReply={() => setReplyingTo(null)}
          draft={draft}
          onDraftChange={setDraft}
          onClearDraft={clearDraft}
        />
      </div>

      {/* Side panels with slide animations */}
      <SlidePanel open={showParticipants} width="w-[360px]">
        <RoomParticipantsPanel
          members={members}
          membersLoading={membersLoading}
          onMention={handleMention}
        />
      </SlidePanel>

      <SlidePanel open={showMentions} width="w-[380px]">
        <RoomMentionPanel
          onClose={() => setShowMentions(false)}
          onSelectNotification={(mention) => {
            toast.info(`Navigate to message in ${mention.room_name}`);
            setShowMentions(false);
          }}
        />
      </SlidePanel>

      <SlidePanel open={showSearch} width="w-[400px]">
        <RoomMessageSearch
          roomId={room.id}
          onSelectMessage={handleSelectSearchResult}
          onClose={() => setShowSearch(false)}
        />
      </SlidePanel>

      <SlidePanel open={showAiTasks} width="w-72">
        <RoomAiTasksPanel
          roomId={room.id}
          onClose={() => setShowAiTasks(false)}
        />
      </SlidePanel>

      <SlidePanel open={showSettings && isAdmin} width="w-[380px]">
        <RoomSettingsPanel
          room={room}
          onUpdate={handleUpdateRoom}
          onClose={() => setShowSettings(false)}
          isPending={isUpdatingRoom}
        />
      </SlidePanel>

      {activeThread && (
        <RoomThreadPanel
          roomId={room.id}
          thread={activeThread.thread}
          parentMessage={activeThread.parentMessage}
          onClose={() => setActiveThread(null)}
        />
      )}

      <RoomMessageEditDialog
        open={editDialogOpen}
        onOpenChange={setEditDialogOpen}
        originalContent={editingMessage?.content ?? ''}
        onConfirm={handleEditConfirm}
      />

      <RoomMessageEditHistoryDialog
        open={editHistoryDialogOpen}
        onOpenChange={setEditHistoryDialogOpen}
        messageId={selectedMessageForHistory}
        roomId={room.id}
      />

    </section>
  );
}
