import { MessageTools, type IMessage } from "../../tools/messages"
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import { Modal } from "@common/components/modal/Modal";
import { DOMTools } from "../../tools/dom";

import styles from './minichat.module.css';
import { LLMContext } from "../../contexts/llm";
import { FormattedMessage } from "../message/formattedMessage";
import { AutoTextarea } from "../autoTextarea";
import { useBool } from "@common/hooks/useBool";

interface IProps {
    open: boolean;
    onClose: () => void;
    history?: IMessage[];
    buttons?: Record<string, (answer: string) => void>;
}

export const MiniChat = ({ history = [], buttons = {}, open, onClose }: IProps) => {
    const { stopGeneration, generate, compilePrompt } = useContext(LLMContext);
    const [messages, setMessages] = useState<IMessage[]>([]);
    const ref = useRef<HTMLDivElement>(null);
    const generating = useBool();

    const answer = useMemo(() =>
        MessageTools.getSwipe(messages.filter(m => m.role === 'assistant').at(-1))?.content,
        [messages]
    );

    const handleInit = useCallback((force = false) => {
        if (force || confirm('Clear chat?')) {
            setMessages([MessageTools.create('', 'user', true)]);
        }
    }, []);

    useEffect(() => {
        setTimeout(() => DOMTools.scrollDown(ref.current, false), 100);
    }, [generating.value, open]);

    useEffect(() => {
        DOMTools.scrollDown(ref.current, false);
    }, [MessageTools.getSwipe(messages.at(-1))?.content]);

    useEffect(() => {
        if (messages.length === 0) {
            handleInit(true);
        }
        DOMTools.scrollDown(ref.current);
    }, [messages.length, handleInit]);

    const handleGenerate = useCallback(async () => {
        if (messages.length > 0 && !generating.value) {
            const promptMessages: IMessage[] = [...history, ...messages];
            const { prompt } = await compilePrompt(promptMessages, { keepUsers: messages.length + 1, continueLast: true });

            let text = '';
            const messageId = messages.length;
            const newMessages = [...messages, MessageTools.create('', 'assistant', true)];
            setMessages(newMessages);

            generating.setTrue();
            for await (const chunk of generate(prompt)) {
                text += chunk.text;
                setMessages(MessageTools.updateSwipe(
                    newMessages,
                    messageId,
                    {
                        content: text.trim(),
                        cost: chunk.cost,
                    }));
            }
            generating.setFalse();

            setMessages([
                ...MessageTools.updateSwipe(newMessages, messageId, { content: MessageTools.trimSentence(text) }),
                MessageTools.create('', 'user', true),
            ]);
            MessageTools.playReady();
        }
    }, [messages, history, generating]);

    const handleKeyDown = useCallback((e: KeyboardEvent) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            handleGenerate();
        }
    }, [handleGenerate]);

    const handleChange = useCallback((i: number, e: InputEvent) => {
        if (e.target instanceof HTMLTextAreaElement) {
            setMessages(MessageTools.updateSwipe(messages, i, { content: e.target.value }));
        }
    }, [messages]);

    if (!open) {
        return null;
    }
    return (
        <Modal open onClose={onClose}>
            <div class={styles.minichat} ref={ref}>
                <div class={styles.messages}>
                    {messages.map((m, i) => (
                        generating.value
                            ? <FormattedMessage key={i} class={`${styles[m.role]} ${styles.message}`}>
                                {MessageTools.getSwipe(m)?.content ?? ''}
                            </FormattedMessage>
                            : <AutoTextarea
                                key={i}
                                class={styles[m.role]}
                                value={MessageTools.getSwipe(m)?.content ?? ''}
                                onInput={(e) => handleChange(i, e)}
                                onKeyDown={handleKeyDown}
                            />
                    ))}
                </div>
            </div>
            <div class={styles.buttons}>
                {generating.value
                    ? <button onClick={stopGeneration}>Stop</button>
                    : <button onClick={handleGenerate}>Generate</button>
                }
                <button onClick={() => handleInit()} class={`${generating.value ? 'disabled' : ''}`}>
                    Clear
                </button>
                {Object.entries(buttons).map(([label, onClick], i) => (
                    <button
                        key={i}
                        onClick={() => onClick(answer ?? '')}
                        class={`${(generating.value || !answer) ? 'disabled' : ''}`}
                    >
                        {label}
                    </button>
                ))}
            </div>
        </Modal>
    )
}