{% extends "page.html" %}

{% block stylesheet %}
{{super()}}
<style type="text/css">
/* disable initial hide */
div#header, div#site {
    display: block;
}
#output-box {
    max-width: 90%;
    height: 300px;
    overflow-y: auto;
    white-space: pre-wrap;
    text-align: left;
    border: 1px solid #ccc;
    background-color: #f7f7f7;
    padding: 10px;
    border-radius: 5px;
    margin: 20px auto;
}
#button-container {
    display: flex;
    justify-content: center;
    align-items: center;
}
#back-button {
    display: none;
    background-color: #fc6404;
    margin-bottom: 20px;
}
</style>
{% endblock %}

{% block params %}
{{super()}}
data-base-url="{{base_url | urlencode}}"
{% endblock %}

{% block site %}

<div class="container">
    <div class="row">
        <div class="swan-info">
            <div id="swan-loader">
                <div class="loader-circle">
                    <img src="{{hub_prefix}}static/swan/logos/{{swan_logo_filename}}">
                </div>
                <div class="loader-line-mask">
                    <div class="loader-line"></div>
                </div>
                <span class="text">Creating environment...</span>
            </div>
        </div>
        <br><br>
        <div id="output-box"></div>
        <div id="button-container">
            <button class="btn btn-danger btn-lg" id="back-button">Restart session</button>
        </div>
    </div>
</div>

{% endblock %}

{% block script %}
    {{super()}}

<script type = "text/javascript">
    const base_url = document.body.dataset.baseUrl; // Use dataset for cleaner access
    const outputBox = document.getElementById('output-box');
    let has_failed = false, project_path = '/';

    const urlParams = new URLSearchParams(window.location.search);
    const file = `/${urlParams.get('file') || ""}`.replace(/\.\.\//g, '').replace(/\.\.\\/g, '');
    const user_interface = urlParams.get('user_interface') || 'lab';

    // Scroll to the output-box height
    outputBox.scrollIntoView({
        behavior: "smooth",
        block: "start" // aligns the top of the element with the top of the scroll area
    });

    const get_jh_auth_header = () => {
        const xsrf = document.cookie.match(/\b_xsrf=([^;]*)\b/);
        return xsrf ? { 'X-XSRFToken': xsrf[1] } : {};
    };

    // Function to trigger a visual error in the loader
    const trigger_visual_error = (message) => {
        const backButton = document.getElementById('back-button');
        const loader = document.getElementById('swan-loader');

        loader.getElementsByClassName('loader-line-mask')[0].style.display = 'none';
        loader.getElementsByClassName('text')[0].innerHTML = 'Error creating environment';
        backButton.onclick = () => window.location = '/hub/home?changeconfig';
        backButton.style.display = 'block';
        console.error(message);
    };

    // Function to process each chunk of data iteratively
    async function render_data_chunks(reader, decoder) {
        try {
            // Read chunks until the stream is done
            while (true) {
                // Wait for the next chunk of data
                const { done, value } = await reader.read();

                // Exit the loop if no more data is available
                if (done) {
                    // Output has errors -> change title and show button for going back to spawner
                    if (has_failed) trigger_visual_error("Environment creation failed");

                    // Output is fine -> Redirect to JupyterLab
                    else window.location = base_url + user_interface + project_path;

                    // Exit the loop after processing all data
                    break;
                }

                // Decode the chunk into text and split by newlines
                // Remove the last element (empty string) for avoiding an extra line break
                const chunked_lines = decoder.decode(value, { stream: true }).split('\n').slice(0, -1);
                chunked_lines.forEach(message_line => {
                    if (message_line.startsWith('REPO_PATH')) {
                        const repo_path = message_line.split(':')[1];
                        if (repo_path && repo_path !== '/') {
                            project_path = "/tree" + repo_path + file;
                        }
                    } 
                    // Usual case -> update output box with the message received
                    else {
                        outputBox.innerHTML += message_line;
                        outputBox.scrollTop = outputBox.scrollHeight;
                        if (message_line.includes('ERROR')) has_failed = true;
                    }
                    outputBox.innerHTML += '<br>';
                });
            }
        } catch (error) {
            trigger_visual_error(`Error reading stream: ${error}`);
        }
    }

    const params = ['repository', 'builder', 'builder_version', 'nxcals'].reduce((acc, key) => ({ ...acc, [key]: urlParams.get(key) || "" }), {});
    fetch(base_url + "api/customenvs?" + new URLSearchParams(params).toString(), {
        method: 'GET',
        headers: get_jh_auth_header()
    })
    .then(response => response.ok ? response.body.getReader() : Promise.reject(response.statusText))
    .then(reader => render_data_chunks(reader, new TextDecoder("utf-8")))
    .catch(error => trigger_visual_error(`Error fetching stream: ${error}`));
</script>
{% endblock %}
