document.addEventListener( "DOMContentLoaded", function () {

    // Функция перевода
    function __( str ) {
        return _( str, "ai.mod.php" );
    }


    const ai_page = document.querySelector( `#ai` );
    const header_el = ai_page.querySelector( `:scope > .header` );
    const chat = ai_page.querySelector( `.chat` );
    const prompt_el = ai_page.querySelector( `.side [name="prompt"]` );
    const files_list_el = ai_page.querySelector( `.side [name="files_list"]` );
    const plan_el   = ai_page.querySelector( `.side [name="plan"]` );
    let row_gap = 0;
    const user_panel = ai_page.querySelector( `:scope > .user` );
    const textarea = user_panel.querySelector( `textarea` );

    // Берем чат из localStorage в виде json-строки
    const chat_str = localStorage.getItem( "ai_chat" );
    let chat_arr;
    if ( chat_str ) {
        chat_arr = JSON.parse( chat_str );
    } else {
        chat_arr = [];
        localStorage.setItem( "ai_chat", JSON.stringify( chat_arr ) );
    }

    const chat_html = [];
    chat_arr.forEach( function( msg ) {
        chat_html.push( wrap_msg( msg ) );
    } );
    chat.innerHTML = chat_html.join( "" );

    // Удалить сообщение из чата
    chat.querySelectorAll( `.controls .trash` ).forEach( function ( trash ) {
        const msg_el = trash.closest( `.msg` );
        const id_msg = Number( msg_el.getAttribute( "data-id" ) );
        trash.addEventListener( "click", function() {
            msg_el.remove();
            // Обновляем массив и localStorage
            chat_arr = chat_arr.filter( msg => msg["id"] !== id_msg );
            localStorage.setItem( "ai_chat", JSON.stringify( chat_arr ) );
        } );
    } );

    function wrap_msg( msg ) {
        const controls = `<div class=controls><div class=role>${msg["role"]}</div><div class="trash icon button"><span></span></div></div>`;
        const html = `
<div class='msg ${msg["role"]}' data-id=${msg["id"]}>
    ${controls}
    <div class=content>${escape_html( msg["content"] )}</div>
    ${controls}
</div>
`;
        return html;
    }

    function escape_html( str ) {
        return str.replace( /&/g, "&amp;" )
            .replace( /</g, "&lt;" )
            .replace( />/g, "&gt;" )
            .replace( /"/g, "&quot;" )
            .replace( /'/g, "&#39;" );
    }

    document.body.addEventListener( "ready", function( event ) { // Дать отработать рендерингу
        chat.style.scrollBehavior = "auto"; // Для быстрой промотки. Откл. анимацию.
        chat.scrollTop = chat.scrollHeight;
        chat.style.scrollBehavior = '';
        textarea.focus();
    } );

    // Получаем вычисленное значение row-gap (пиксели)
    row_gap = parseFloat( window.getComputedStyle( chat ).row_gap );

    // Настройки доступа к ИИ
    const endpoint_el = ai_page.querySelector( `input[name=endpoint]` );
    const model_el    = ai_page.querySelector( `input[name=model]` );
    const key_el      = ai_page.querySelector( `input[name=password]` );

    let endpoint = localStorage.getItem( "ai_endpoint" );
    if ( endpoint === null ) {
        endpoint = "http://127.0.0.1:1234/v1/chat/completions";
        localStorage.setItem( "ai_endpoint", endpoint );
    }
    endpoint_el.value = endpoint;

    let model = localStorage.getItem( "ai_model" );
    if ( model === null ) {
        model = "google/gemma-4-26b-a4b";
        localStorage.setItem( "ai_model", model );
    }
    model_el.value = model;

    let api_key = localStorage.getItem( "ai_api_key" );
    key_el.value = api_key;

    header_el.querySelector( `.button.save` ).addEventListener( "click", function () {
        endpoint = endpoint_el.value.trim();
        localStorage.setItem( "ai_endpoint", endpoint );
        model = model_el.value.trim();
        localStorage.setItem( "ai_model", model );
        api_key = key_el.value.trim();
        localStorage.setItem( "ai_api_key", api_key );
        notify_answer( { "notify": {
            "notify_html": __( "saved" ),
        } } );
    } );

    /*
    user_panel.querySelector( `.button.copy.demo` ).addEventListener( "click", function () {
        const data = { fn: "ai_get_cms_files", mod: "demo_for_ai.mod.php" };
        api( data, function( answer ) {
            if ( ! answer.result ) {
                return;
            }

            const r = copy_to_clipboard( answer.result.content );
            if ( r ) {
                const msg = __( "copyed" ) + ": " + data.mod + "<br>"
                 + __( "files" ) + `: ${answer.result.files_count}<br>`
                 + __( "lines" ) + `: ${answer.result.lines_count}<br>`
                 + __( "size_in_chars" ) + `: ${answer.result.size_in_chars}`;
                notify_answer( { "result": {
                    "notify_html": msg,
                } } );
            } else {
                notify_answer( { "error": {
                    "notify_html": __( "copy_error" ),
                } } );
            }
            textarea.focus();
        } );
    } );
    */

    user_panel.querySelector( `.settings.button` ).addEventListener( "click", function () {
        ai_page.classList.toggle( "settings" );
    } );
    
    user_panel.querySelector( `.clear.button` ).addEventListener( "click", function () {
        chat_arr = [];
        localStorage.setItem( "ai_chat", JSON.stringify( chat_arr ) );
        chat.innerHTML = "";
        textarea.focus();
    } );

    textarea.addEventListener( "keydown", function ( event ) {
        if ( event.code === "Enter" && event.ctrlKey ) {
            user_panel.querySelector( `.send.button` ).click();
        }
    } );

    let ajax = null;
    let requestStarted = false;

    function ai_api({ url, data, api_key, onSuccess, onError, onAbort, onProgress }) {
        const xhr = new XMLHttpRequest();
        xhr.open( "POST", url, true );
        xhr.setRequestHeader( "Content-Type", "application/json" );
        if ( api_key ) {
            xhr.setRequestHeader( "Authorization", `Bearer ${api_key}` );
        }
        if ( onProgress ) { xhr.upload.addEventListener( "progress", onProgress ); }
        xhr.onload = function () {
            let response;
            if ( xhr.status < 200 || xhr.status >= 300 ) {
                onError && onError( new Error( "Network error: " + xhr.status ) );
                return;
            }
            try {
                response = JSON.parse( xhr.responseText );
            } catch ( e ) {
                onError && onError( e );
                return;
            }
            onSuccess && onSuccess( response );
        };
        xhr.onerror = function () {
            onError && onError( new Error( "Network error" ) );
        };
        xhr.onabort = function () {
            onAbort && onAbort();
        };
        xhr.send( JSON.stringify( data ) );
        return xhr;
    }
    user_panel.querySelector( `.send.button` ).addEventListener( "click", function () {
        textarea.focus(); // Потому что чуть ниже выход

        const content = textarea.value;
        if ( content.trim() === "" ) {
            return;
        }

        if ( ! endpoint || ! model ) {
            notify_answer( { "notify": {
                "notify_html": __( "ai_conf_error" ),
            } } );
            return;
        }
        
        // Отмотан ли чат в конец
        const atBottom = ( chat.scrollTop + chat.clientHeight ) >= chat.scrollHeight;

        const msg = {
            "id": new Date().getTime(),
            "role": "user",
            "content": content,
        };
        chat_arr.push( msg );

        localStorage.setItem( "ai_chat", JSON.stringify( chat_arr ) );
        chat.insertAdjacentHTML( "beforeend", wrap_msg( msg ) );
        textarea.value = "";
        
        // Если был отмотан в конец, то докручиваем
        if ( atBottom ) {
            chat.scrollTop = chat.scrollHeight;
        }

        // Системное сообщение - это Промпт 1 + Содержимое файлов + Промпт 2.
        // За определением функции будет добавка содержимого файлов если они есть.
        // А Промпт 2 добавится уже внутри самой функции отправки.
        let system_content = prompt_el.value;

        // Определим функцию отправки в ИИ чтобы использовать ее в двух местах
        function sendRequest() {
            system_content += "\n\n" + plan_el.value;

            const system_msg = {
                "id": 0,
                "role": "system",
                "content": system_content,
            };

            const request = {
                "model": model,
                "messages": [ system_msg ].concat( chat_arr ),
            }

            user_panel.classList.add( "think" );

            ajax = ai_api( {
                url: endpoint,
                data: request,
                api_key: api_key,
                onSuccess: function ( data ) {
                    requestStarted = false;
                    user_panel.classList.remove( "think" );

                    // Сохраняем ответ
                    if ( data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content ) {
                        const content = data.choices[0].message.content;
                        const msg = {
                            "id": new Date().getTime(),
                            "role": "assistant",
                            "content": content,
                        };

                        chat_arr.push( msg );
                        localStorage.setItem( "ai_chat", JSON.stringify( chat_arr ) );

                        // Отмотан ли чат в конец
                        const atBottom = ( chat.scrollTop + chat.clientHeight ) >= chat.scrollHeight;
                        const oldScrollHeight = chat.scrollHeight;

                        chat.insertAdjacentHTML( "beforeend", wrap_msg( msg ) );

                        // Если был отмотан в конец, то докручиваем
                        if ( atBottom ) {
                            const diffScrollHeight = chat.scrollHeight - oldScrollHeight;
                            const diffScrollTop = Math.min( chat.clientHeight, diffScrollHeight + row_gap );
                            chat.scrollTop += diffScrollTop;
                        }
                    }
                },
                onError: function ( err ) {
                    requestStarted = false;
                    notify_answer( { "error": {
                        "notify_html": err.message,
                    } } );
                    user_panel.classList.remove( "think" );
                },
                onAbort: function () {
                    console.log( "Запрос отменён" );
                    user_panel.classList.remove( "think" );
                }
            } );

            // TODO: Возможно стоит переделать логику.
            // requestStarted устанавливается странным образом - спустя секунду после запроса
            // но теоретически он во-первых может и не стартануть,
            // а во-вторых и в catch нет записи о том, что произошла ошибка.
            // Возможно она произошла сразу и нужно тоже ее тут проверять.
            // Иначе запрос будет считаться запущенным, хотя он сорвался.
            setTimeout( () => {
                if ( ajax && ajax.readyState !== 4 ) {
                    requestStarted = true;
                }
            }, 1000 );
        }

        // Получаем содержимое файлов если список не пуст и отправляем запрос.
        if ( files_list_el.value.trim() !== "" ) {
            const data = { fn: "ai_get_files_content", files: files_list_el.value };
            api( data, function( answer ) {
                if ( answer.result && answer.result.content !== "" ) {
                    system_content += "\n\n" + answer.result.content;
                }
                sendRequest();
            } );
        } else {
            sendRequest();
        }
    } );

    user_panel.querySelector( `.stop.button` ).addEventListener( "click", function () {
        if ( requestStarted && ajax ) {
            ajax.abort();
        }
    } );

    //#region rpompt и plan
    // Закрытие выпдающего списка
    document.body.addEventListener( "click", function( event ) {
        ai_page.querySelectorAll( `.select.widget` ).forEach( function( select ) {
            select.classList.remove( "open" );
        } );
    } );

    const select = ai_page.querySelector( `.select.widget` );
    const selected = select.querySelector( `.selected` );
    const value = selected.querySelector( `.value` );
    const input = select.querySelector( `input` );
    const options = select.querySelector( `.options .scrollable` );
    
    // Промпты
    let last_preset = localStorage.getItem( "ai_last_preset" );
    let last_preset_option_el;
    const presets_str = localStorage.getItem( "ai_presets" );
    let presets;
    if ( presets_str ) {
        presets = JSON.parse( presets_str );
    } else {
        presets = {};
        localStorage.setItem( "ai_presets", JSON.stringify( presets ) );
    }
    
    for ( let preset in presets ) {
        options.insertAdjacentHTML( "beforeend", `
            <div class=option>
                <span class=preset>${preset}</span>
                <span class="trash icon button"><span></span></span>
            </div>
        ` );
        if ( preset === last_preset ) {
            last_preset_option_el = options.lastElementChild;
        }
    }

    // Открыть/закрыть список
    selected.addEventListener( "click", function( event ) {
        event.stopPropagation(); // Иначе сразу закрывается
        select.classList.toggle( "open" );
        input.focus();
    } );

    // Предотвратить закрытие при кликах в инпуте
    input.addEventListener( "click", function( event ) {
        event.stopPropagation();
    } );

    // Новый пресет (забирает к себе промпт и план)
    input.addEventListener( "keydown", function ( event ) {
        if ( event.code === "Enter" ) {
            const preset = input.value;
            input.value = "";
            value.innerText = preset;
            presets[preset] = {
                prompt: prompt_el.value,
                plan: plan_el.value,
            };
            options.insertAdjacentHTML( "beforeend", `
                <div class=option>
                    <span class=preset>${preset}</span>
                    <span class="trash icon button"><span></span></span>
                </div>
            ` );
            options.lastElementChild.addEventListener( "click", option_click );
            localStorage.setItem( "ai_presets", JSON.stringify( presets ) );
            localStorage.setItem( "ai_last_preset", preset );
            select.classList.remove( "open" );
        }
    } );

    // Сохранение промпта
    prompt_el.addEventListener( "input", function ( event ) {
        const preset = value.innerText;
        // Если название пресета не установлено, то ничего и не сохраняется
        if ( preset ) {
            presets[preset]["prompt"] = prompt_el.value;
            localStorage.setItem( "ai_presets", JSON.stringify( presets ) );
        }
    } );

    // Сохранение плана
    plan_el.addEventListener( "input", function ( event ) {
        const preset = value.innerText;
        if ( preset ) {
            presets[preset]["plan"] = plan_el.value;
            localStorage.setItem( "ai_presets", JSON.stringify( presets ) );
        }
    } );

    // Клики по опциям
    options.querySelectorAll( `.option` ).forEach( function ( option ) {
        option.addEventListener( "click", option_click );
        option.querySelector( `.trash` ).addEventListener( "click", del_click );
    } );
    function option_click ( event ) {
        const preset = event.currentTarget.firstElementChild.innerText;
        value.innerText = preset;
        prompt_el.value = presets[preset]["prompt"];
        plan_el.value = presets[preset]["plan"];
        localStorage.setItem( "ai_last_preset", preset );
    }
    if ( last_preset_option_el ) {
        last_preset_option_el.click();
    }
    function del_click( event ) {
        event.stopPropagation();
        const preset = event.currentTarget.previousElementSibling.innerText;
        const option = event.currentTarget.parentElement;
        if ( value.innerText === preset ) {
            value.innerText = "";
            prompt_el.value = "";
            plan_el.value = "";
        }
        option.remove();
        delete presets[preset];
        localStorage.setItem( "ai_presets", JSON.stringify( presets ) );
    }

    //#endregion rpompt и plan

});