'use client';

import React, {
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import {
  AlertTriangle,
} from 'lucide-react';
import {
  BillingError,
  Project,
  Message as BaseApiMessageType,
} from '@/lib/api';
import { toast } from 'sonner';
import { ChatInput } from '@/components/thread/chat-input/chat-input';
import { FileViewerModal } from '@/components/thread/file-viewer-modal';
import { SiteHeader } from '@/components/thread/thread-site-header';
import {
  ToolCallSidePanel,
  ToolCallInput,
} from '@/components/thread/tool-call-side-panel';
import { useSidebar } from '@/components/ui/sidebar';
import { useAgentStream } from '@/hooks/useAgentStream';
import { cn } from '@/lib/utils';
import { useIsMobile } from '@/hooks/use-mobile';
import { BillingErrorAlert } from '@/components/billing/usage-limit-alert';
import { isLocalMode } from '@/lib/config';
import { ThreadContent } from '@/components/thread/content/ThreadContent';
import { ThreadSkeleton } from '@/components/thread/content/ThreadSkeleton';

import {
  UnifiedMessage,
  ParsedMetadata,
  ThreadParams,
} from '@/components/thread/types';
import {
  safeJsonParse,
} from '@/components/thread/utils';
import { useThreadQuery } from '@/hooks/react-query/threads/use-threads';
import { useAddUserMessageMutation, useMessagesQuery } from '@/hooks/react-query/threads/use-messages';
import { useProjectQuery } from '@/hooks/react-query/threads/use-project';
import { useAgentRunsQuery, useStartAgentMutation, useStopAgentMutation } from '@/hooks/react-query/threads/use-agent-run';
import { useBillingStatusQuery } from '@/hooks/react-query/threads/use-billing-status';

// Extend the base Message type with the expected database fields
interface ApiMessageType extends BaseApiMessageType {
  message_id?: string;
  thread_id?: string;
  is_llm_message?: boolean;
  metadata?: string;
  created_at?: string;
  updated_at?: string;
}

// Add a simple interface for streaming tool calls
interface StreamingToolCall {
  id?: string;
  name?: string;
  arguments?: string;
  index?: number;
  xml_tag_name?: string;
}

export default function ThreadPage({
  params,
}: {
  params: Promise<ThreadParams>;
}) {
  const unwrappedParams = React.use(params);
  const threadId = unwrappedParams.threadId;
  const isMobile = useIsMobile();
  const searchParams = useSearchParams();

  const router = useRouter();
  const [messages, setMessages] = useState<UnifiedMessage[]>([]);
  const [newMessage, setNewMessage] = useState('');
  const [isLoading, setIsLoading] = useState(true);
  const [isSending, setIsSending] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [agentRunId, setAgentRunId] = useState<string | null>(null);
  const [agentStatus, setAgentStatus] = useState<
    'idle' | 'running' | 'connecting' | 'error'
  >('idle');
  const [isSidePanelOpen, setIsSidePanelOpen] = useState(false);
  const [toolCalls, setToolCalls] = useState<ToolCallInput[]>([]);
  const [currentToolIndex, setCurrentToolIndex] = useState<number>(0);
  const [autoOpenedPanel, setAutoOpenedPanel] = useState(false);
  const [initialPanelOpenAttempted, setInitialPanelOpenAttempted] =
    useState(false);

  // Billing alert state
  const [showBillingAlert, setShowBillingAlert] = useState(false);
  const [billingData, setBillingData] = useState<{
    currentUsage?: number;
    limit?: number;
    message?: string;
    accountId?: string | null;
  }>({});

  const messagesEndRef = useRef<HTMLDivElement>(null);
  const messagesContainerRef = useRef<HTMLDivElement>(null);
  const latestMessageRef = useRef<HTMLDivElement>(null);
  const [showScrollButton, setShowScrollButton] = useState(false);
  const [userHasScrolled, setUserHasScrolled] = useState(false);
  const hasInitiallyScrolled = useRef<boolean>(false);

  const [project, setProject] = useState<Project | null>(null);
  const [sandboxId, setSandboxId] = useState<string | null>(null);
  const [fileViewerOpen, setFileViewerOpen] = useState(false);
  const [projectName, setProjectName] = useState<string>('');
  const [fileToView, setFileToView] = useState<string | null>(null);

  const initialLoadCompleted = useRef<boolean>(false);
  const messagesLoadedRef = useRef(false);
  const agentRunsCheckedRef = useRef(false);
  const previousAgentStatus = useRef<typeof agentStatus>('idle');

  // Add debug mode state - check for debug=true in URL
  const [debugMode, setDebugMode] = useState(false);

  const threadQuery = useThreadQuery(threadId);
  const messagesQuery = useMessagesQuery(threadId);
  const projectId = threadQuery.data?.project_id || '';
  const projectQuery = useProjectQuery(projectId);
  const agentRunsQuery = useAgentRunsQuery(threadId);
  const billingStatusQuery = useBillingStatusQuery(); 

  const addUserMessageMutation = useAddUserMessageMutation();
  const startAgentMutation = useStartAgentMutation();
  const stopAgentMutation = useStopAgentMutation();


  const handleProjectRenamed = useCallback((newName: string) => {
    setProjectName(newName);
  }, []);

  const { state: leftSidebarState, setOpen: setLeftSidebarOpen } = useSidebar();
  const initialLayoutAppliedRef = useRef(false);

  const userClosedPanelRef = useRef(false);

  // Replace both useEffect hooks with a single one that respects user closing
  useEffect(() => {
    if (initialLoadCompleted.current && !initialPanelOpenAttempted) {
      // Only attempt to open panel once on initial load
      setInitialPanelOpenAttempted(true);

      // Open the panel with tool calls if available
      if (toolCalls.length > 0) {
        setIsSidePanelOpen(true);
        setCurrentToolIndex(toolCalls.length - 1);
      } else {
        // Only if there are messages but no tool calls yet
        if (messages.length > 0) {
          setIsSidePanelOpen(true);
        }
      }
    }
  }, [initialPanelOpenAttempted, messages, toolCalls]);

  const toggleSidePanel = useCallback(() => {
    setIsSidePanelOpen((prevIsOpen) => {
      const newState = !prevIsOpen;
      if (!newState) {
        userClosedPanelRef.current = true;
      }
      if (newState) {
        // Close left sidebar when opening side panel
        setLeftSidebarOpen(false);
      }
      return newState;
    });
  }, [setLeftSidebarOpen]);

  const handleSidePanelNavigate = useCallback((newIndex: number) => {
    setCurrentToolIndex(newIndex);
  }, []);

  useEffect(() => {
    if (!initialLayoutAppliedRef.current) {
      setLeftSidebarOpen(false);
      initialLayoutAppliedRef.current = true;
    }
  }, [setLeftSidebarOpen]);

  // Update keyboard shortcut handlers to manage both panels
  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      // CMD+I for ToolCall SidePanel
      if ((event.metaKey || event.ctrlKey) && event.key === 'i') {
        event.preventDefault();
        // If side panel is already open, just close it
        if (isSidePanelOpen) {
          setIsSidePanelOpen(false);
          userClosedPanelRef.current = true;
        } else {
          // Open side panel and ensure left sidebar is closed
          setIsSidePanelOpen(true);
          setLeftSidebarOpen(false);
        }
      }

      // CMD+B for Left Sidebar
      if ((event.metaKey || event.ctrlKey) && event.key === 'b') {
        event.preventDefault();
        // If left sidebar is expanded, collapse it
        if (leftSidebarState === 'expanded') {
          setLeftSidebarOpen(false);
        } else {
          // Otherwise expand the left sidebar and close the side panel
          setLeftSidebarOpen(true);
          if (isSidePanelOpen) {
            setIsSidePanelOpen(false);
            userClosedPanelRef.current = true;
          }
        }
      }

      if (event.key === 'Escape' && isSidePanelOpen) {
        setIsSidePanelOpen(false);
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [toggleSidePanel, isSidePanelOpen, leftSidebarState, setLeftSidebarOpen]);

  const handleNewMessageFromStream = useCallback((message: UnifiedMessage) => {
    // Log the ID of the message received from the stream
    console.log(
      `[STREAM HANDLER] Received message: ID=${message.message_id}, Type=${message.type}`,
    );

    if (!message.message_id) {
      console.warn(
        `[STREAM HANDLER] Received message is missing ID: Type=${message.type}, Content=${message.content?.substring(0, 50)}...`,
      );
    }

    setMessages((prev) => {
      const messageExists = prev.some(
        (m) => m.message_id === message.message_id,
      );
      if (messageExists) {
        return prev.map((m) =>
          m.message_id === message.message_id ? message : m,
        );
      } else {
        return [...prev, message];
      }
    });

    // If we received a tool message, refresh the tool panel
    if (message.type === 'tool') {
      setAutoOpenedPanel(false);
    }
  }, []);

  const handleStreamStatusChange = useCallback((hookStatus: string) => {
    console.log(`[PAGE] Hook status changed: ${hookStatus}`);
    switch (hookStatus) {
      case 'idle':
      case 'completed':
      case 'stopped':
      case 'agent_not_running':
      case 'error':
      case 'failed':
        setAgentStatus('idle');
        setAgentRunId(null);
        // Reset auto-opened state when agent completes to trigger tool detection
        setAutoOpenedPanel(false);

        // After terminal states, we should scroll to bottom to show latest messages
        // The hook will already have refetched messages by this point
        if (
          [
            'completed',
            'stopped',
            'agent_not_running',
            'error',
            'failed',
          ].includes(hookStatus)
        ) {
          scrollToBottom('smooth');
        }
        break;
      case 'connecting':
        setAgentStatus('connecting');
        break;
      case 'streaming':
        setAgentStatus('running');
        break;
    }
  }, []);

  const handleStreamError = useCallback((errorMessage: string) => {
    console.error(`[PAGE] Stream hook error: ${errorMessage}`);
    if (
      !errorMessage.toLowerCase().includes('not found') &&
      !errorMessage.toLowerCase().includes('agent run is not running')
    ) {
      toast.error(`Stream Error: ${errorMessage}`);
    }
  }, []);

  const handleStreamClose = useCallback(() => {
    console.log(`[PAGE] Stream hook closed with final status: ${agentStatus}`);
  }, [agentStatus]);

  const {
    status: streamHookStatus,
    textContent: streamingTextContent,
    toolCall: streamingToolCall,
    error: streamError,
    agentRunId: currentHookRunId,
    startStreaming,
    stopStreaming,
  } = useAgentStream(
    {
      onMessage: handleNewMessageFromStream,
      onStatusChange: handleStreamStatusChange,
      onError: handleStreamError,
      onClose: handleStreamClose,
    },
    threadId,
    setMessages,
  );

  useEffect(() => {
    if (agentRunId && agentRunId !== currentHookRunId) {
      console.log(
        `[PAGE] Target agentRunId set to ${agentRunId}, initiating stream...`,
      );
      startStreaming(agentRunId);
    }
  }, [agentRunId, startStreaming, currentHookRunId]);

  const handleKeyDown = (event: KeyboardEvent) => {
    if (event.key === 'Escape' && isSidePanelOpen) {
      setIsSidePanelOpen(false);
    }
  };

  useEffect(() => {
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [isSidePanelOpen]);

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

  // Effect to load initial data using React Query
  useEffect(() => {
    let isMounted = true;

    async function initializeData() {
      if (!initialLoadCompleted.current) setIsLoading(true);
      setError(null);

      try {
        if (!threadId) throw new Error('Thread ID is required');

        // Check if we have thread data
        if (threadQuery.isError) {
          throw new Error('Failed to load thread data: ' + threadQuery.error);
        }

        if (!isMounted) return;

        // Process project data when available
        if (projectQuery.data) {
          // Set project data
          setProject(projectQuery.data);

          // Make sure sandbox ID is set correctly
          if (typeof projectQuery.data.sandbox === 'string') {
            setSandboxId(projectQuery.data.sandbox);
          } else if (projectQuery.data.sandbox?.id) {
            setSandboxId(projectQuery.data.sandbox.id);
          }

          setProjectName(projectQuery.data.name || '');
        }

        // Process messages data when available
        if (messagesQuery.data && !messagesLoadedRef.current) {
          // Map API message type to UnifiedMessage type
          const unifiedMessages = (messagesQuery.data || [])
            .filter((msg) => msg.type !== 'status')
            .map((msg: ApiMessageType) => ({
              message_id: msg.message_id || null,
              thread_id: msg.thread_id || threadId,
              type: (msg.type || 'system') as UnifiedMessage['type'],
              is_llm_message: Boolean(msg.is_llm_message),
              content: msg.content || '',
              metadata: msg.metadata || '{}',
              created_at: msg.created_at || new Date().toISOString(),
              updated_at: msg.updated_at || new Date().toISOString(),
            }));

          setMessages(unifiedMessages);
          console.log('[PAGE] Loaded Messages (excluding status, keeping browser_state):', unifiedMessages.length);
          messagesLoadedRef.current = true;

          if (!hasInitiallyScrolled.current) {
            scrollToBottom('auto');
            hasInitiallyScrolled.current = true;
          }
        }

        // Check for active agent runs
        if (agentRunsQuery.data && !agentRunsCheckedRef.current && isMounted) {
          console.log('[PAGE] Checking for active agent runs...');
          agentRunsCheckedRef.current = true;

          const activeRun = agentRunsQuery.data.find((run) => run.status === 'running');
          if (activeRun && isMounted) {
            console.log('[PAGE] Found active run on load:', activeRun.id);
            setAgentRunId(activeRun.id);
          } else {
            console.log('[PAGE] No active agent runs found');
            if (isMounted) setAgentStatus('idle');
          }
        }

        // Mark initialization as complete when we have the core data
        if (threadQuery.data && messagesQuery.data && agentRunsQuery.data) {
          initialLoadCompleted.current = true;
          setIsLoading(false);
        }

      } catch (err) {
        console.error('Error loading thread data:', err);
        if (isMounted) {
          const errorMessage =
            err instanceof Error ? err.message : 'Failed to load thread';
          setError(errorMessage);
          toast.error(errorMessage);
          setIsLoading(false);
        }
      }
    }

    if (threadId) {
      initializeData();
    }

    return () => {
      isMounted = false;
    };
  }, [
    threadId, 
    threadQuery.data, 
    threadQuery.isError, 
    threadQuery.error,
    projectQuery.data,
    messagesQuery.data,
    agentRunsQuery.data
  ]);

  const handleSubmitMessage = useCallback(
    async (
      message: string,
      options?: { model_name?: string; enable_thinking?: boolean },
    ) => {
      if (!message.trim()) return;
      setIsSending(true);

      const optimisticUserMessage: UnifiedMessage = {
        message_id: `temp-${Date.now()}`,
        thread_id: threadId,
        type: 'user',
        is_llm_message: false,
        content: message,
        metadata: '{}',
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      };

      setMessages((prev) => [...prev, optimisticUserMessage]);
      setNewMessage('');
      scrollToBottom('smooth');

      try {
        // Use React Query mutations instead of direct API calls
        const messagePromise = addUserMessageMutation.mutateAsync({ 
          threadId, 
          message 
        });
        
        const agentPromise = startAgentMutation.mutateAsync({ 
          threadId, 
          options 
        });

        const results = await Promise.allSettled([messagePromise, agentPromise]);

        // Handle failure to add the user message
        if (results[0].status === 'rejected') {
          const reason = results[0].reason;
          console.error("Failed to send message:", reason);
          throw new Error(`Failed to send message: ${reason?.message || reason}`);
        }

        // Handle failure to start the agent
        if (results[1].status === 'rejected') {
          const error = results[1].reason;
          console.error("Failed to start agent:", error);

          // Check if it's our custom BillingError (402)
          if (error instanceof BillingError) {
            console.log("Caught BillingError:", error.detail);
            // Extract billing details
            setBillingData({
              // Note: currentUsage and limit might not be in the detail from the backend yet
              currentUsage: error.detail.currentUsage as number | undefined,
              limit: error.detail.limit as number | undefined,
              message: error.detail.message || 'Monthly usage limit reached. Please upgrade.', // Use message from error detail
              accountId: project?.account_id || null // Pass account ID
            });
            setShowBillingAlert(true);

            // Remove the optimistic message since the agent couldn't start
            setMessages(prev => prev.filter(m => m.message_id !== optimisticUserMessage.message_id));
            return; // Stop further execution in this case
          }

          // Handle other agent start errors
          throw new Error(`Failed to start agent: ${error?.message || error}`);
        }

        // If agent started successfully
        const agentResult = results[1].value;
        setAgentRunId(agentResult.agent_run_id);
        
        // Refresh queries after successful operations
        messagesQuery.refetch();
        agentRunsQuery.refetch();
        
      } catch (err) {
        // Catch errors from addUserMessage or non-BillingError agent start errors
        console.error('Error sending message or starting agent:', err);
        // Don't show billing alert here, only for specific BillingError
        if (!(err instanceof BillingError)) {
          toast.error(err instanceof Error ? err.message : 'Operation failed');
        }
        // Ensure optimistic message is removed on any error during submit
        setMessages((prev) =>
          prev.filter((m) => m.message_id !== optimisticUserMessage.message_id),
        );
      } finally {
        setIsSending(false);
      }
    },
    [threadId, project?.account_id, addUserMessageMutation, startAgentMutation, messagesQuery, agentRunsQuery],
  );

  const handleStopAgent = useCallback(async () => {
    console.log(`[PAGE] Requesting agent stop via hook.`);
    setAgentStatus('idle');

    // First stop the streaming and let the hook handle refetching
    await stopStreaming();
    
    // Use React Query's stopAgentMutation if we have an agent run ID
    if (agentRunId) {
      try {
        await stopAgentMutation.mutateAsync(agentRunId);
        // Refresh agent runs after stopping
        agentRunsQuery.refetch();
      } catch (error) {
        console.error('Error stopping agent:', error);
      }
    }
  }, [stopStreaming, agentRunId, stopAgentMutation, agentRunsQuery]);

  useEffect(() => {
    const lastMsg = messages[messages.length - 1];
    const isNewUserMessage = lastMsg?.type === 'user';
    if ((isNewUserMessage || agentStatus === 'running') && !userHasScrolled) {
      scrollToBottom('smooth');
    }
  }, [messages, agentStatus, userHasScrolled]);

  useEffect(() => {
    if (!latestMessageRef.current || messages.length === 0) return;
    const observer = new IntersectionObserver(
      ([entry]) => setShowScrollButton(!entry?.isIntersecting),
      { root: messagesContainerRef.current, threshold: 0.1 },
    );
    observer.observe(latestMessageRef.current);
    return () => observer.disconnect();
  }, [messages, streamingTextContent, streamingToolCall]);

  useEffect(() => {
    console.log(`[PAGE] 🔄 Page AgentStatus: ${agentStatus}, Hook Status: ${streamHookStatus}, Target RunID: ${agentRunId || 'none'}, Hook RunID: ${currentHookRunId || 'none'}`);

    // If the stream hook reports completion/stopping but our UI hasn't updated
    if ((streamHookStatus === 'completed' || streamHookStatus === 'stopped' ||
      streamHookStatus === 'agent_not_running' || streamHookStatus === 'error') &&
      (agentStatus === 'running' || agentStatus === 'connecting')) {
      console.log('[PAGE] Detected hook completed but UI still shows running, updating status');
      setAgentStatus('idle');
      setAgentRunId(null);
      setAutoOpenedPanel(false);
    }
  }, [agentStatus, streamHookStatus, agentRunId, currentHookRunId]);

  const handleOpenFileViewer = useCallback((filePath?: string) => {
    if (filePath) {
      setFileToView(filePath);
    } else {
      setFileToView(null);
    }
    setFileViewerOpen(true);
  }, []);

  // Process the assistant call data
  const toolViewAssistant = useCallback(
    (assistantContent?: string, toolContent?: string) => {
      // This needs to stay simple as it's meant for the side panel tool call view
      if (!assistantContent) return null;

      return (
        <div className="space-y-1">
          <div className="text-xs font-medium text-muted-foreground">
            Assistant Message
          </div>
          <div className="rounded-md border bg-muted/50 p-3">
            <div className="text-xs prose prose-xs dark:prose-invert chat-markdown max-w-none">{assistantContent}</div>
          </div>
        </div>
      );
    },
    [],
  );

  // Process the tool result data
  const toolViewResult = useCallback(
    (toolContent?: string, isSuccess?: boolean) => {
      if (!toolContent) return null;

      return (
        <div className="space-y-1">
          <div className="flex justify-between items-center">
            <div className="text-xs font-medium text-muted-foreground">
              Tool Result
            </div>
            <div
              className={`px-2 py-0.5 rounded-full text-xs ${isSuccess
                ? 'bg-green-50 text-green-700 dark:bg-green-900 dark:text-green-300'
                : 'bg-red-50 text-red-700 dark:bg-red-900 dark:text-red-300'
                }`}
            >
              {isSuccess ? 'Success' : 'Failed'}
            </div>
          </div>
          <div className="rounded-md border bg-muted/50 p-3">
            <div className="text-xs prose prose-xs dark:prose-invert chat-markdown max-w-none">{toolContent}</div>
          </div>
        </div>
      );
    },
    [],
  );

  // Automatically detect and populate tool calls from messages
  useEffect(() => {
    // Calculate historical tool pairs regardless of panel state
    const historicalToolPairs: ToolCallInput[] = [];
    const assistantMessages = messages.filter(m => m.type === 'assistant' && m.message_id);

    assistantMessages.forEach(assistantMsg => {
      const resultMessage = messages.find(toolMsg => {
        if (toolMsg.type !== 'tool' || !toolMsg.metadata || !assistantMsg.message_id) return false;
        try {
          const metadata = JSON.parse(toolMsg.metadata);
          return metadata.assistant_message_id === assistantMsg.message_id;
        } catch (e) {
          return false;
        }
      });

      if (resultMessage) {
        // Determine tool name from assistant message content
        let toolName = 'unknown';
        try {
          // Try to extract tool name from content
          const xmlMatch = assistantMsg.content.match(
            /<([a-zA-Z\-_]+)(?:\s+[^>]*)?>|<([a-zA-Z\-_]+)(?:\s+[^>]*)?\/>/,
          );
          if (xmlMatch) {
            toolName = xmlMatch[1] || xmlMatch[2] || 'unknown';
          } else {
            // Fallback to checking for tool_calls JSON structure
            const assistantContentParsed = safeJsonParse<{
              tool_calls?: { name: string }[];
            }>(assistantMsg.content, {});
            if (
              assistantContentParsed.tool_calls &&
              assistantContentParsed.tool_calls.length > 0
            ) {
              toolName = assistantContentParsed.tool_calls[0].name || 'unknown';
            }
          }
        } catch { }

        // Skip adding <ask> tags to the tool calls
        if (toolName === 'ask' || toolName === 'complete') {
          return;
        }

        let isSuccess = true;
        try {
          const toolContent = resultMessage.content?.toLowerCase() || '';
          isSuccess = !(toolContent.includes('failed') ||
            toolContent.includes('error') ||
            toolContent.includes('failure'));
        } catch { }

        historicalToolPairs.push({
          assistantCall: {
            name: toolName,
            content: assistantMsg.content,
            timestamp: assistantMsg.created_at,
          },
          toolResult: {
            content: resultMessage.content,
            isSuccess: isSuccess,
            timestamp: resultMessage.created_at,
          },
        });
      }
    });

    // Always update the toolCalls state
    setToolCalls(historicalToolPairs);

    // Logic to open/update the panel index
    if (historicalToolPairs.length > 0) {
      // If the panel is open (or was just auto-opened) and the user didn't close it
      if (isSidePanelOpen && !userClosedPanelRef.current) {
        // Always jump to the latest tool call index
        setCurrentToolIndex(historicalToolPairs.length - 1);
      } else if (!isSidePanelOpen && !autoOpenedPanel && !userClosedPanelRef.current) {
        // Auto-open the panel only the first time tools are detected
        setCurrentToolIndex(historicalToolPairs.length - 1);
        setIsSidePanelOpen(true);
        setAutoOpenedPanel(true);
      }
    }
  }, [messages, isSidePanelOpen, autoOpenedPanel]); // Rerun when messages or panel state changes

  // Reset auto-opened state when panel is closed
  useEffect(() => {
    if (!isSidePanelOpen) {
      setAutoOpenedPanel(false);
    }
  }, [isSidePanelOpen]);

  // Update handleToolClick to respect user closing preference and navigate correctly
  const handleToolClick = useCallback((clickedAssistantMessageId: string | null, clickedToolName: string) => {
    // Explicitly ignore ask tags from opening the side panel
    if (clickedToolName === 'ask') {
      return;
    }

    if (!clickedAssistantMessageId) {
      console.warn("Clicked assistant message ID is null. Cannot open side panel.");
      toast.warning("Cannot view details: Assistant message ID is missing.");
      return;
    }

    // Reset user closed state when explicitly clicking a tool
    userClosedPanelRef.current = false;

    console.log(
      '[PAGE] Tool Click Triggered. Assistant Message ID:',
      clickedAssistantMessageId,
      'Tool Name:',
      clickedToolName,
    );

    // Find the index of the tool call associated with the clicked assistant message
    const toolIndex = toolCalls.findIndex((tc) => {
      // Check if the assistant message ID matches the one stored in the tool result's metadata
      if (!tc.toolResult?.content || tc.toolResult.content === 'STREAMING')
        return false; // Skip streaming or incomplete calls

      // Directly compare assistant message IDs if available in the structure
      // Find the original assistant message based on the ID
      const assistantMessage = messages.find(
        (m) =>
          m.message_id === clickedAssistantMessageId &&
          m.type === 'assistant',
      );
      if (!assistantMessage) return false;

      // Find the corresponding tool message using metadata
      const toolMessage = messages.find((m) => {
        if (m.type !== 'tool' || !m.metadata) return false;
        try {
          const metadata = safeJsonParse<ParsedMetadata>(m.metadata, {});
          return (
            metadata.assistant_message_id === assistantMessage.message_id
          );
        } catch {
          return false;
        }
      });

      // Check if the current toolCall 'tc' corresponds to this assistant/tool message pair
      return (
        tc.assistantCall?.content === assistantMessage.content &&
        tc.toolResult?.content === toolMessage?.content
      );
    });

    if (toolIndex !== -1) {
      console.log(
        `[PAGE] Found tool call at index ${toolIndex} for assistant message ${clickedAssistantMessageId}`,
      );
      setCurrentToolIndex(toolIndex);
      setIsSidePanelOpen(true); // Explicitly open the panel
    } else {
      console.warn(
        `[PAGE] Could not find matching tool call in toolCalls array for assistant message ID: ${clickedAssistantMessageId}`,
      );
      toast.info('Could not find details for this tool call.');
      // Optionally, still open the panel but maybe at the last index or show a message?
      // setIsSidePanelOpen(true);
    }
  },
    [messages, toolCalls],
  ); // Add toolCalls as a dependency

  // Handle streaming tool calls
  const handleStreamingToolCall = useCallback(
    (toolCall: StreamingToolCall | null) => {
      if (!toolCall) return;

      const toolName = toolCall.name || toolCall.xml_tag_name || 'Unknown Tool';

      // Skip <ask> tags from showing in the side panel during streaming
      if (toolName === 'ask' || toolName === 'complete') {
        return;
      }

      console.log('[STREAM] Received tool call:', toolName);

      // If user explicitly closed the panel, don't reopen it for streaming calls
      if (userClosedPanelRef.current) return;

      // Create a properly formatted tool call input for the streaming tool
      // that matches the format of historical tool calls
      const toolArguments = toolCall.arguments || '';

      // Format the arguments in a way that matches the expected XML format for each tool
      // This ensures the specialized tool views render correctly
      let formattedContent = toolArguments;
      if (
        toolName.toLowerCase().includes('command') &&
        !toolArguments.includes('<execute-command>')
      ) {
        formattedContent = `<execute-command>${toolArguments}</execute-command>`;
      } else if (
        toolName.toLowerCase().includes('file') &&
        !toolArguments.includes('<create-file>')
      ) {
        // For file operations, wrap with appropriate tag if not already wrapped
        const fileOpTags = ['create-file', 'delete-file', 'full-file-rewrite'];
        const matchingTag = fileOpTags.find((tag) =>
          toolName.toLowerCase().includes(tag),
        );
        if (matchingTag && !toolArguments.includes(`<${matchingTag}>`)) {
          formattedContent = `<${matchingTag}>${toolArguments}</${matchingTag}>`;
        }
      }

      const newToolCall: ToolCallInput = {
        assistantCall: {
          name: toolName,
          content: formattedContent,
          timestamp: new Date().toISOString(),
        },
        // For streaming tool calls, provide empty content that indicates streaming
        toolResult: {
          content: 'STREAMING',
          isSuccess: true,
          timestamp: new Date().toISOString(),
        },
      };

      // Update the tool calls state to reflect the streaming tool
      setToolCalls((prev) => {
        // If the same tool is already being streamed, update it instead of adding a new one
        if (prev.length > 0 && prev[0].assistantCall.name === toolName) {
          return [
            {
              ...prev[0],
              assistantCall: {
                ...prev[0].assistantCall,
                content: formattedContent,
              },
            },
          ];
        }
        return [newToolCall];
      });

      setCurrentToolIndex(0);
      setIsSidePanelOpen(true);
    },
    [],
  );

  // SEO title update
  useEffect(() => {
    if (projectName) {
      // Update document title when project name changes
      document.title = `${projectName} | Kortix Suna`;

      // Update meta tags for SEO
      const metaDescription = document.querySelector(
        'meta[name="description"]',
      );
      if (metaDescription) {
        metaDescription.setAttribute(
          'content',
          `${projectName} - Interactive agent conversation powered by Kortix Suna`,
        );
      }

      // Update OpenGraph tags if they exist
      const ogTitle = document.querySelector('meta[property="og:title"]');
      if (ogTitle) {
        ogTitle.setAttribute('content', `${projectName} | Kortix Suna`);
      }

      const ogDescription = document.querySelector(
        'meta[property="og:description"]',
      );
      if (ogDescription) {
        ogDescription.setAttribute(
          'content',
          `Interactive AI conversation for ${projectName}`,
        );
      }
    }
  }, [projectName]);

  // Update messages when they change in the query
  useEffect(() => {
    if (messagesQuery.data && messagesQuery.status === 'success') {
      // Only update if we're not in initial loading and the agent isn't running
      if (!isLoading && agentStatus !== 'running' && agentStatus !== 'connecting') {
        // Map API message type to UnifiedMessage type
        const unifiedMessages = (messagesQuery.data || [])
          .filter((msg) => msg.type !== 'status')
          .map((msg: ApiMessageType) => ({
            message_id: msg.message_id || null,
            thread_id: msg.thread_id || threadId,
            type: (msg.type || 'system') as UnifiedMessage['type'],
            is_llm_message: Boolean(msg.is_llm_message),
            content: msg.content || '',
            metadata: msg.metadata || '{}',
            created_at: msg.created_at || new Date().toISOString(),
            updated_at: msg.updated_at || new Date().toISOString(),
          }));

        setMessages(unifiedMessages);
        // Reset auto-opened panel to allow tool detection with fresh messages
        setAutoOpenedPanel(false);
        scrollToBottom('smooth');
      }
    }
  }, [messagesQuery.data, messagesQuery.status, isLoading, agentStatus, threadId]);

  // Check billing status and handle billing limit
  const checkBillingLimits = useCallback(async () => {
    // Skip billing checks in local development mode
    if (isLocalMode()) {
      console.log(
        'Running in local development mode - billing checks are disabled',
      );
      return false;
    }

    try {
      // Use React Query to get billing status
      await billingStatusQuery.refetch();
      const result = billingStatusQuery.data;

      if (result && !result.can_run) {
        setBillingData({
          currentUsage: result.subscription?.minutes_limit || 0,
          limit: result.subscription?.minutes_limit || 0,
          message: result.message || 'Usage limit reached',
          accountId: project?.account_id || null,
        });
        setShowBillingAlert(true);
        return true;
      }
      return false;
    } catch (err) {
      console.error('Error checking billing status:', err);
      return false;
    }
  }, [project?.account_id, billingStatusQuery]);

  useEffect(() => {
    let timeoutId: NodeJS.Timeout;
    const shouldCheckBilling = 
      project?.account_id && 
      (initialLoadCompleted.current || 
       (messagesLoadedRef.current && !isLoading) ||
       (previousAgentStatus.current === 'running' && agentStatus === 'idle'));

    if (shouldCheckBilling) {
      timeoutId = setTimeout(() => {
        checkBillingLimits();
      }, 500);
    }

    previousAgentStatus.current = agentStatus;

    return () => {
      if (timeoutId) {
        clearTimeout(timeoutId);
      }
    };
  }, [
    project?.account_id,
    isLoading,
    agentStatus,
    checkBillingLimits
  ]);

  // Check for debug mode in URL on initial load and when URL changes
  useEffect(() => {
    const debugParam = searchParams.get('debug');
    setDebugMode(debugParam === 'true');
  }, [searchParams]);

  // Main rendering function for the thread page
  if (!initialLoadCompleted.current || isLoading) {
    // Use the new ThreadSkeleton component instead of inline skeleton
    return <ThreadSkeleton isSidePanelOpen={isSidePanelOpen} />;
  } else if (error) {
    // Error state...
    return (
      <div className="flex h-screen">
        <div
          className={`flex flex-col flex-1 overflow-hidden transition-all duration-200 ease-in-out ${(isSidePanelOpen && initialLoadCompleted.current) ? 'mr-[90%] sm:mr-[450px] md:mr-[500px] lg:mr-[550px] xl:mr-[650px]' : ''}`}
        >
          <SiteHeader
            threadId={threadId}
            projectName={projectName}
            projectId={project?.id || ''}
            onViewFiles={handleOpenFileViewer}
            onToggleSidePanel={toggleSidePanel}
            isMobileView={isMobile}
            debugMode={debugMode}
          />
          <div className="flex flex-1 items-center justify-center p-4">
            <div className="flex w-full max-w-md flex-col items-center gap-4 rounded-lg border bg-card p-6 text-center">
              <div className="rounded-full bg-destructive/10 p-3">
                <AlertTriangle className="h-6 w-6 text-destructive" />
              </div>
              <h2 className="text-lg font-semibold text-destructive">
                Thread Not Found
              </h2>
              <p className="text-sm text-muted-foreground">
                {error.includes(
                  'JSON object requested, multiple (or no) rows returned',
                )
                  ? 'This thread either does not exist or you do not have access to it.'
                  : error
                }
              </p>
            </div>
          </div>
        </div>
        <ToolCallSidePanel
          isOpen={isSidePanelOpen && initialLoadCompleted.current}
          onClose={() => {
            setIsSidePanelOpen(false);
            userClosedPanelRef.current = true;
            setAutoOpenedPanel(true);
          }}
          toolCalls={toolCalls}
          messages={messages as ApiMessageType[]}
          agentStatus={agentStatus}
          currentIndex={currentToolIndex}
          onNavigate={handleSidePanelNavigate}
          project={project || undefined}
          renderAssistantMessage={toolViewAssistant}
          renderToolResult={toolViewResult}
          isLoading={!initialLoadCompleted.current || isLoading}
        />

        {
          sandboxId && (
            <FileViewerModal
              open={fileViewerOpen}
              onOpenChange={setFileViewerOpen}
              sandboxId={sandboxId}
              initialFilePath={fileToView}
              project={project || undefined}
            />
          )
        }

        {/* Billing Alert for usage limit */}
        <BillingErrorAlert
          message={billingData.message}
          currentUsage={billingData.currentUsage}
          limit={billingData.limit}
          accountId={billingData.accountId}
          onDismiss={() => setShowBillingAlert(false)}
          isOpen={showBillingAlert}
        />
      </div>
    );
  } else {
    return (
      <div className="flex h-screen">
        {/* Render debug mode indicator when active */}
        {debugMode && (
          <div className="fixed top-16 right-4 bg-amber-500 text-black text-xs px-2 py-1 rounded-md shadow-md z-50">
            Debug Mode
          </div>
        )}
        <div
          className={`flex flex-col flex-1 overflow-hidden transition-all duration-200 ease-in-out ${(!initialLoadCompleted.current || isSidePanelOpen) ? 'mr-[90%] sm:mr-[450px] md:mr-[500px] lg:mr-[550px] xl:mr-[650px]' : ''}`}
        >
          <SiteHeader
            threadId={threadId}
            projectName={projectName}
            projectId={project?.id || ''}
            onViewFiles={handleOpenFileViewer}
            onToggleSidePanel={toggleSidePanel}
            onProjectRenamed={handleProjectRenamed}
            isMobileView={isMobile}
            debugMode={debugMode}
          />

          {/* Pass debugMode to ThreadContent component */}
          <ThreadContent
            messages={messages}
            streamingTextContent={streamingTextContent}
            streamingToolCall={streamingToolCall}
            agentStatus={agentStatus}
            handleToolClick={handleToolClick}
            handleOpenFileViewer={handleOpenFileViewer}
            readOnly={false}
            streamHookStatus={streamHookStatus}
            sandboxId={sandboxId}
            project={project}
            debugMode={debugMode}
          />

          <div
            className={cn(
              "fixed bottom-0 z-10 bg-gradient-to-t from-background via-background/90 to-transparent px-4 pt-8 transition-all duration-200 ease-in-out",
              leftSidebarState === 'expanded' ? 'left-[72px] lg:left-[256px]' : 'left-[72px]',
              isSidePanelOpen ? 'right-[90%] sm:right-[450px] md:right-[500px] lg:right-[550px] xl:right-[650px]' : 'right-0',
              isMobile ? 'left-0 right-0' : ''
            )}>
            <div className={cn(
              "mx-auto",
              isMobile ? "w-full px-4" : "max-w-3xl"
            )}>
              <ChatInput
                value={newMessage}
                onChange={setNewMessage}
                onSubmit={handleSubmitMessage}
                placeholder="Ask Suna anything..."
                loading={isSending}
                disabled={isSending || agentStatus === 'running' || agentStatus === 'connecting'}
                isAgentRunning={agentStatus === 'running' || agentStatus === 'connecting'}
                onStopAgent={handleStopAgent}
                autoFocus={!isLoading}
                onFileBrowse={handleOpenFileViewer}
                sandboxId={sandboxId || undefined}
              />
            </div>
          </div>
        </div>

        <ToolCallSidePanel
          isOpen={isSidePanelOpen && initialLoadCompleted.current}
          onClose={() => {
            setIsSidePanelOpen(false);
            userClosedPanelRef.current = true;
            setAutoOpenedPanel(true);
          }}
          toolCalls={toolCalls}
          messages={messages as ApiMessageType[]}
          agentStatus={agentStatus}
          currentIndex={currentToolIndex}
          onNavigate={handleSidePanelNavigate}
          project={project || undefined}
          renderAssistantMessage={toolViewAssistant}
          renderToolResult={toolViewResult}
          isLoading={!initialLoadCompleted.current || isLoading}
        />

        {sandboxId && (
          <FileViewerModal
            open={fileViewerOpen}
            onOpenChange={setFileViewerOpen}
            sandboxId={sandboxId}
            initialFilePath={fileToView}
            project={project || undefined}
          />
        )}

        {/* Billing Alert for usage limit */}
        <BillingErrorAlert
          message={billingData.message}
          currentUsage={billingData.currentUsage}
          limit={billingData.limit}
          accountId={billingData.accountId}
          onDismiss={() => setShowBillingAlert(false)}
          isOpen={showBillingAlert}
        />
      </div>
    );
  }
}