/**
 * List of voice types available
 * @type {string[]}
 */
window.voiceTypes = [];
/**
 * List of categories available
 * @type {string[]}
 */
window.categories = [];
/**
 * List of languages available
 * @type {string[]}
 */
window.languages = [];

/**
 * List of scheduled days available
 * @type {string[]}
 */
window.scheduledays = []

/**
 * Create a list item element
 * @param {String} text Text Content for the list item 
 * @param {String} className Specific class name for the list item
 * @returns {JQuery<HTMLElement>}
 */
function ListItem(text, className = "") {
    return $('<li>').addClass(className).text(text);
}

/**
 * WebSocket connection
 * @type {WebSocket}
 */
window.ws = null;

/**
 * Send a command to the WebSocket server.
 * @param {String} command command to send
 * @param {String} data data to send
 */
function sendCommand(command, data) {
    if (window.ws.readyState === WebSocket.OPEN) {
        window.ws.send(JSON.stringify({ command, data }));
    }
}

/**
 * Fetch API helper function
 * @param {string} endpoint Endpoint URL
 * @param {string} method Method (GET, POST, etc.)
 * @param {Object} headers Headers to include in the request
 * @param {Object} body Body of the request
 * @param {Function} cbOK Callback function for successful response
 * @param {Function} cbError Callback function for error response
 */
function fetchAPI(endpoint, method, headers = {}, body = null, cbOK, cbError) {
    let url = window.location.origin + "/api/" + endpoint;
    let options = {
        method: method,
        headers: headers
    }
    if (body !== null) {
        options.body = JSON.stringify(body);
        if (!options.headers['Content-Type']) {
            options.headers['Content-Type'] = 'application/json';
        }
    }
    fetch(url, options)
        .then(async (response) => {
            if (!response.ok) {
                let msg;
                try {
                    let _xxx = await response.json();
                    msg = _xxx.message || response.statusText;
                } catch {
                    msg = await response.statusText;
                }
                throw new Error(msg);
            }
            return response.json();
        })
        .then(data => {
            cbOK(data);
        })
        .catch(error => {
            cbError(error);
        });
}

/**
 * Fetch asset file from /assets/img/*
 * @param {String} url the filename to fetch, relative to /assets/img/
 * @param {Function} cbOK callback function on success, will receive the object URL
 * @param {Function} cbError callback function on error, will receive the error object
 */
function fetchImg(url, cbOK, cbError) {
    url = "/assets/img/" + url;
    fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok ' + response.statusText);
            }
            return response.blob();
        })
        .then(blob => {
            const url = URL.createObjectURL(blob);
            cbOK(url);
        })
        .catch(error => {
            cbError(error);
        });
}

/**
 * Reload voice types from server
 */
function getVoiceTypes() {
    window.voiceTypes = [];
    fetchAPI("VoiceType", "GET", {}, null, (okdata) => {
        // okdata is a string contains elements separated by semicolon ;
        if (Array.isArray(okdata)) {
            window.voiceTypes = okdata.filter(item => item.trim().length > 0);
            //console.log("Loaded " + voiceTypes.length + " voice types : " + voiceTypes.join(", "));
        } else console.log("getVoiceTypes: okdata is not array");
    }, (errdata) => {
        alert("Error loading voice types : " + errdata.message);
    });
}

/**
 * Reload categories from server
 */
function getCategories() {
    window.categories = [];
    fetchAPI("Category", "GET", {}, null, (okdata) => {
        // okdata is a string contains elements separated by semicolon ;
        if (Array.isArray(okdata)) {
            window.categories = okdata.filter(item => item.trim().length > 0);
            //console.log("Loaded " + categories.length + " categories : " + categories.join(", "));
        } else console.log("getCategories: okdata is not array");
    }, (errdata) => {
        alert("Error loading categories : " + errdata.message);
    });
}

/**
 * Reload languages from server
 */
function getLanguages() {
    window.languages = [];
    fetchAPI("Language", "GET", {}, null, (okdata) => {
        // okdata is a string contains elements separated by semicolon ;
        if (Array.isArray(okdata)) {
            window.languages = okdata.filter(item => item.trim().length > 0);
            //console.log("Loaded " + languages.length + " languages : " + languages.join(", ") );
        } else console.log("getLanguages: okdata is not array");
    }, (errdata) => {
        alert("Error loading languages : " + errdata.message);
    });
}

/**
 * Reload scheduled days from server
 */
function getScheduledDays() {
    window.scheduledays = [];
    fetchAPI("ScheduleDay", "GET", {}, null, (okdata) => {
        // okdata is a string contains elements separated by semicolon ;
        if (Array.isArray(okdata)) {
            window.scheduledays = okdata.filter(item => item.trim().length > 0);
            //console.log("Loaded " + scheduledays.length + " scheduled days : " + scheduledays.join(", ") );
        } else console.log("getScheduledDays: okdata is not array");
    }, (errdata) => {
        alert("Error loading scheduled days : " + errdata.message);
    });
}

/**
 * Clear database mechanism
 * @param {String} APIURL API URL endpoint 
 * @param {String} whattoclear what to clear
 * @param {Function} cbOK callback function on success
 * @param {Function} cbError callback function on error
 */
function DoClear(APIURL, whattoclear, cbOK, cbError) {
    if (confirm(`Are you sure want to clear ${whattoclear} ? This procedure is not reversible`)) {
        fetchAPI(APIURL + "List", "DELETE", {}, null, (okdata) => {
            cbOK(okdata);
        }, (errdata) => {
            cbError(errdata);
        });
    }
}


/**
 * Export mechanism to XLSX file
 * @param {String} APIURL API URL endpoint
 * @param {String} filename target filename
 * @param {Object} queryParams additional query parameters as object
 */
function DoExport(APIURL, filename, queryParams = {}) {
    // send GET request to APIURL + "ExportXLSX"
    // reply Content-Type is application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
    // reply Content-Disposition: attachment; filename=filename
    // Use fetch to download the XLSX file as a blob and trigger download
    let url = "/api/" + APIURL + "ExportXLSX";
    if (queryParams && Object.keys(queryParams).length > 0) {
        url += "?" + new URLSearchParams(queryParams).toString();
    }

    fetch(url, {
        method: "GET",
        headers: {}
    })
        .then(response => {
            if (!response.ok) throw new Error('Network response was not ok ' + response.statusText);
            return response.blob();
        })
        .then(blob => {
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            a.remove();
            window.URL.revokeObjectURL(url);
        })
        .catch(error => {
            alert("Error export to " + filename + ": " + error.message);
        });
    return; // prevent the rest of the function from running

}

/**
 * Import mechanism from XLSX file
 * @param {String} APIURL API URL endpoint
 * @param {Function<Object>} cbOK function that accept object data
 * @param {Function<Error>} cbError function that accept error object
 */
function DoImport(APIURL, cbOK, cbError) {
    // Open file selection dialog that accepts only .xlsx files
    // then upload to server using fetchAPI at "api/ImportXLSX" with POST method
    let fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = '.xlsx';
    fileInput.onchange = e => {
        let file = e.target.files[0];
        if (file) {
            let formData = new FormData();
            formData.append('file', file);
            fetch("/api/" + APIURL + "ImportXLSX", { method: 'POST', body: formData })
                .then(response => {
                    if (!response.ok) { throw new Error('Network response was not ok ' + response.statusText); }
                    return response.json();
                })
                .then(data => {
                    cbOK(data);
                })
                .catch(error => {
                    cbError(error);
                });
        } else {
            cbError(new Error("No file selected"));
        }
    };
    fileInput.click();
    fileInput.remove();
}


window.greencircle = null;
window.redcircle = null;
/**
 * App entry point
 */
$(document).ready(function () {
    document.title = "Automatic Announcement System"
    if (window.greencircle === null) {
        fetchImg('green_circle.png', (url) => { window.greencircle = url; }, (err) => { console.error("Error loading green_circle.png : ", err); });
    }
    if (window.redcircle === null) {
        fetchImg('red_circle.png', (url) => { window.redcircle = url; }, (err) => { console.error("Error loading red_circle.png : ", err); });
    }
    const wsURL = window.location.pathname + '/ws'
    if (chrome && chrome.runtime && chrome.runtime.lastError) {
        alert("Runtime error: " + chrome.runtime.lastError.message);
        return;
    }



    // reset status indicators
    function resetStatusIndicators() {
        $('#onlineindicator').attr('src', window.redcircle);
        $('#cpustatus').text("CPU : N/A");
        $('#ramstatus').text("RAM : N/A");
        $('#diskstatus').text("Disk : N/A");
        $('#networkstatus').text("Network : N/A");
        $('#datetimetext').text("Date/Time : N/A");
    }


    resetStatusIndicators();
    getVoiceTypes();
    getCategories();
    getLanguages();
    getScheduledDays();


    // reconnect handle
    let ws_reconnect;

    function reconnect() {
        if (window.ws && window.ws.readyState === WebSocket.OPEN) return;
        const s = new WebSocket(wsURL);
        s.addEventListener('open', () => {
            console.log('WebSocket connection established');
            $('#onlineindicator').attr('src', window.greencircle);

            if (ws_reconnect) {
                // stop reconnect attempts
                clearTimeout(ws_reconnect);
                ws_reconnect = null;
            }
            window.dispatchEvent(new Event('ws_connected'));
        });
        s.addEventListener('close', () => {
            console.log('WebSocket connection closed');
            window.dispatchEvent(new Event('ws_disconnected'));
            resetStatusIndicators();
            if (!ws_reconnect) {
                clearTimeout(ws_reconnect);
                ws_reconnect = null;
            }
            ws_reconnect = setTimeout(reconnect, 5000); // try to reconnect every 5 seconds
        });
        s.addEventListener('message', (event) => {
            if ($('#onlineindicator').attr('src') !== window.greencircle) {
                $('#onlineindicator').attr('src', window.greencircle);
            }
            let rep = JSON.parse(event.data);
            window.dispatchEvent(new CustomEvent('ws_message', { detail: rep }));
            let cmd = rep.reply
            let data = rep.data;
            if (cmd && cmd.length > 0) {
                switch (cmd) {
                    case "getCPUStatus":
                        $('#cpustatus').text("CPU : " + data)
                        break;
                    case "getMemoryStatus":
                        $('#ramstatus').text("RAM : " + data)
                        break;
                    case "getDiskStatus":
                        $('#diskstatus').text("Disk : " + data)
                        break;
                    case "getNetworkStatus":
                        let result = "";
                        let json = JSON.parse(data);
                        if (Array.isArray(json) && json.length > 0) {
                            json.forEach((net) => {
                                if (result.length > 0) result += "\n"
                                result += `${net.displayName} (${net.ipV4addr.join(";")}) TX:${(net.txSpeed / 1024).toFixed(1)} KB/s RX:${(net.rxSpeed / 1024).toFixed(1)} KB/s`
                            })
                        } else result = "N/A";
                        $('#networkstatus').text(result)
                        break;
                    case "getSystemTime":
                        $('#datetimetext').text(data)
                        break;

                }
            }
        });
        window.ws = s;
    }

    reconnect();
    window.addEventListener('beforeunload', () => {
        try{
            window.ws?.close(1000, "Client closed connection");
        } catch (error) {
            console.error("Error closing WebSocket connection:", error);
        }
    });

    setInterval(() => {
        sendCommand("getCPUStatus", "")
        sendCommand("getMemoryStatus", "")
        sendCommand("getDiskStatus", "")
        sendCommand("getNetworkStatus", "")
        sendCommand("getSystemTime", "")
    }, 1000)

    let sidemenu = new bootstrap.Offcanvas('#offcanvas-menu');
    $('#showmenu').click(() => { sidemenu.show(); })

    $('#homelink').click(() => {
        sidemenu.hide();
        $('#content').load('overview.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Overview content loaded successfully");
            }
        });
    });
    $('#soundbanklink').click(() => {
        sidemenu.hide();
        $('#content').load('soundbank.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Soundbank content loaded successfully");
                // pindah soundbank.js

            } else {
                console.error("Error loading soundbank content : ", xhr.status, xhr.statusText);
            }
        });

    })
    $('#messagebanklink').click(() => {
        sidemenu.hide();
        $('#content').load('messagebank.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Messagebank content loaded successfully");
                // pindah messagebank.js

            } else {
                console.error("Error loading messagebank content : ", xhr.status, xhr.statusText);
            }
        });

    })
    $('#languagelink').click(() => {
        sidemenu.hide();
        $('#content').load('language.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Language content loaded successfully");
                // pindah languagelink.js

            } else {
                console.error("Error loading language content : ", xhr.status, xhr.statusText);
            }
        });
    })
    $('#broadcastzonelink').click(() => {
        sidemenu.hide();
        $('#content').load('broadcastzones.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Broadcast Zone content loaded successfully");
                // pindah ke broadcastzones.js

            } else {
                console.error("Error loading broadcast zone content : ", xhr.status, xhr.statusText);
            }
        });
    })
    $('#timerlink').click(() => {
        sidemenu.hide();
        $('#content').load('timer.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Timer content loaded successfully");
                // pindah ke schedulebank.js

            } else {
                console.error("Error loading timer content : ", xhr.status, xhr.statusText);
            }
        });
    })
    $('#loglink').click(() => {
        sidemenu.hide();
        $('#content').load('log.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Log content loaded successfully");
                // pindah ke log.js

            } else {
                console.error("Error loading log content:", xhr.status, xhr.statusText);
            }
        });
    })
    $('#usermanagement').click(() => {
        sidemenu.hide();
        $('#content').load('usermanagement.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("User Management content loaded successfully");
                // pindah ke usermanagement.js

            } else {
                console.error("Error loading user management content:", xhr.status, xhr.statusText);
            }
        });
    });
    $('#settinglink').click(() => {
        sidemenu.hide();
        $('#content').load('setting.html', function (response, status, xhr) {
            if (status === "success") {
                console.log("Setting content loaded successfully");
                //sendCommand("getSetting", "");
            } else {
                console.error("Error loading setting content:", xhr.status, xhr.statusText);
            }
        });
    })
    $('#logoutlink').click(() => {
        window.location.href = "login.html"
    })

});