{% extends "swan_page.html" %}

{% block main %}
<main class="container">
  <div class="row">
    <div id="swan-loader" class="d-flex flex-column align-items-center justify-content-center gap-3 py-5">
      <div class="loader-ring">
        <img src="{{ hub_prefix }}static/swan/logos/logo_swan_letters.png" alt="SWAN logo">
      </div>
      <h4 id="status-msg">Creating environment...</h4>
      <button id="restart-session" class="hidden btn btn-primary btn-lg">Restart session</button>
    </div>
  </div>
  <div class="row">
    <div class="card">
      <div id="output-box" class="card-body" style="font-family: monospace; height: 50vh; max-height: 50vh; overflow-y: auto; white-space: pre-wrap;"></div>
    </div>
  </div>
</main>
{% endblock %}

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

<script type = "text/javascript">
    const base_url = "{{ base_url }}";
    const hubPrefix = "{{ hub_prefix }}";
    const outputBox = document.getElementById('output-box');
    const restartSessionBtn = document.getElementById('restart-session');
    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';

    restartSessionBtn.addEventListener('click', () => window.location = `${hubPrefix}home?changeconfig`);

    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 loader = document.getElementById('swan-loader');
        const statusMsg = document.getElementById('status-msg');

        loader.classList.add('loader-error');
        statusMsg.textContent = 'Error creating environment';
        statusMsg.classList.add('text-danger');
        restartSessionBtn.classList.remove('hidden');
        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 => {
                    // Skip backend keepalive heartbeats
                    if (message_line.trim() === '# keepalive') return;

                    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.textContent += message_line;
                        outputBox.textContent += '\n';
                        outputBox.scrollTop = outputBox.scrollHeight; // Auto-scroll to the bottom
                        if (message_line.includes('ERROR')) has_failed = true;
                    }
                });
            }
        } 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 %}
