import { useCallback, useEffect, useMemo, useState } from 'preact/hooks';
import styles from './header.module.css';
import { Connection, HORDE_ANON_KEY, isHordeConnection, isKoboldConnection, type IConnection, type IHordeModel } from '../../tools/connection';
import { Instruct } from '../../contexts/state';
import { useInputState } from '@common/hooks/useInputState';
import { useInputCallback } from '@common/hooks/useInputCallback';
import { Huggingface } from '../../tools/huggingface';

interface IProps {
    connection: IConnection;
    setConnection: (c: IConnection) => void;
}

export const ConnectionEditor = ({ connection, setConnection }: IProps) => {
    // kobold
    const [connectionUrl, setConnectionUrl] = useInputState('');
    // horde
    const [apiKey, setApiKey] = useInputState(HORDE_ANON_KEY);
    const [modelName, setModelName] = useInputState('');

    const [instruct, setInstruct] = useInputState('');
    const [modelTemplate, setModelTemplate] = useInputState('');
    const [hordeModels, setHordeModels] = useState<IHordeModel[]>([]);
    const [contextLength, setContextLength] = useState<number>(0);

    const backendType = useMemo(() => {
        if (isKoboldConnection(connection)) return 'kobold';
        if (isHordeConnection(connection)) return 'horde';
        return 'unknown';
    }, [connection]);

    const isOnline = useMemo(() => contextLength > 0, [contextLength]);

    useEffect(() => {
        setInstruct(connection.instruct);

        if (isKoboldConnection(connection)) {
            setConnectionUrl(connection.url);
            Connection.getContextLength(connection).then(setContextLength);
        } else if (isHordeConnection(connection)) {
            setModelName(connection.model);
            setApiKey(connection.apiKey || HORDE_ANON_KEY);

            Connection.getHordeModels()
                .then(m => setHordeModels(Array.from(m.values()).sort((a, b) => a.name.localeCompare(b.name))));
        }
    }, [connection]);

    useEffect(() => {
        if (modelName) {
            Huggingface.findModelTemplate(modelName)
                .then(template => {
                    if (template) {
                        setModelTemplate(template);
                        setInstruct(template);
                    }
                });
        }
    }, [modelName]);

    const setBackendType = useInputCallback((type) => {
        if (type === 'kobold') {
            setConnection({
                instruct,
                url: connectionUrl,
            });
        } else if (type === 'horde') {
            setConnection({
                instruct,
                apiKey,
                model: modelName,
            });
        }
    }, [setConnection, connectionUrl, apiKey, modelName, instruct]);

    const handleSetInstruct = useInputCallback((instruct: string) => {
        setConnection({...connection, instruct});
    }, [setConnection, connection]);

    const handleBlurUrl = useCallback(() => {
        const regex = /^(?:http(s?):\/\/)?(.*?)\/?$/i;
        const url = connectionUrl.replace(regex, 'http$1://$2');

        setConnection({
            instruct,
            url,
        });
    }, [connectionUrl, instruct, setConnection]);

    const handleBlurHorde = useCallback(() => {
        setConnection({
            instruct,
            apiKey,
            model: modelName,
        });
    }, [apiKey, modelName, instruct, setConnection]);

    return (
        <div class={styles.connectionEditor}>
            <select value={backendType} onChange={setBackendType}>
                <option value='kobold'>Kobold CPP</option>
                <option value='horde'>Horde</option>
            </select>
            <select value={instruct} onChange={handleSetInstruct} title='Instruct template'>
                {modelName && modelTemplate && <optgroup label='Native model template'>
                    <option value={modelTemplate} title='Native for model'>{modelName}</option>
                </optgroup>}
                <optgroup label='Manual templates'>
                    {Object.entries(Instruct).map(([label, value]) => (
                        <option value={value} key={value}>
                            {label.toLowerCase()}
                        </option>
                    ))}
                </optgroup>
                {instruct !== modelTemplate && <optgroup label='Custom'>
                    <option value={connection.instruct}>Custom</option>
                </optgroup>}
            </select>
            {isKoboldConnection(connection) && <input
                value={connectionUrl}
                onInput={setConnectionUrl}
                onBlur={handleBlurUrl}
                class={isOnline ? styles.valid : styles.invalid}
            />}
            {isHordeConnection(connection) && <>
                <input
                    placeholder='Horde API key'
                    title='Horde API key'
                    value={apiKey}
                    onInput={setApiKey}
                    onBlur={handleBlurHorde}
                />

                <select
                    value={modelName}
                    onChange={setModelName}
                    onBlur={handleBlurHorde}
                    title='Horde model'
                >
                    {hordeModels.map((m) => (
                        <option value={m.name} key={m.name}>
                            {m.name} ({m.maxLength}/{m.maxContext})
                        </option>
                    ))}
                </select>
            </>}
        </div>
    );
};
