import type { KeyboardEvent } from 'react';
import { useState, useRef, useEffect } from 'react';
import { useUserVisibleModels } from 'state/useModels';
import { useChat, type UseChatType } from '../../../hooks/useChat';
import { useAttachments } from '../useAttachments';
import { useSlashCommands } from '../useSlashCommands';

type UseEmbeddableChatParams = {
  sessionId?: string;
  initialModel?: string | null;
  initialMessage?: {
    text: string;
    attachmentIds?: string[];
    images?: { filename: string; dataUrl: string }[];
    cwd?: { root?: string; path: string };
  };
  defaultInput?: string;
  promptPrefix?: string;
  cwd?: { root?: string; path: string };
  replaceUrl?: boolean;
  autoSend?: boolean;
  chat?: UseChatType;
  context?: string;
  contextId?: string;
};

export function useEmbeddableChat(params: UseEmbeddableChatParams, onMessageComplete?: () => void) {
  const { initialMessage, defaultInput = '', promptPrefix, cwd, autoSend = false, chat: externalChat } = params;

  const internalChat = useChat(params.sessionId, params.initialModel, {
    replaceUrl: params.replaceUrl ?? false,
    context: params.context,
    contextId: params.contextId,
  });
  const chat = externalChat ?? internalChat;

  const {
    messages,
    streamingText,
    isConnected,
    isGenerating,
    sessionId,
    model,
    selectedModel,
    hasStarted,
    thinkingLevel,
    setSelectedModel,
    setThinkingLevel,
    sendPrompt,
    stopGeneration,
  } = chat;

  const availableModels = useUserVisibleModels();
  const attachmentManager = useAttachments({ sessionId });
  const slashCommands = useSlashCommands({ sessionId });

  // Detect when agent finishes a message (isGenerating: true → false)
  const wasGeneratingRef = useRef(false);
  useEffect(() => {
    if (isGenerating) {
      wasGeneratingRef.current = true;
    } else if (wasGeneratingRef.current) {
      wasGeneratingRef.current = false;
      onMessageComplete?.();
    }
  }, [isGenerating]);

  const [input, setInput] = useState(defaultInput);
  const [showJumpToBottom, setShowJumpToBottom] = useState(false);
  const [commandFeedback, setCommandFeedback] = useState<string | null>(null);
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);
  const scrollViewportRef = useRef<HTMLDivElement | null>(null);
  const bottomRef = useRef<HTMLDivElement | null>(null);
  const userScrolledRef = useRef(false);

  const handleSend = async () => {
    const text = input.trim();
    if (!text || isGenerating) return;

    // Handle slash commands
    if (text.startsWith('/')) {
      const result = await slashCommands.execute(text);
      if (result.handled) {
        setCommandFeedback(result.feedback);
        setInput('');
        if (textareaRef.current) textareaRef.current.style.height = 'auto';
        return;
      }
    }
    setCommandFeedback(null);

    const { prefix, ids, images } = attachmentManager.processAttachments();
    let prompt = promptPrefix ? `${promptPrefix}\n\n${text}` : text;
    if (prefix) prompt = `${prefix}${prompt}`;

    // Send the cwd on every message (not just the first): OpenCode rebuilds its working-directory
    // system prompt each turn, so it needs the cwd every time. It stays constant for a session.
    const displayText = promptPrefix ? text : undefined;
    sendPrompt(
      prompt,
      !sessionId && ids.length > 0 ? ids : undefined,
      images.length > 0 ? images : undefined,
      cwd,
      undefined,
      thinkingLevel,
      displayText,
    );

    attachmentManager.clearAttachments();
    setInput('');
    userScrolledRef.current = false;
    if (textareaRef.current) textareaRef.current.style.height = 'auto';
  };

  const handleKeyDown = (ev: KeyboardEvent<HTMLTextAreaElement>) => {
    if (ev.key === 'Enter' && !ev.shiftKey) {
      ev.preventDefault();
      handleSend();
    }
  };

  const jumpToBottom = () => {
    userScrolledRef.current = false;
    bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
  };

  const appendToInput = (text: string) => {
    setInput((prev) => prev + (prev.length > 0 ? ' ' : '') + text);
  };

  // Auto-resize textarea
  const resizeTextarea = () => {
    const textarea = textareaRef.current;
    if (!textarea) return;
    textarea.style.height = 'auto';
    textarea.style.height = Math.min(textarea.scrollHeight, 200) + 'px';
  };

  useEffect(resizeTextarea, [input]);

  // Re-measure when textarea width changes (e.g. panel animation)
  useEffect(() => {
    const textarea = textareaRef.current;
    if (!textarea) return;
    let rafId = 0;
    let prevWidth = textarea.clientWidth;
    const observer = new ResizeObserver(() => {
      const width = textarea.clientWidth;
      if (width === prevWidth) return;
      prevWidth = width;
      cancelAnimationFrame(rafId);
      rafId = requestAnimationFrame(resizeTextarea);
    });
    observer.observe(textarea);
    return () => {
      observer.disconnect();
      cancelAnimationFrame(rafId);
    };
  }, []);

  // Auto-scroll to bottom on new messages
  useEffect(() => {
    if (!userScrolledRef.current) {
      bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
    }
  }, [messages, streamingText]);

  // Detect user scrolling up
  useEffect(() => {
    const viewport = scrollViewportRef.current;
    if (!viewport) return;

    const handleScroll = () => {
      const { scrollTop, scrollHeight, clientHeight } = viewport;
      const atBottom = scrollHeight - scrollTop - clientHeight < 60;
      userScrolledRef.current = !atBottom;
      setShowJumpToBottom(!atBottom);
    };

    viewport.addEventListener('scroll', handleScroll);
    return () => viewport.removeEventListener('scroll', handleScroll);
  }, []);

  // Focus textarea on mount
  useEffect(() => {
    textareaRef.current?.focus();
  }, []);

  // Auto-send initial message
  const initialSentRef = useRef(false);
  useEffect(() => {
    if (initialMessage && isConnected && !initialSentRef.current) {
      initialSentRef.current = true;
      if (params.initialModel) setSelectedModel(params.initialModel);
      sendPrompt(
        initialMessage.text,
        initialMessage.attachmentIds,
        initialMessage.images,
        initialMessage.cwd,
        undefined,
      );
    }
  }, [initialMessage, isConnected]);

  // Auto-send when autoSend is enabled
  const autoSentRef = useRef(false);
  useEffect(() => {
    if (autoSend && isConnected && !messages.length && input.trim() && !autoSentRef.current) {
      autoSentRef.current = true;
      handleSend();
    }
  }, [autoSend, isConnected, messages.length, input]);

  return {
    messages,
    streamingText,
    isConnected,
    isGenerating,
    sessionId,
    model,
    selectedModel,
    hasStarted,
    thinkingLevel,
    setSelectedModel,
    setThinkingLevel,
    sendPrompt,
    stopGeneration,
    input,
    setInput,
    handleSend,
    handleKeyDown,
    appendToInput,
    attachments: attachmentManager.attachments,
    attachWebpage: attachmentManager.attachWebpage,
    attachImage: attachmentManager.attachImage,
    removeAttachment: attachmentManager.removeAttachment,
    availableModels,
    showJumpToBottom,
    jumpToBottom,
    commandFeedback,
    textareaRef,
    scrollViewportRef,
    bottomRef,
  };
}

export type UseEmbeddableChatType = ReturnType<typeof useEmbeddableChat>;
