<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

        <title>ACE{{ ' ' + env if (env := mgr.options.get('env')) else '' }}: {{url}}</title>

    	<script language='javascript'>
            const display_errors = false;
            if (display_errors){
                window.onerror = (errorMsg, url, lineNumber) => {
                    document.getElementById('errors').innerText = `Error: ${errorMsg} at ${url}:${lineNumber}`;
                    return false;
                };

                window.addEventListener("unhandledrejection", (event) => {
                    document.getElementById('errors').innerText = `Error: ${event.reason.message} at ${event.reason.stack}`;
                });
            }
        </script>

        <script type="module" src="/node_modules/@finos/perspective-viewer/dist/cdn/perspective-viewer.js"></script>
        <script type="module" src="/node_modules/@finos/perspective-workspace/dist/cdn/perspective-workspace.js"></script>
        <script type="module" src="/node_modules/@finos/perspective-viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js"></script>
        <script type="module" src="/node_modules/@finos/perspective-viewer-d3fc/dist/cdn/perspective-viewer-d3fc.js"></script>
        <script type="module" src="/node_modules/perspective-viewer-datagrid-norollups/dist/cdn/perspective-viewer-datagrid-norollups.js"></script>
        <script type="module" src="/node_modules/perspective-viewer-summary/dist/cdn/perspective-viewer-summary.js"></script>

        <script type="importmap">
            {
                "imports": {
                    "flatbuffers": "/node_modules/flatbuffers/mjs/flatbuffers.js",
                    "tslib": "/node_modules/tslib/tslib.es6.js"
                }
            }
        </script>

        <link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@finos/perspective-viewer/dist/css/themes.css" />
        <link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@finos/perspective-workspace/dist/css/pro-dark.css" />

        <style>
            perspective-workspace {
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
            }

            .hidden-column-width {
                min-width: 1px !important;
                max-width: 1px !important;

                user-select: none;
                overflow: hidden;
                white-space: nowrap;
                text-overflow: clip;
            }

            .announcement-banner {
                display: block;
                position: fixed; /* Stay in place */
                z-index: 9999; /* Sit on top */
                left: 50%;
                top: 0%;
                transform: translate(-50%, 0%);
                padding: 5px;
                background-color: rgba(99, 55, 55, 0.8); /* Dark red background with opacity */
                color: white;
                font-family: "ui-monospace","SFMono-Regular","SF Mono","Menlo","Consolas","Liberation Mono",monospace;
                font-size: small;
                text-align: center;
                justify-content: center;
            }

            .announcement-banner:empty {
                padding: 0px;
            }

            .error-banner {
                display: block;
                position: fixed; /* Stay in place */
                z-index: 9999; /* Sit on top */
                left: 50%;
                top: 100%;
                transform: translate(-50%, -100%);
                padding: 0px;
                background-color: rgba(0, 0, 0, 0.8); /* Black background with opacity */
                color: white;
                font-family: "ui-monospace","SFMono-Regular","SF Mono","Menlo","Consolas","Liberation Mono",monospace;
                font-size: small;
                text-align: center;
                justify-content: center;
            }

            .loading-banner {
                display: block; 
                position: fixed; /* Stay in place */
                z-index: 9999; /* Sit on top */
                left: 50%;
                top: 50%;
                transform: translate(-50%, -100px);
                padding: 20px;
                background-color: rgba(0, 0, 0, 0.8); /* Black background with opacity */
                color: white;
                font-family: "ui-monospace","SFMono-Regular","SF Mono","Menlo","Consolas","Liberation Mono",monospace;
                font-size: 20px;
                border-radius: 10px;
                text-align: center;
                justify-content: center;
                --progress-width: 0;
            }

            .progress-indicator {
                left: 50%;
                transform: translate(-50%);
                margin-top: 10px;
                width: 100px;
                height: 10px;
                background-color: #111;
                border-color: #ddd;
                border-radius: 5px;
                border-width: 1px;
                border-style: solid;
                overflow: hidden;
                position: relative;
            }

            .progress-indicator::before {
                content: "";
                position: absolute;
                top: 0;
                left: 0;
                width: var(--progress-width, 0);
                height: 100%;
                background-color: #ddd;
            }

            .error-list {
                margin: 10px;
                color: red;
                list-style: none;
            }

            .announcement-list {
                margin: 10px;
                color: lightgrey;
                list-style: none;
            }

            .announcement-list li {
                text-align: left;
                font-size: x-small;
            }

            .login-dialog {
                display: none;
                position: fixed;
                z-index: 10000;
                left: 50%;
                top: 50%;
                transform: translate(-50%, -50%);
                background-color: #2a2a2a;
                padding: 20px;
                border-radius: 8px;
                border: 1px solid #444;
                color: white;
            }

            .login-dialog input {
                margin: 10px 0;
                padding: 5px;
                width: 200px;
                background-color: #333;
                border: 1px solid #555;
                color: white;
                display: block;
            }

            .login-dialog button {
                padding: 8px 16px;
                background-color: #444;
                border: none;
                color: white;
                cursor: pointer;
                border-radius: 4px;
                margin-top: 10px;
                width: 100%;
            }

            .login-dialog button:hover {
                background-color: #555;
            }
        </style>
    </head>
    <body>
        <div id="loading-banner" class="loading-banner">
            <span id="loading-message">...</span>
            <div class="progress-indicator"></div>
            <div>
                <ul class="error-list"></ul>
            </div>
            <div>
                <ul class="announcement-list"></ul>
            </div>
        </div>

        <div id="login-dialog" class="login-dialog">
            <h3>Login Required</h3>
            <input type="text" id="username" placeholder="Username" />
            <input type="password" id="password" placeholder="Password" />
            <button id="login-button">Login</button>
        </div>

        <div id="errors" class="error-banner"></div>
        <div id="announcement" class="announcement-banner"></div>
        <perspective-workspace id="workspace"></perspective-workspace>

        <script type="module">
            import {connectWorkspaceTables, wait_for_table, DefaultMap} from "/workspace_code/workspace_tables.js";
            import {installTableWorkarounds, ensureTablesForConfig, loadLayout, saveLayout} from "/workspace_code/table_workarounds.js";
            import {setupInspectorTable} from "/workspace_code/inspector.js";
            import {WebSocketHelper} from "/workspace_code/websocket_helper.js";

            {% if mgr.get_table_config_files() %}
                {% autoescape None %}
                {% import json %}
                const table_config = {{ json.dumps(mgr.read_table_config(), indent=4) }};
                {% else %}
                const table_config = {};
            {% end %}

            async function handleLogin() {
                return new Promise((resolve, reject) => {
                    const loginDialog = document.getElementById('login-dialog');
                    const loginButton = document.getElementById('login-button');
                    const usernameInput = document.getElementById('username');
                    const passwordInput = document.getElementById('password');

                    loginDialog.style.display = 'block';
                    
                    const handleLoginClick = async () => {
                        const username = usernameInput.value;
                        const password = passwordInput.value;
                        
                        if (!username || !password) {
                            return;
                        }

                        loginButton.removeEventListener('click', handleLoginClick);
                        loginDialog.style.display = 'none';
                        
                        const authHeader = 'Basic ' + btoa(username + ':' + password);
                        const response = await fetch("/ui-login", {
                            credentials: 'include',
                            headers: {
                                'Authorization': authHeader
                            }
                        });
                        
                        if (!response.ok) {
                            reject(new Error('Authentication failed'));
                            return;
                        }
                        
                        resolve(response);
                    };

                    loginButton.addEventListener('click', handleLoginClick);
                });
            }

            async function fetchWithAuth(url, options = {}) {
                // Always include credentials to send cookies
                options.credentials = 'include';
                
                let response = await fetch(url, options);
                if (response.status === 401) {
                    const authResponse = await handleLogin();
                    if (!authResponse) throw new Error('Authentication required');
                    
                    // Retry original request with cookies now set
                    response = await fetch(url, {
                        ...options,
                        credentials: 'include'
                    });
                }
                return response;
            }

            window.addEventListener("DOMContentLoaded", async function () {
                const workspace = window.workspace

                let mode = { editable: false };

                try {
                    document.getElementById('loading-message').innerText = "Logging in...";
                    await new Promise(res => setTimeout(res, 100));

                    {% try %}
                    {% if mgr.options.get('user_roles') %}
                    const user_roles = [{{ ', '.join(f'"{r}"' for r in mgr.options.get('user_roles').get(current_user, mgr.options.get('user_roles').get("DEFAULT", []))) }}]
                    {% else %}
                    const user_roles = [];
                    {% end %}
                    {% except %}
                    const user_roles = [];
                    {% end %}

                    {% if mgr.options.get('require_login') %}
                    {% end %}

                    {% if mgr.options.get('require_login') %}
                    const token = await (await fetchWithAuth("{{mgr.options['login_url']}}")).text();
                    {% else %}
                    const token = undefined;
                    {% end %}

                    {% if mgr.options.get('management_ws') %}
                    const management_ws = new WebSocketHelper("{{mgr.options['management_ws']}}", {
                        'Authorization': `Bearer ${token}`,
                        // Add any other required headers here
                    });
                    management_ws.connect().catch((e) => {
                        console.error(e);
                        const errorList = document.querySelector('.error-list');
                        errorList.innerHTML += `<li>Failed to connect to management server</li>`;
                    });

                    if (management_ws) {
                        const error_stats = new DefaultMap(() => ({count: 0, last: new Date()}));
                        function publishError(message) {
                            const now = new Date();
                            const stats = error_stats.get(message);
                            if (stats.count == 0 || (now - stats.last) > 60000) {
                                management_ws.send({type: "error", message: `${stats.count ? stats.count : 'first'} of: ${message}`});
                                stats.last = now;
                                stats.count = 0;
                            }
                            stats.count += 1;
                        }

                        const error_fn = console.error;
                        console.error = (...args) => {
                            error_fn(...args);
                            const message = args.join(' ');
                            publishError(message);
                        };

                        window.onerror = (errorMsg, url, lineNumber) => {
                            publishError(`Error: ${errorMsg} at ${url}:${lineNumber}`);
                            return false;
                        }

                        window.addEventListener("unhandledrejection", (event) => {
                            publishError(`Error: ${event.reason.message} at ${event.reason.stack}`);
                        });

                        console.server = (...args) => {
                            error_fn(...args);
                            management_ws.send({type: "log", message: args.join(' ')});
                        };
                    }
                    {% else %}
                    const management_ws = undefined;
                    {% end %}

                    window.workspace.addEventListener("workspace-layout-update", async () => {
                        setTimeout(async () => {
                            installTableWorkarounds(mode);
                            setupInspectorTable();
                        }, 100);
                    });

                    setTimeout(async () => {
                        document.getElementById('loading-message').innerText = "Setting up tables...";
                        const req = fetch("/layout/{{url}}");
                        await connectWorkspaceTables(workspace, table_config, user_roles, "{{'true' if mgr.is_new_api() else 'false'}}", management_ws);

                        document.getElementById('loading-message').innerText = "Loading layout...";
                        let json = await (await req).json();

                        if (json.version && json.version >= 1) {
                            json = await loadLayout(json);
                        }

                        await ensureTablesForConfig(json, (progress, message, error, key, status) => {
                            const progressBar = document.querySelector('.progress-indicator');
                            progressBar.style.setProperty('--progress-width', `${100 * progress}%`);
                            if (message){
                                document.getElementById('loading-message').innerText = "Loading " + message + "...";
                            }
                            if (error){
                                console.error(error);
                                const errorList = document.querySelector('.error-list');
                                errorList.innerHTML += `<li>${error}</li>`;
                            }
                            if (key) {
                                const announcementList = document.querySelector('.announcement-list');
                                let item = Array.from(announcementList.children).find(e => e.innerText.startsWith(`${key}:`));
                                if (!item) {
                                    item = document.createElement('li');
                                    announcementList.appendChild(item);
                                }
                                item.innerText = `${key}: ${status}`;
                            }
                        });

                        management_ws && management_ws.send_on_connect_message({type: 'load_layout', layout: '{{url}}'});
                        document.getElementById('loading-banner').style.display = 'none';

                        setTimeout(() => { // let all the queued date to hit the tables so that pivots have it upfront
                            window.workspace.restore(json);

                            window.workspace.addEventListener("workspace-new-view", async (event) => {
                                if (!mode.editable) {
                                    const yes = window.confirm("This layout is open readonly, would you like to make it editable? If you click OK any changes you make from now on will be automatically saved otherwise they will not be saved");
                                    if (yes) {
                                        mode.editable = true;
                                    }
                                }
                                await wait_for_table(window.workspace, event.detail.config.table);
                                event.stopImmediatePropagation();
                            });

                        }, 1000);

                    }, 50);

                    window.setTimeout(async () => {
                        window.workspace.addEventListener("workspace-layout-update", async () => {
                            if (mode.editable) {
                                const config = await window.workspace.save();
                                const has_viewers = Object.keys(config.viewers).length;
                                if (has_viewers) {  // prevent saving broken empty views
                                    const layout = await saveLayout(config);
                                    await fetch("/layout/{{url}}", {method: "post", body: JSON.stringify(layout)});
                                    management_ws && management_ws.send({type: 'save_layout', layout: '{{url}}'});
                                }
                            }
                        });
                    }, 10000);  // not saving anything for a bit
                } catch (error) {
                    console.error(error);
                    const errorList = document.querySelector('.error-list');
                    errorList.innerHTML += `<li>Authentication failed: ${error.message}</li>`;
                    document.getElementById('loading-banner').style.display = 'none';
                }
            });

        </script>
    </body>
</html>
