import { Sidebar } from "./sidebar";
import { useAppState } from "../contexts/state";
import styles from '../assets/chat-sidebar.module.css';
import { useState, useRef, useEffect } from "preact/hooks";
import LLM from "../utils/llm";
import { highlight } from "../utils/highlight";
import Prompt from "../utils/prompt";
import clsx from "clsx";

export const ChatSidebar = () => {
    const appState = useAppState();
    const { currentStory, dispatch, connection, model } = appState;
    const [input, setInput] = useState('');
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const messagesRef = useRef<HTMLDivElement>(null);
    const abortControllerRef = useRef<AbortController | null>(null);

    useEffect(() => {
        if (messagesRef.current) {
            messagesRef.current.scrollTo({
                top: messagesRef.current.scrollHeight,
                behavior: 'smooth',
            });
        }
    }, [currentStory?.chatMessages.length]);

    useEffect(() => {
        return () => {
            abortControllerRef.current?.abort();
        };
    }, []);

    const sendMessage = async () => {
        if (!currentStory || !input.trim() || !connection || !model || isLoading) return;

        const userMessage = {
            id: crypto.randomUUID(),
            role: 'user' as const,
            content: input.trim(),
        };

        dispatch({
            type: 'ADD_CHAT_MESSAGE',
            storyId: currentStory.id,
            message: userMessage,
        });

        const assistantMessageId = crypto.randomUUID();
        dispatch({
            type: 'ADD_CHAT_MESSAGE',
            storyId: currentStory.id,
            message: {
                id: assistantMessageId,
                role: 'assistant',
                content: 'Generating...',
            },
        });

        setInput('');
        setIsLoading(true);
        setError(null);

        const request = Prompt.compilePrompt(appState, userMessage);

        if (!request) {
            setError('Failed to compile prompt');
            setIsLoading(false);
            return;
        }

        try {
            let accumulatedContent = '';

            for await (const chunk of LLM.generateStream(connection, request)) {
                const delta = chunk.choices[0]?.delta?.content;
                if (delta) {
                    accumulatedContent += delta;
                    dispatch({
                        type: 'ADD_CHAT_MESSAGE',
                        storyId: currentStory.id,
                        message: {
                            id: assistantMessageId,
                            role: 'assistant',
                            content: accumulatedContent,
                        },
                    });
                }
                if (abortControllerRef.current?.signal.aborted) {
                    break;
                }
            }
        } catch (err) {
            const errorMessage = err instanceof Error
                ? err.message
                : 'Failed to generate response';

            setError(errorMessage);
        } finally {
            setIsLoading(false);
            abortControllerRef.current = null;
        }
    };

    const handleKeyDown = (e: KeyboardEvent) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            sendMessage();
        }
    };

    const handleClear = () => {
        if (!currentStory) return;
        dispatch({
            type: 'CLEAR_CHAT',
            storyId: currentStory.id,
        });
    };

    const isDisabled = !currentStory || !connection || !model || isLoading;

    return (
        <Sidebar side="right">
            <div class={styles.chat}>
                {!currentStory ? (
                    <div class={styles.placeholder}>
                        Select a story to start chatting
                    </div>
                ) : !connection || !model ? (
                    <div class={styles.placeholder}>
                        {!connection ? 'Connect to an LLM server' : 'Select a model'} to start chatting
                    </div>
                ) : currentStory.chatMessages.length === 0 ? (
                    <div class={styles.placeholder}>
                        No messages yet
                    </div>
                ) : (
                    <div class={styles.messages} ref={messagesRef}>
                        {currentStory.chatMessages.map((message) => (
                            <div key={message.id} class={styles.message} data-role={message.role}>
                                <div class={styles.role}>{message.role}</div>
                                <div
                                    class={styles.content}
                                    dangerouslySetInnerHTML={{ __html: highlight(message.content) }}
                                />
                            </div>
                        ))}
                        {error && (
                            <div class={clsx(styles.message, styles.errorMessage)} data-role="assistant">
                                <div class={styles.role}>error</div>
                                <div class={styles.errorText}>{error}</div>
                            </div>
                        )}
                        <button class={styles.clearButton} onClick={handleClear}>
                            Clear chat
                        </button>
                    </div>
                )}
                {currentStory && (
                    <div class={styles.inputContainer}>
                        <textarea
                            class={styles.input}
                            value={input}
                            onInput={(e) => setInput((e.target as HTMLTextAreaElement).value)}
                            onKeyDown={handleKeyDown}
                            placeholder={isDisabled ? 'Connect to an LLM server to chat' : 'Type a message...'}
                            rows={3}
                            disabled={isDisabled}
                        />
                        <button
                            class={styles.sendButton}
                            onClick={sendMessage}
                            disabled={isDisabled || !input.trim()}
                        >
                            {isLoading ? 'Sending...' : 'Send'}
                        </button>
                    </div>
                )}
            </div>
        </Sidebar>
    );
};
