import type { KeyboardEvent } from 'react';
import { useRef, useEffect, useState } from 'react';
import { toast } from 'sonner';
import { useClient } from 'hooks/useClient';
import type { ModelOption } from '@/state/useModels';
import type { useClaude } from './useClaude';
import { MessageList } from 'widgets/Chat';
import { InputArea } from './InputArea';

export type Attachment =
  | { type: 'webpage'; url: string; title: string; content: string; attachmentId: string; loading?: boolean }
  | { type: 'image'; filename: string; dataUrl: string; attachmentId: string; loading?: boolean };

type EmbeddableChatProps = {
  chat: ReturnType<typeof useClaude>;
  provider?: 'claude' | 'opencode';
  availableModels?: ModelOption[];
  onProviderChange?: (provider: 'claude' | 'opencode') => void;
  onBeforeSend?: (text: string) => boolean | Promise<boolean>;
  commandFeedback?: string | null;
  defaultInput?: string;
  className?: string;
  cwd?: { root?: string; path: string };
  autoSend?: boolean;
};

export const EmbeddableChat = ({
  chat,
  provider = 'claude',
  availableModels = [],
  onProviderChange,
  onBeforeSend,
  commandFeedback = null,
  defaultInput = '',
  className,
  cwd,
  autoSend = false,
}: EmbeddableChatProps) => {
  const {
    messages,
    streamingText,
    isConnected,
    isGenerating,
    sessionId,
    model,
    selectedModel,
    setSelectedModel,
    sendPrompt,
    stopGeneration,
  } = chat;

  const client = useClient();
  const [input, setInput] = useState(defaultInput);
  const [attachments, setAttachments] = useState<Attachment[]>([]);
  const [showJumpToBottom, setShowJumpToBottom] = useState(false);
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);
  const scrollViewportRef = useRef<HTMLDivElement | null>(null);
  const bottomRef = useRef<HTMLDivElement | null>(null);
  const userScrolledRef = useRef(false);

  const handleAttachWebpage = async (url: string) => {
    const idx = attachments.length;
    setAttachments((prev) => [
      ...prev,
      { type: 'webpage', url, title: '', content: '', attachmentId: '', loading: true },
    ]);

    try {
      const res = await client.post<{ url: string; title: string; content: string; attachmentId: string }>('/scrape', {
        url,
        sessionId: sessionId ?? undefined,
        provider,
      });
      setAttachments((prev) =>
        prev.map((a, i) =>
          i === idx
            ? { ...a, title: res.title, content: res.content, attachmentId: res.attachmentId, loading: false }
            : a,
        ),
      );
    } catch {
      setAttachments((prev) => prev.filter((_, i) => i !== idx));
      toast.error('Failed to scrape webpage');
    }
  };

  const handleAttachImage = async (file: File) => {
    const idx = attachments.length;
    setAttachments((prev) => [
      ...prev,
      { type: 'image', filename: file.name, dataUrl: '', attachmentId: '', loading: true },
    ]);

    try {
      const formData = new FormData();
      formData.append('file', file);
      if (sessionId) formData.append('sessionId', sessionId);
      formData.append('provider', provider);

      const res = await client.post<{ filename: string; dataUrl: string; attachmentId: string }>('/upload', formData);
      setAttachments((prev) =>
        prev.map((a, i) =>
          i === idx ? { ...a, dataUrl: res.dataUrl, attachmentId: res.attachmentId, loading: false } : a,
        ),
      );
    } catch {
      setAttachments((prev) => prev.filter((_, i) => i !== idx));
      toast.error('Failed to upload image');
    }
  };

  const handleRemoveAttachment = (index: number) => {
    setAttachments((prev) => prev.filter((_, i) => i !== index));
  };

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

    if (onBeforeSend) {
      const handled = await onBeforeSend(text);
      if (handled) {
        setInput('');
        if (textareaRef.current) textareaRef.current.style.height = 'auto';
        return;
      }
    }

    // Prepend attachment content to the prompt
    let prompt = text;
    const ids: string[] = [];
    const images: { filename: string; dataUrl: string }[] = [];
    for (const a of attachments) {
      if (a.loading) continue;
      if (a.type === 'webpage' && a.content) {
        prompt = `[Attached webpage: ${a.url}]\n${a.content}\n\n${prompt}`;
      } else if (a.type === 'image' && a.dataUrl) {
        prompt = `[Attached image: ${a.filename}]\n\n${prompt}`;
        images.push({ filename: a.filename, dataUrl: a.dataUrl });
      }
      ids.push(a.attachmentId);
    }

    // On first message (no sessionId), include attachmentIds so server can relocate tmp files
    const cwdForFirst = !sessionId ? cwd : undefined;
    sendPrompt(
      prompt,
      !sessionId && ids.length > 0 ? ids : undefined,
      images.length > 0 ? images : undefined,
      cwdForFirst,
    );
    setAttachments([]);
    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();
    }
  };

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

  // 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);
  }, []);

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

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

  // Auto-send first message 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 (
    <div className={`flex flex-col ${className ?? ''}`}>
      <MessageList
        messages={messages}
        streamingText={streamingText}
        isGenerating={isGenerating}
        showJumpToBottom={showJumpToBottom}
        onJumpToBottom={jumpToBottom}
        onQuestionAnswer={(text) => sendPrompt(text)}
        scrollViewportRef={scrollViewportRef}
        bottomRef={bottomRef}
      />

      <InputArea
        input={input}
        onInputChange={setInput}
        onKeyDown={handleKeyDown}
        onSend={handleSend}
        onStop={stopGeneration}
        isGenerating={isGenerating}
        isConnected={isConnected}
        commandFeedback={commandFeedback}
        textareaRef={textareaRef}
        provider={provider}
        messages={messages}
        onProviderChange={onProviderChange}
        availableModels={availableModels}
        selectedModel={selectedModel}
        onModelChange={setSelectedModel}
        model={model}
        attachments={attachments}
        onAttachWebpage={handleAttachWebpage}
        onAttachImage={handleAttachImage}
        onRemoveAttachment={handleRemoveAttachment}
      />
    </div>
  );
};
