import { useCallback, useEffect, useRef, useState } from 'react';
import type { ChatMessage, ToolActivity } from '../../hooks/useChat';
import MessageBubble from './MessageBubble';
import TypingIndicator from './TypingIndicator';
import ImageLightbox, { type LightboxImage } from './ImageLightbox';

interface Props {
  messages: ChatMessage[];
  streaming: boolean;
  streamBuffer: string;
  tools: ToolActivity[];
  hasMore?: boolean;
  onLoadOlder?: () => void;
}

export default function MessageList({ messages, streaming, streamBuffer, tools, hasMore, onLoadOlder }: Props) {
  const bottomRef = useRef<HTMLDivElement>(null);
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const sentinelRef = useRef<HTMLDivElement>(null);
  const isInitialLoad = useRef(true);
  const [loadingOlder, setLoadingOlder] = useState(false);
  const [lightbox, setLightbox] = useState<{ images: LightboxImage[]; index: number } | null>(null);

  const handleImageClick = useCallback((images: LightboxImage[], index: number) => {
    setLightbox({ images, index });
  }, []);

  // Scroll to bottom — instant on initial load, smooth afterwards
  useEffect(() => {
    if (!bottomRef.current) return;
    if (isInitialLoad.current && messages.length > 0) {
      bottomRef.current.scrollIntoView({ behavior: 'instant' as ScrollBehavior });
      isInitialLoad.current = false;
    } else if (!isInitialLoad.current) {
      bottomRef.current.scrollIntoView({ behavior: 'smooth' });
    }
  }, [messages, streamBuffer, tools, streaming]);

  // IntersectionObserver for infinite scroll up
  useEffect(() => {
    if (!sentinelRef.current || !onLoadOlder) return;
    const sentinel = sentinelRef.current;

    const observer = new IntersectionObserver(
      async (entries) => {
        if (entries[0].isIntersecting && hasMore && !loadingOlder) {
          const container = scrollContainerRef.current;
          if (!container) return;

          // Save scroll position before loading
          const prevScrollHeight = container.scrollHeight;

          setLoadingOlder(true);
          await onLoadOlder();
          setLoadingOlder(false);

          // Restore scroll position after DOM update
          requestAnimationFrame(() => {
            const newScrollHeight = container.scrollHeight;
            container.scrollTop += newScrollHeight - prevScrollHeight;
          });
        }
      },
      { root: scrollContainerRef.current, threshold: 0.1 },
    );

    observer.observe(sentinel);
    return () => observer.disconnect();
  }, [hasMore, loadingOlder, onLoadOlder]);

  // Get the latest running tool name (only the current one)
  const currentTool = tools.length > 0 ? tools[tools.length - 1] : null;
  const currentToolName = currentTool?.status === 'running' ? currentTool.name : undefined;

  return (
    <div ref={scrollContainerRef} className="flex-1 overflow-y-auto overflow-x-hidden p-4 flex flex-col min-h-0">
      {/* Spacer pushes messages to the bottom when few */}
      <div className="flex-1" />

      {/* Sentinel for infinite scroll + loading spinner */}
      {hasMore && (
        <div ref={sentinelRef} className="flex justify-center py-2">
          {loadingOlder && (
            <div className="w-5 h-5 border-2 border-muted-foreground/20 border-t-muted-foreground/60 rounded-full animate-spin" />
          )}
        </div>
      )}

      {messages.length === 0 && !streaming && (
        <div className="flex items-center justify-center py-8 text-muted-foreground text-sm">
          Start a conversation with your Bloby..
        </div>
      )}

      <div className="space-y-3">
        {messages.map((msg) => (
          <MessageBubble
            key={msg.id}
            role={msg.role}
            content={msg.content}
            timestamp={msg.timestamp}
            hasAttachments={msg.hasAttachments}
            audioData={msg.audioData}
            attachments={msg.attachments}
            onImageClick={handleImageClick}
            transcribing={msg.transcribing}
          />
        ))}

        {/* Typing / streaming bubble — shown as soon as agent starts.
            Strip the leading "[Mac] " / "[Alexa] " / etc. that the agent sometimes echoes. */}
        {streaming && (
          <TypingIndicator
            text={streamBuffer
              .replace(/^\[(Mac|Alexa|WhatsApp|Chat|PWA|Workspace|workspace)\]\s*/i, '')
              // Hide Mac-notch card markup as it streams (complete blocks, then any trailing unclosed one)
              .replace(/<notch_card\s+type="[^"]*">[\s\S]*?<\/notch_card>/gi, '')
              .replace(/<notch_html>[\s\S]*?<\/notch_html>/gi, '')
              .replace(/<notch_(?:html|card)\b[\s\S]*$/i, '')
              // Same for <morphy_action> Mac screen actions (complete blocks, then a trailing unclosed one)
              .replace(/<morphy_action\s+type="[^"]*">[\s\S]*?<\/morphy_action>/gi, '')
              .replace(/<morphy_action\b[\s\S]*$/i, '')
              // Same for the current <mac_actions> JSON-array blocks
              .replace(/<mac_actions>[\s\S]*?<\/mac_actions>/gi, '')
              .replace(/<mac_actions\b[\s\S]*$/i, '')
              .trimEnd()}
            toolName={currentToolName}
          />
        )}
      </div>
      <div ref={bottomRef} />

      {lightbox && (
        <ImageLightbox
          images={lightbox.images}
          index={lightbox.index}
          onClose={() => setLightbox(null)}
          onNavigate={(i) => setLightbox((prev) => prev ? { ...prev, index: i } : null)}
        />
      )}
    </div>
  );
}
