import { createContext } from "preact";
import { useEffect, useMemo, useState } from "preact/hooks";
import { MessageTools, type IMessage } from "../messages";

interface IContext {
    connectionUrl: string;
    input: string;
    name: string;
    messages: IMessage[];
    triggerNext: boolean;
}

interface IActions {
    setConnectionUrl: (url: string) => void;
    setInput: (url: string) => void;
    setName: (name: string) => void;
    setTriggerNext: (triggerNext: boolean) => void;

    setMessages: (messages: IMessage[]) => void;
    addMessage: (content: string, role: IMessage['role'], triggerNext?: boolean) => void;
    editMessage: (index: number, content: string, triggerNext?: boolean) => void;
    deleteMessage: (index: number) => void;
    setCurrentSwipe: (index: number, swipe: number) => void;
    addSwipe: (index: number, content: string) => void;

    continueMessage: () => void;
}

const SAVE_KEY = 'ai_game_save_state';

export const saveContext = (context: IContext) => {
    localStorage.setItem(SAVE_KEY, JSON.stringify({
        ...context,
        triggerNext: false,
    }));
}

export const loadContext = (): IContext => {
    const defaultContext: IContext = {
        connectionUrl: 'http://192.168.10.102:5001',
        input: '',
        name: 'Maya',
        messages: [],
        triggerNext: false,
    };

    let loadedContext: Partial<IContext> = {};

    try {
        const json = localStorage.getItem(SAVE_KEY);
        if (json) {
            loadedContext = JSON.parse(json);
        }
    } catch { }

    return { ...defaultContext, ...loadedContext };
}

export type IStateContext = IContext & IActions;

export const StateContext = createContext<IStateContext>({} as IStateContext);

export const StateContextProvider = ({ children }: { children?: any }) => {
    const loadedContext = useMemo(() => loadContext(), []);
    const [connectionUrl, setConnectionUrl] = useState(loadedContext.connectionUrl);
    const [input, setInput] = useState(loadedContext.input);
    const [name, setName] = useState(loadedContext.name);
    const [messages, setMessages] = useState(loadedContext.messages);
    const [triggerNext, setTriggerNext] = useState(false);

    const actions: IActions = useMemo(() => ({
        setConnectionUrl,
        setInput,
        setName,
        setTriggerNext,

        setMessages: (newMessages) => setMessages(newMessages.slice()),
        addMessage: (content, role, triggerNext = false) => {
            setMessages(messages => [
                ...messages,
                MessageTools.create(content, role),
            ]);
            setTriggerNext(triggerNext);
        },
        editMessage: (index, content, triggerNext = false) => {
            setMessages(messages => MessageTools.updateContent(messages, index, content));
            setTriggerNext(triggerNext);
        },
        deleteMessage: (index) => setMessages(messages =>
            messages.filter((_, i) => i !== index)
        ),
        setCurrentSwipe: (index, currentSwipe) => {
            let shouldTrigger = false;

            setMessages(messages =>
                messages.map(
                    (message, i) => {
                        if (i === index) {
                            const swipes = message.swipes.slice();
                            const latestSwipe = swipes.at(-1);
                            if (currentSwipe >= swipes.length) {
                                if (latestSwipe.content.length > 0) {
                                    currentSwipe = swipes.length;
                                    swipes.push({ content: '' });
                                } else {
                                    currentSwipe = swipes.length - 1;
                                }
                                shouldTrigger = true;
                            } else while (currentSwipe < 0) {
                                currentSwipe += swipes.length;
                            }
                            return {
                                ...message, swipes, currentSwipe
                            };
                        } else {
                            return message;
                        }
                    }
                )
            );

            setTriggerNext(shouldTrigger);
        },
        addSwipe: (index, content) => setMessages(messages =>
            messages.map(
                (message, i) => {
                    if (i === index) {
                        const swipes = [...message.swipes, { content }];

                        return {
                            ...message,
                            swipes,
                            currentSwipe: swipes.length - 1,
                        };
                    } else {
                        return message;
                    }
                }
            )
        ),
        continueMessage: () => setTriggerNext(true),
    }), []);

    const rawContext: IContext = {
        connectionUrl,
        input,
        name,
        messages,
        triggerNext,
    };

    const context = useMemo(() => rawContext, Object.values(rawContext));

    useEffect(() => {
        saveContext(context);
    }, [context]);

    const value = useMemo(() => ({ ...context, ...actions }), [context, actions])

    return (
        <StateContext.Provider value={value}>
            {children}
        </StateContext.Provider>
    );
}