﻿
$(document).ready(function () {

    ConSyst.isFormChanged = false;
    $(document).on('change', '#form[method="post"] select', function () {
        ConSyst.formChanged();
    });
    $(document).on('input', '#form[method="post"] input, #form[method="post"] textarea, #form[method="post"] [contenteditable="true"]', function () {
        ConSyst.formChanged();
    });
    $('#topNavbar .nav-link').on('click', function () {
        $('#sidenav-left .scroll-section.show').removeClass('show');
    });
    $(window).on('beforeunload', function (event) {
        if (ConSyst.isFormChanged) {
            // Set a custom message
            const confirmationMessage = ConSyst.localizeText('You have unsaved changes. Are you sure you want to leave this page?');

            // Standard for most browsers, but might give deprecation warnings
            /* jshint ignore:start */
            // eslint-disable-next-line no-console
            //event.returnValue = confirmationMessage;
            /* jshint ignore:end */

            // For some older browsers
            // For some older browsers
            return confirmationMessage;
        }
    });
    let lastInteraction = null;
    let lastInteractionTime = 0;

    if (ConSyst.isMobile()) {
        document.addEventListener('pointerdown', (evt) => {
            lastInteraction = evt.pointerType;
            const now = new Date();
            lastInteractionTime = now.getSeconds() * 1000 + now.getMilliseconds();
        }, true);

        $(document).on('blur', 'input', function (evt) {
            const now = new Date();
            const timeSinceLastInteraction = now.getSeconds() * 1000 - lastInteractionTime;
            if (lastInteraction === 'touch' && timeSinceLastInteraction < 10) {
                // Treat this as a click
                // Omit navigation key function
            } else {
                // Treat this as a key
                const current = $(this);
                ConSyst.navigationKeyFunction(evt, current, 9, false);
            }
        });
    }

    ConSyst.setInputsAutoCaps();
    ConSyst.setInputsKeys();
    ConSyst.setDateInputsBlur();

    // Don't deactivate form labels, make them fixed on top
    var inputs = $('.form-outline .form-control');
    inputs.addClass("active");
    inputs.on('blur', function () { 
        var label = $(this).siblings('.form-label');
        label.addClass('active');
        $(this).addClass('active');
        //$(".select-input").addClass("active");
    });

    // Run backend alerts automatically
    var alerts = $(".alert");
    for (var i = 0; i < alerts.length; i++) {
        animation = mdb.Animate.getInstance(alerts[i]);
        if (animation != null) {
            animation.stopAnimation();
            animation.dispose();
        }
        animation = mdb.Animate.getOrCreateInstance(alerts[i]);
        animation.changeAnimationType('fade-in-right');
        animation.startAnimation();
        alert = new mdb.Alert(alerts[i]);
        alert.show();
    }

    //Modal Events
    ConSyst.setModalEvents('.modal');


    if (ConSyst.isLogged) {
        // Unit search initialization
        function calculateSimilarityIndex(searchString, resultString) {
            const normalizedSearchString = (searchString ?? "").toString().toUpperCase();
            const normalizedResultString = (resultString ?? "").toString().toUpperCase();

            if (normalizedSearchString === "" || !normalizedResultString.includes(normalizedSearchString)) {
                return 0;
            }
            else {
                return normalizedResultString.length > 0 ? normalizedSearchString.length / normalizedResultString.length : 0;
            }
        }

        function getGlobalSearchType(searchValue) {
            const searchPrefix = searchValue.substring(0, 2).toLowerCase();

            return searchPrefix === "u:" ?
                "Unit"
                : searchPrefix === "t:" ?
                    "Transaction"
                    : searchPrefix === "i:" ?
                        "Invoice"
                        : null;
        }

        function getGlobalSearchTerm(searchValue, searchType) {
            return searchType == null ? searchValue : searchValue.substring(2).trim();
        }

        function parseGlobalSearchResult(serviceResult) {
            if (serviceResult.ICode >= 0) {
                try {
                    return serviceResult.SValue != null && serviceResult.SValue !== "" ? JSON.parse(serviceResult.SValue) : [];
                }
                catch (error) {
                    console.error("Error parsing global search results:", error);
                    ConSyst.showMessage(ConSyst.localizeText("Error parsing search results"), 'danger');
                    return [];
                }
            }

            if (serviceResult.SCode != "N/A") {
                ConSyst.showMessage(serviceResult.SName, 'danger');
            }

            return [];
        }

        function sortGlobalSearchResults(results) {
            return results.sort((a, b) => b.ISearchSimilarity - a.ISearchSimilarity);
        }

        function setGlobalSearchTypeResults(searchType, results) {
            if (searchType === "Unit") {
                ConSyst.menuUnitResults = results;
            }
            else if (searchType === "Transaction") {
                ConSyst.menuDocResults = results;
            }
            else if (searchType === "Invoice") {
                ConSyst.menuInvoiceResults = results;
            }
        }

        function setGlobalAutocompleteNoResultsMessage(isSearchComplete) {
            if (ConSyst.globalSearchAutocomplete != null && ConSyst.globalSearchAutocomplete._options != null) {
                ConSyst.globalSearchAutocomplete._options.noResults = isSearchComplete
                    ? ConSyst.localizeText("No results found")
                    : ConSyst.localizeText("Please wait while we complete your search");
            }
        }

        function updateGlobalAutocompleteResults(results, searchToken, isSearchComplete) {
            if (searchToken !== ConSyst.globalSearchToken) {
                return;
            }

            ConSyst.globalSearchResults = sortGlobalSearchResults(results);
            setGlobalAutocompleteNoResultsMessage(isSearchComplete);

            if (ConSyst.globalSearchAutocomplete != null && typeof ConSyst.globalSearchAutocomplete._updateResults === "function") {
                ConSyst.globalSearchAutocomplete._updateResults(ConSyst.globalSearchResults);
            }
        }

        function getGlobalSearchServiceCalls(searchType, searchValue, isGlobalInputVal) {
            const searchServices = {
                Unit: "GetUnitSearchResults",
                Transaction: "GetTransactionSearchResults",
                Invoice: "GetInvoiceSearchResults"
            };

            const selectedSearchTypes = searchType == null ? Object.keys(searchServices) : [searchType];

            return selectedSearchTypes.map((selectedSearchType) => ({
                SearchType: selectedSearchType,
                Promise: ConSyst.callService("Unit", "GET", searchServices[selectedSearchType], { search: searchValue, isGlobal: isGlobalInputVal }, {}, "json")
                    .catch((error) => {
                        console.error("Error calling global search service:", error);
                        return { ICode: -1, SCode: "N/A", SName: "", SValue: null };
                    })
            }));
        }

        function buildUnitSearchResults(unitResults, searchValue) {
            var units = parseGlobalSearchResult(unitResults);

            const unitMap = units.reduce((accumulator, item) => {
                if (item.SUnit != null) {
                    if (!accumulator[item.SUnit]) {
                        accumulator[item.SUnit] = {
                            SResultType: "Unit",
                            SId: item.SUnit,
                            SUnit: item.SUnit,
                            SOldUnit: item.SOldUnit,
                            SUnitStatus: item.SUnitStatus,
                            SUnitStatusClass: item.SUnitStatusClass,
                            SUnitStatusIcon: item.SUnitStatusIcon,
                            SRepairStatus: item.SRepairStatus,
                            SRepairStatusClass: item.SRepairStatusClass,
                            SRepairStatusIcon: item.SRepairStatusIcon,
                            SDepot: item.SDepot,
                            SCustomer: item.SCustomer,
                            SCustomerName: item.SCustomerName,
                            ICustomer: item.ICustomer,
                            SGrade: item.SGrade,
                            BOnHold : item.BOnHold,
                            SUnitStatusName: item.SUnitStatusName,
                            SStatusNotes: item.SStatusNotes,
                            SOnHoldReason: item.SOnHoldReason,
                            TIngateDate: item.TIngateDate,
                            IDocCount: item.IDocCount,
                            ISearchSimilarity: Math.max(
                                calculateSimilarityIndex(searchValue, item.SUnit),
                                calculateSimilarityIndex(searchValue, item.SOldUnit ?? "")
                            )
                        };
                    }
                }
                return accumulator;
            }, {});

            return Object.values(unitMap);
        }

        function buildTransactionSearchResults(transactionResults, searchValue) {
            var transactions = parseGlobalSearchResult(transactionResults);

            const docGroups = transactions.reduce((accumulator, item) => {
                var itemType = item.IAcceptance != null ? `${ConSyst.localizeText('Acceptance')}` : (
                    item.IGateEventIn != null ? `${ConSyst.localizeText('Inbound')}` : (
                        item.IBooking != null ? `${ConSyst.localizeText('Booking')}` : (
                            item.IGateEventOut != null ? `${ConSyst.localizeText('Outbound')}` : (
                                item.IEstimate != null ? `${ConSyst.localizeText('Estimate')}` : (
                                    item.IRepair != null ? `${ConSyst.localizeText('Repair')}` : ``)))));
                var itemCode = item.SAcceptance ?? item.SGateEventIn ?? item.SBooking ?? item.SGateEventOut ?? item.SEstimate ?? item.SRepair;
                var itemId = item.IAcceptance ?? item.IGateEventIn ?? item.IBooking ?? item.IGateEventOut ?? item.IEstimate ?? item.IRepair;

                if (itemId == null || itemType === "") {
                    return accumulator;
                }

                var itemKey = `${itemType[0]}:${itemId}`;

                if (!accumulator[itemKey]) {
                    accumulator[itemKey] = {
                        IId: itemId,
                        SCode: itemCode,
                        SType: itemType,
                        SUnit: item.SUnit,
                        SDepot: item.SDepot,
                        SCustomer: item.SCustomer,
                        SCustomerName: item.SCustomerName,
                        ICustomer: item.ICustomer,
                        TDocTime: item.TDocTime,
                        IUnitCount: 0,
                        Units: [],
                        UnitSimilarities: []
                    };
                }

                if (item.SUnit != null && item.SUnit != "") {
                    accumulator[itemKey].IUnitCount++;
                    if (!accumulator[itemKey].Units.includes(item.SUnit)) {
                        accumulator[itemKey].Units.push(item.SUnit);
                        accumulator[itemKey].UnitSimilarities.push(calculateSimilarityIndex(searchValue, item.SUnit));
                    }
                }

                return accumulator;
            }, {});

            return Object.keys(docGroups).map(docKey => {
                const bestUnitSimilarity = docGroups[docKey].UnitSimilarities.length > 0 ? Math.max(...docGroups[docKey].UnitSimilarities) : 0;
                const bestUnitIndex = docGroups[docKey].UnitSimilarities.indexOf(bestUnitSimilarity);

                return {
                    SResultType: "Transaction",
                    SId: docGroups[docKey].IId.toString(),
                    IDoc: docGroups[docKey].IId,
                    SCode: docGroups[docKey].SCode,
                    IUnitCount: docGroups[docKey].IUnitCount,
                    SType: docGroups[docKey].SType,
                    SDocKey: docKey,
                    SUnit: bestUnitIndex >= 0 ? docGroups[docKey].Units[bestUnitIndex] : docGroups[docKey].SUnit,
                    SDepot: docGroups[docKey].SDepot,
                    SCustomer: docGroups[docKey].SCustomer,
                    SCustomerName: docGroups[docKey].SCustomerName,
                    ICustomer: docGroups[docKey].ICustomer,
                    TDocTime: docGroups[docKey].TDocTime,
                    ISearchSimilarity: Math.max(
                        calculateSimilarityIndex(searchValue, docGroups[docKey].SCode),
                        calculateSimilarityIndex(searchValue, docGroups[docKey].IId.toString())
                    )
                };
            });
        }

        function buildInvoiceSearchResults(invoiceResults, searchValue) {
            var invoices = parseGlobalSearchResult(invoiceResults);

            return invoices.map((item) => ({
                SResultType: "Invoice",
                SId: item.IInvoice.toString(),
                IDoc: item.IInvoice,
                SCode: item.SInvoice,
                SType: ConSyst.localizeText('Invoice'),
                SDocKey: `V:${item.IInvoice}`,
                SUnit: item.SUnit,
                SDepot: item.SDepot,
                SCustomer: item.SCustomer,
                SCustomerName: item.SCustomerName,
                ICustomer: item.ICustomer,
                TDocTime: item.TDocTime,
                FTotal: item.FTotal,
                ISearchSimilarity: Math.max(
                    calculateSimilarityIndex(searchValue, item.SInvoice),
                    calculateSimilarityIndex(searchValue, item.SOverageCode),
                    calculateSimilarityIndex(searchValue, item.SAltCode),
                    calculateSimilarityIndex(searchValue, item.SAltOverageCode),
                    calculateSimilarityIndex(searchValue, item.IInvoice.toString())
                )
            }));
        }

        function buildGlobalSearchResults(searchType, serviceResult, searchValue) {
            if (searchType === "Unit") {
                return buildUnitSearchResults(serviceResult, searchValue);
            }
            else if (searchType === "Transaction") {
                return buildTransactionSearchResults(serviceResult, searchValue);
            }
            else if (searchType === "Invoice") {
                return buildInvoiceSearchResults(serviceResult, searchValue);
            }

            return [];
        }

        function getGlobalSearchDocumentUrl(value) {
            if (value.SResultType == "Invoice") {
                return `/Invoice/Edit/${value.IDoc}`;
            }
            else if (value.SType == ConSyst.localizeText('Acceptance')) {
                return `/Acceptance/Edit/${value.IDoc}`;
            }
            else if (value.SType == ConSyst.localizeText('Inbound')) {
                return `/GateEvent/Edit/${value.IDoc}?type=I`;
            }
            else if (value.SType == ConSyst.localizeText('Booking')) {
                return `/Booking/Edit/${value.IDoc}`;
            }
            else if (value.SType == ConSyst.localizeText('Outbound')) {
                return `/GateEvent/Edit/${value.IDoc}?type=O`;
            }
            else if (value.SType == ConSyst.localizeText('Estimate')) {
                return `/Estimate/Edit/${value.IDoc}`;
            }
            else if (value.SType == ConSyst.localizeText('Repair')) {
                return `/Repair/Edit/${value.IDoc}?unitId=${value.SUnit}`;
            }

            return `/Unit`;
        }

        function getGlobalSearchDocumentLabel(value) {
            if (value.SResultType == "Invoice") {
                return ConSyst.localizeText('Invoice');
            }
            else if (value.SType == ConSyst.localizeText('Repair')) {
                return ConSyst.localizeText('Work Order');
            }

            return value.SType;
        }

        ConSyst.lastSearchVal = null;
        ConSyst.lastSearchTime = null;
        ConSyst.globalSearchResults = [];
        ConSyst.globalSearchToken = 0;
        ConSyst.menuUnitResults = [];
        ConSyst.menuDocResults = [];
        ConSyst.menuInvoiceResults = [];
        const asyncUnitAutocompleteJq = $('#menuUnitSearchDiv');
        const asyncUnitAutocomplete = asyncUnitAutocompleteJq[0];
        const asyncUnitFilter = async (query) => {
            var isGlobalInputVal = $("#menuUnitSearchIsGlobal").val() == "true" ? true : false;
            var currentSearchVal = query.trim();
            var currentSearchToken = ++ConSyst.globalSearchToken;
            ConSyst.lastSearchTime = new Date();
            ConSyst.lastSearchVal = currentSearchVal;
            ConSyst.globalSearchResults = [];
            ConSyst.menuUnitResults = [];
            ConSyst.menuDocResults = [];
            ConSyst.menuInvoiceResults = [];

            const searchType = getGlobalSearchType(currentSearchVal);
            const searchValue = getGlobalSearchTerm(currentSearchVal, searchType);
            const searchServiceCalls = getGlobalSearchServiceCalls(searchType, searchValue, isGlobalInputVal);
            var allResults = [];
            var completedSearchCount = 0;

            await Promise.all(searchServiceCalls.map(async (serviceCall) => {
                const serviceResult = await serviceCall.Promise;
                const serviceResults = buildGlobalSearchResults(serviceCall.SearchType, serviceResult, searchValue);
                setGlobalSearchTypeResults(serviceCall.SearchType, serviceResults);
                completedSearchCount++;

                if (currentSearchToken === ConSyst.globalSearchToken) {
                    allResults.push(...serviceResults);
                    updateGlobalAutocompleteResults(allResults, currentSearchToken, completedSearchCount === searchServiceCalls.length);
                }
            }));

            ConSyst.lastSearchTime = null;
            if (currentSearchToken === ConSyst.globalSearchToken) {
                setGlobalAutocompleteNoResultsMessage(true);
            }

            return currentSearchToken === ConSyst.globalSearchToken ? sortGlobalSearchResults(allResults) : ConSyst.globalSearchResults;
        };
        // Search input initialization
        ConSyst.globalSearchAutocomplete = new mdb.Autocomplete(asyncUnitAutocomplete, {
            debounce: 300,
            filter: asyncUnitFilter,
            displayValue: (value) => value.SId,
            noResults: ConSyst.localizeText("No results found"),
            threshold: 999,
            itemContent: (value) => {
                if ("SDocKey" in value) {
                    // Document display with enhanced information
                    var linkHtml = `<a href="#" onclick="window.open('${getGlobalSearchDocumentUrl(value)}');" class="btn btn-sm btn-rounded btn-primary"><i class="fas fa-eye"></i> ${getGlobalSearchDocumentLabel(value)}</a>`;

                    var depotBadge = value.SDepot ? `<span class="badge badge-info ms-1" title="${ConSyst.localizeText('Depot')}"><i class="fas fa-warehouse"></i> ${value.SDepot}</span>` : "";
                    var customerBadge = value.SCustomer ? `<span class="badge badge-secondary ms-1" title="${ConSyst.localizeText('Customer')}"><i class="fas fa-user"></i> ${value.SCustomer}</span>` : "";
                    var dateBadge = value.TDocTime ? `<span class="badge badge-light ms-1" title="${ConSyst.localizeText('Document Date')}"><i class="fas fa-calendar"></i> ${new Date(value.TDocTime).toLocaleDateString()}</span>` : "";
                    var detailText = value.SResultType == "Invoice"
                        ? (value.SUnit ? ` - ${value.SUnit}` : "")
                        : ` - ${value.IUnitCount} ${ConSyst.localizeText('Units')}`;
                    
                    var itemHtml = `
                    <div class="autocomplete-custom-item-content container">
                        <div class="autocomplete-custom-item-title">
                            <div class="d-flex justify-content-between align-items-center">
                                <div>
                                    <b>${value.SCode}</b>${detailText}
                                </div>
                                <div class="d-flex flex-wrap gap-1">
                                    ${depotBadge}${customerBadge}${dateBadge}
                                </div>
                            </div>
                        </div>
                        <div class="autocomplete-custom-item-subtitle d-flex justify-content-between">
                            <div>${linkHtml}</div>
                        </div>
                    </div>`;
                    return itemHtml;
                } else {
                    // Unit display with enhanced information
                    var isRepairStat = ConSyst.repairableShortUnitStatuses.includes(value.SUnitStatus);
                    var statusCode = !isRepairStat ? value.SUnitStatus : value.SRepairStatus ?? value.SUnitStatus;
                    var statusIcon = !isRepairStat ? value.SUnitStatusIcon : value.SRepairStatusIcon ?? value.SUnitStatusIcon;
                    var statusClass = !isRepairStat ? value.SUnitStatusClass : value.SRepairStatusClass ?? value.SUnitStatusClass;
                    var statusColor = statusClass.replace("badge-", "");

                    if (value.BOnHold) {
                        statusColor = "danger";
                        statusCode = "OH";
                        statusIcon = "fa-lock";
                    }

                    var statusTitle = value.BOnHold
                        ? (!value.SOnHoldReason ? `${value.SUnitStatusName || 'N/A'} (${ConSyst.localizeText('On-Hold')})` : `${value.SUnitStatusName || 'N/A'} (${ConSyst.localizeText('On-Hold')}: ${value.SOnHoldReason})`)
                        : (!value.SStatusNotes ? (value.SUnitStatusName || 'N/A') : `${value.SUnitStatusName}: ${value.SStatusNotes}`);

                    var depotBadge = value.SDepot ? `<span class="badge badge-info ms-1" title="${ConSyst.localizeText('Depot')}"><i class="fas fa-warehouse"></i> ${value.SDepot}</span>` : "";
                    var customerBadge = value.SCustomer ? `<span class="badge badge-secondary ms-1" title="${ConSyst.localizeText('Customer')}: ${value.SCustomerName || value.SCustomer}"><i class="fas fa-user"></i> ${value.SCustomer}</span>` : "";
                    var gradeBadge = value.SGrade ? `<span class="badge badge-primary ms-1" title="${ConSyst.localizeText('Grade')}"><i class="fas fa-star"></i> ${value.SGrade}</span>` : "";
                    var ingateBadge = value.TIngateDate ? `<span class="badge badge-success ms-1" title="${ConSyst.localizeText('Last Ingate')}"><i class="fas fa-sign-in-alt"></i> ${new Date(value.TIngateDate).toLocaleDateString()}</span>` : "";
                    var onHoldBadge = value.BOnHold ? `<span class="badge badge-danger ms-1" title="${ConSyst.localizeText('On Hold')}"><i class="fas fa-lock"></i> ${ConSyst.localizeText('Hold')}</span>` : "";

                    var itemHtml = `
                    <div class="autocomplete-custom-item-content container">
                        <div class="autocomplete-custom-item-title">
                            <div class="d-flex justify-content-between align-items-center flex-wrap">
                                <div class="mb-1">
                                    <a onclick="window.open('/Unit/Inquiry/${value.SUnit}','_blank');" class="btn btn-sm btn-rounded btn-${statusColor}" title="${statusTitle}">
                                        <i class="fas ${statusIcon}"></i> ${statusCode}: ${value.SUnit}
                                    </a>
                                </div>
                                <div class="d-flex flex-wrap gap-1 align-items-center">
                                    ${depotBadge}${customerBadge}${gradeBadge}${ingateBadge}${onHoldBadge}
                                    <span class="badge badge-light ms-1" title="${ConSyst.localizeText('Transaction Count')}">
                                        <i class="fas fa-file-alt"></i> ${value.IDocCount} ${ConSyst.localizeText('Trans.')}
                                    </span>
                                </div>
                            </div>
                        </div>
                    </div>`;
                    return itemHtml;
                }
            },
            customContent: `
            <div class="autocomplete-custom-content"></div>
            `,
        });
        asyncUnitAutocomplete.addEventListener('update.mdb.autocomplete', (event) => {
            //ConSyst.menuUnitResults = event.results;
            if (event.results != null) {
                var menuSearchAutocompleteSelector = `#autocomplete-dropdown-${$(event.currentTarget).attr("id")}`;
                setTimeout(() => {
                    var customContentContainer = $(menuSearchAutocompleteSelector).find('.autocomplete-custom-content');
                    let unitsArray = event.results.filter(obj => obj.SResultType == "Unit" || (obj.SResultType == null && obj.hasOwnProperty('SUnit') && !obj.hasOwnProperty('IDoc')));
                    let transArray = event.results.filter(obj => obj.SResultType == "Transaction" || (obj.SResultType == null && obj.hasOwnProperty('IDoc')));
                    let invoiceArray = event.results.filter(obj => obj.SResultType == "Invoice");
                    var customContentHtml = `
                    <div class="border-top d-flex">
                        <span><b>${ConSyst.localizeText('Units:')}</b> ${unitsArray.length}</span>,
                        <span class="ms-1"><b>${ConSyst.localizeText('Transactions:')}</b> ${transArray.length}</span>,
                        <span class="ms-1"><b>${ConSyst.localizeText('Invoices:')}</b> ${invoiceArray.length}</span>
                    </div>`;
                    customContentContainer.html(customContentHtml);

                }, 0);
            }
        });
        asyncUnitAutocomplete.addEventListener('itemSelect.mdb.autocomplete', (event) => {
            if ("SDocKey" in event.value) {
                var value = event.value;
                var resultLink = getGlobalSearchDocumentUrl(value);
                //window.open(resultLink, '_self');
                location.href = resultLink;
            }
            else {
                location.href = `/Unit/Inquiry/${event.value.SUnit}`;
            }
        });


        //On enter key of global search input, start search
        $("#menuUnitSearch").on("keydown", function (e) {
            // e.which is 13 for Enter
            if (e.which === 13 || e.key =="Enter" || e.key=="enter") {
                e.preventDefault();              // prevent form submission or other defaults
                var query = $(this).val();
                ConSyst.globalSearch(query);   
            }
        });


    }
    ConSyst.loadTooltips();
    ConSyst.captureCurrentNavigationStep(false);
    ConSyst.captureCurrentListState();
    ConSyst.renderPageBreadcrumbs();
    window.addEventListener('popstate', function () {
        ConSyst.captureCurrentNavigationStep(true);
        ConSyst.captureCurrentListState();
        ConSyst.renderPageBreadcrumbs();
    });

    // Global ALT+S handler for saving
    $(document).on('keydown', function (event) {
        // Check for ALT+S combination (consistent key detection)
        if (event.altKey && event.code === 'KeyS') {
            event.preventDefault();

            // Find all modals on the page
            const modals = document.querySelectorAll('.modal');

            // Find the active modal (the one that is currently shown)
            const activeModal = Array.from(modals).find(m => {
                const style = window.getComputedStyle(m);
                return style.display !== 'none' && m.classList.contains('show');
            });
            if (activeModal) {
                // Find the save button in the active modal
                const saveButton = activeModal.querySelector('.btn-save-shortcut');
                // If the save button is found and not disabled, trigger a click on it
                if (saveButton && !saveButton.disabled) {
                    saveButton.click();
                }
            } else {

                // First check if we're on a page with a form#form (the main form from _Form.cshtml)
                const editForm = document.querySelector('form#form[method="post"]');

                if (!editForm) {
                    // No main form found, so we're probably on an index/listing page
                    // This handles Index.cshtml case where we don't want ALT+S to work
                    return;
                }

                // If no modal is active, submit the main form
                const mainForm = document.querySelector('form#form') || document.querySelector('form');

                if (mainForm) {
                    $(mainForm).trigger("submit");
                }
            }
        }
    });

    // Handle form submission with Ctrl + Enter
    $('form').on('keydown', function (event) {
        if (event.ctrlKey && (event.code === 'Enter' || event.code === 'NumpadEnter'))
        {
            event.preventDefault(); // Add this to prevent double submission
            $(this).trigger("submit");
        }
        else if (event.code === 'Enter' || event.code === 'NumpadEnter')
        {
            if ($(this).attr("method") !== "get")
            {
                if (!$(".wysiwyg-content").is(":focus") && !$("textarea").is(":focus"))
                {
                    event.preventDefault();
                }
            }
        }
    });

    // Define global keys
    ConSyst.globalKeys = ["Equal", "Enter", "NumpadEnter", "F1", "F2", "KeyM", "KeyO", "KeyX", "KeyU",
        "KeyY", "KeyA", "KeyB", "KeyE", "KeyR", "KeyG",
        "KeyD", "KeyF", "KeyP", "PageDown", "PageUp"];

    // General keydown handling everywhere
    $(document).on("keydown", function (event) {
        // Check if the pressed key is in globalKeys
        if (ConSyst.globalKeys.includes(event.code)) {
            switch (event.code) {
                case 'Equal':
                    if ($("#btnAdd").attr("disabled") || $("#btnAdd").length === 0) {
                        return;
                    }
                    $("#btnAdd").trigger("click");
                    event.preventDefault();
                    break;

                case 'Enter':
                case 'NumpadEnter':
                    $('button:focus').trigger("click");
                    const focusedLink = $('a:focus');
                    if (focusedLink.length > 0) {
                        window.location.href = focusedLink.attr('href');
                    }
                    break;

                case 'F1':
                    ConSyst.useInputHelp = !ConSyst.useInputHelp;
                    ConSyst.loadTooltips();
                    event.preventDefault();
                    break;

                case 'F2':
                    $("#menuUnitSearch").trigger("focus");
                    break;

                case 'KeyM': // Alt + M
                    if (event.altKey) {
                        var sidenavLeft = mdb.Sidenav.getInstance(document.getElementById('sidenav-left'));
                        sidenavLeft.toggle();
                    }
                    break;

                case 'KeyO': // Alt + O
                    if (event.altKey) {
                        var sidenavRight = mdb.Sidenav.getInstance(document.getElementById('sidenav-right'));
                        sidenavRight.show();
                    }
                    break;

                case 'KeyX': // Alt + X
                    if (event.altKey) {
                        var jqLink = $("i.fa-file-excel").parent("a.sidenav-link");
                        if (jqLink.length > 0) {
                            jqLink[0].click();
                        }
                    }
                    break;

                case 'KeyU': // Alt + U
                    if (event.altKey) {
                        window.location.href = "/Unit";
                    }
                    break;

                case 'KeyY': // Alt + Y
                    if (event.altKey) {
                        window.location.href = "/YardTruck";
                    }
                    break;

                case 'KeyA': // Alt + A
                    if (event.altKey) {
                        window.location.href = "/Acceptance";
                    }
                    break;

                case 'KeyB': // Alt + B
                    if (event.altKey) {
                        window.location.href = "/Booking";
                    }
                    break;

                case 'KeyE': // Alt + E
                    if (event.altKey) {
                        event.preventDefault();
                        window.location.href = "/Estimate";
                    }
                    break;

                case 'KeyR': // Alt + R
                    if (event.altKey) {
                        event.preventDefault();
                        window.location.href = "/Repair";
                    }
                    break;

                case 'KeyG': // Alt + G
                    if (event.altKey) {
                        window.location.href = "/GateEvent";
                    }
                    break;

                case 'KeyD': // Alt + D
                    if (event.altKey) {
                        event.preventDefault();
                        ConSyst.redirectToPage('/Estimate');
                    }
                    break;

                case 'KeyF': // Alt + F
                    if (event.altKey) {
                        event.preventDefault();
                        $("#menuUnitSearch").trigger("focus");
                    }
                    break;

                case 'KeyP': // Alt + P
                    if (event.altKey) {
                        var jqLink = $("i.fa-print").parent("a.sidenav-link");
                        if (jqLink.length > 0) {
                            jqLink[0].click();
                        }
                    }
                    break;

                case 'PageDown': // Alt + PageDown
                    if (event.altKey) {
                        event.preventDefault();
                        $("#previous-page").trigger("click");
                    }
                    break;

                case 'PageUp': // Alt + PageUp
                    if (event.altKey) {
                        event.preventDefault();
                        $("#next-page").trigger("click");
                    }
                    break;

                default:
                    break;
            }
        }
    });

    $('#next-page').on('click', function (event) {
        event.preventDefault();
        // Perform any additional actions if needed
        isDisabled = $(this).parent().hasClass("disabled");
        if(!isDisabled){
            location.href = $(this).attr('href');
        }
    });
    $('#previous-page').on('click', function (event) {
        event.preventDefault();
        // Perform any additional actions if needed
        isDisabled = $(this).parent().hasClass("disabled");
        if (!isDisabled) {
            location.href = $(this).attr('href');
        }
    });
    // Wait dialog on form submit
    $(document).on('submit', 'form:not(.json-form)', async function (e) {
        const form = $(this);
        var waitTitle = form.attr('data-cst-wait-title');
        var waitMessage = form.attr('data-cst-wait-message');
        var waitColor = form.attr('data-cst-wait-color');
        var waitMethod = form.attr('data-cst-wait-method');
        if (waitMethod == "" || waitMethod == null) {
            waitMethod = "ajax";
        }

        if (form.attr("method") == "post" && form.attr("id")=="form") {
            ConSyst.isFormChanged = false;
        }

        if (waitMethod == "ajax") {
            e.preventDefault();
            await ConSyst.waitResultModal(async () => {
                const formData = form.serialize();
                const actionUrl = form.attr('action');
                const methodType = form.attr('method') || 'POST';

                const response = await $.ajax({
                    url: actionUrl,
                    type: methodType,
                    data: formData,
                    dataType: 'html', // Expect HTML response from the server
                    statusCode: {
                        400: function (jqXHR) {
                            if (jqXHR.getResponseHeader("Location") != null) {
                                window.location.href = jqXHR.getResponseHeader("Location");
                            }
                            else {
                                ConSyst.showMessage(ConSyst.localizeText("There was an issue with your request. Contact support for more information"), "danger", null, null, null, null, 'top');
                            }
                        },
                        401: function (jqXHR) {
                            if (jqXHR.getResponseHeader("Location") != null) {
                                window.location.href = jqXHR.getResponseHeader("Location");
                            }
                            else {
                                ConSyst.showMessage(ConSyst.localizeText("There was an issue with your credentials. Please try to sign out and then sign in again"), "danger", null, null, null, null, 'top');
                            }
                        },
                        403: function (jqXHR) {
                            if (jqXHR.getResponseHeader("Location") != null) {
                                window.location.href = jqXHR.getResponseHeader("Location");
                            }
                            else {
                                ConSyst.showMessage(ConSyst.localizeText("You're not authorized for this resource. Contact support for more information"), "danger", null, null, null, null, 'top');
                            }
                        },
                        500: function (jqXHR) {
                            if (jqXHR.getResponseHeader("Location") != null) {
                                window.location.href = jqXHR.getResponseHeader("Location");
                            }
                            else {
                                ConSyst.showMessage(ConSyst.localizeText("Unexpected error occurred. Contact support for more information"), "danger", null, null, null, null,'top' );
                            }
                        }
                    }
                });
                const newUrl = $(response).filter('meta[name="url"]').attr('content');

                // Replace the current page content with the new content
                document.open();
                document.write(response);
                document.close();

                // Update the URL in the browser to reflect the new page
                if (newUrl) {
                    history.pushState({}, '', newUrl);
                }
            }, waitTitle ?? ConSyst.localizeText('Saving'), waitMessage ?? ConSyst.localizeText('Saving your changes. Please wait...'), waitColor);
        }
        else {
            const [modalJq, modal] = await ConSyst.showResultModal(waitTitle ?? ConSyst.localizeText('Saving'), waitMessage ?? ConSyst.localizeText('Saving your changes. Please wait...'), waitColor);
        }
    });

    Cookie.set('timezone', Intl.DateTimeFormat().resolvedOptions().timeZone);
    const offset = new Date().getTimezoneOffset();
    Cookie.set('tz_offset', offset);
    for (var a = 0; a < alertsToDismiss.length; a++) {
        ConSyst.dismissAlert($(`#${alertsToDismiss[a]} button`)[0], 5000);
    }

    $("textarea").each(function (index, element) {
        var elementJq = $(element);
        var width = elementJq.width();
        if (width > 0) {
            element.style.height = 'auto';
            var scrollHeight = element.scrollHeight;
            if (scrollHeight <= 60) {
                scrollHeight = 60;
            }
            else if (scrollHeight >= 200) {
                scrollHeight = 200;
            }
            elementJq.height(scrollHeight);
        }
    });
    const tDocTimeInput = $("#TDocTime");
    if (tDocTimeInput != undefined) {

        tDocTimeInput.on("blur change", function () {
            let val = $(this).val();
            if (!val) return;

            // Try to extract year from start of string (handles both "2025" and "2025-07-01T11:56")
            let yearMatch = val.match(/^(\d{2,4})/);
            if (yearMatch) {
                let year = parseInt(yearMatch[1], 10);
                let newYear = year;
                if (year === 25) {
                    newYear = 2025;
                }
                else if (year === 100) {
                    newYear = 2000;
                }
                // Only update if newYear is a valid 4-digit year between 1900 and 2100
                if (typeof newYear === 'number' && newYear >= 1900 && newYear <= 2100 && newYear.toString().length === 4) {
                    // Replace only the year part at the start
                    let newVal = val.replace(/^(\d{2,4})/, newYear);
                    $(this).val(newVal);
                }
            }
        });
    }
    $(document).on('click', 'th', function (event) {
        event.preventDefault();
        var $th = $(this);
        var sortAttribute = $th.find('[data-mdb-sort]').attr('data-mdb-sort');
        if (!sortAttribute || !sortAttribute.includes('-sort')) {
            return;
        }
        // Show wait modal instead of message toast
        ConSyst.showResultModal(
            ConSyst.localizeText('Loading'),
            ConSyst.localizeText('Your query is being loaded. Please wait...'),
            'primary'
        );


        // Get current query parameters
        var queryParams = ConSyst.getQueryParams();

        // Determine the new sort order
        var currentSort = queryParams.sort;
        var currentOrder = queryParams.sortOrder;
        var sortOrder = "asc"; // default

        if (currentSort === sortAttribute) {
            // Toggle order
            sortOrder = (currentOrder === "asc") ? "desc" : "asc";
        }

        // Update sort parameters
        queryParams.sort = sortAttribute;
        queryParams.sortOrder = sortOrder;

        // Build new URL with updated parameters
        var newUrl = ConSyst.buildUrlWithParams(window.location.pathname, queryParams);

        // Navigate to new URL to update the table
        window.location.href = newUrl;
    });
    // Add the missing function to build URL with parameters
    ConSyst.buildUrlWithParams = function (basePath, params) {
        // basePath puede ser "/Unit" o una URL completa
        const url = new URL(basePath, window.location.origin);
        const sp = url.searchParams;

        for (const key of Object.keys(params))
        {
            const val = params[key];
            sp.delete(key);
            if (val === undefined || val === null)
            {
                continue;
            }
            if (Array.isArray(val)) {
                if (val.length && typeof val[0] === 'object' && val[0] !== null)
                {
                    val.forEach((obj, i) => {
                        for (const prop in obj) {
                            const v = obj[prop];
                            if (v !== undefined && v !== null && v !== '') {
                                sp.append(`${key}[${i}].${prop}`, String(v));
                            }
                        }
                    });
                }
                else
                {
                    val.forEach(v => {
                        if (v !== undefined && v !== null && v !== '') sp.append(key, String(v));
                    });
                }
                continue;
            }
            if (val !== '') sp.set(key, String(val));
        }

        return url.toString();
    };

    // delete select duplicated fake-value
    $('select').each(function (index, element) {
        var selectJq = $(element);
        var fakeValue = selectJq.parent().find(".form-outline").find(".select-fake-value");
        if (fakeValue != null && fakeValue.length>1) {
            //remove last fake-value
            fakeValue.last().remove();
        }
    });

});

ConSyst.globalSearch = function (search = null, isGlobal=false) {
    if (search == null) {
        search = $("#menuUnitSearch").val();
    }
    $("#menuUnitSearchIsGlobal").val(isGlobal);
    if (search != null) {

        search = search.trim()

        ConSyst.globalSearchAutocomplete.initSearch(search);
    }
}
// Add this somewhere in your ConSyst object or a common JS file
ConSyst.isMobile = function () {
    return /Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop|BlackBerry/i.test(navigator.userAgent);
};

ConSyst.parseString = function (str) {
    if (str == null || str == "null") {
        return null;
    }
    return str;
}

ConSyst.formChanged = function () {
    ConSyst.isFormChanged = true;
    $(".btn-save").removeAttr("disabled");
}

ConSyst.sleep = (milliseconds) => {
    return new Promise(resolve => setTimeout(resolve, milliseconds));
}

ConSyst.loadTooltips = function (selector = null, opts = null, container = null, reset=false) {
    if (container != null && container != "") {
        container += " ";
    }
    else {
        container = "";
    }
    if (selector == null) {
        selector = `${container}a[title], ${container}button[title], ${container}input[title], ${container}div[title], ${container}span[title], ${container}a[data-mdb-original-title], 
            ${container}button[data-mdb-original-title], ${container}input[data-mdb-original-title], ${container}div[data-mdb-original-title], ${container}span[data-mdb-original-title]`;
    }
    var tooltipsJq = $(selector);
    if (!ConSyst.useInputHelp) {
        tooltipsJq.filter(".form-outline").removeAttr("title");
        tooltipsJq.filter("input").removeAttr("title");
        tooltipsJq = tooltipsJq.not("input");
        tooltipsJq = tooltipsJq.not(".form-outline");
    }
    tooltipsJq.each(async function (index, element) {
        var rowOpts = opts;
        if (rowOpts == null) {
            rowOpts = {};
            rowOpts.placement = 'auto';
            rowOpts.trigger = 'hover';
            rowOpts.delay = { "show": 700, "hide": 10 };
            rowOpts.sanitizeFn = ConSyst.sanitizeHtml;
            rowOpts.html = true;
        }
        var origTitle = $(element).attr("data-mdb-original-title") ?? null;
        if (origTitle != null && origTitle != "") {
            rowOpts.title = origTitle;
        }
        
        var tooltip = mdb.Tooltip.getInstance(element);
        if (tooltip == null) {
            var newTooltip = new mdb.Tooltip(element, rowOpts);
            //if ($(element).is("span")) {
            //    // add <i> icon to span
            //    $(element).append(' <i class="fas fa-circle-info text-primary"></i>')
            //}
            if ($(element).is(".form-outline")) {
                var labelJq = $(element).find("label");
                // add <i> icon to span
                infoIconJq = labelJq.find("i.fa-circle-info");
                if (infoIconJq.length == 0) {
                    labelJq.append(' <i class="fas fa-circle-info text-primary"></i>');
                }

            }
            //if (!('ontouchstart' in window)) {
            //    $(element).mouseenter(function () {
            //        newTooltip.enable();
            //    });
            //    $(element).mouseleave(async function () {
            //        newTooltip.disable();
            //    });
            //}
            //else {

            //    let longPressTimer;
            //    $(element).on('touchstart', function (e) {
            //        longPressTimer = setTimeout(function () {
            //            newTooltip.toggle();
            //        }, 1000); // 1000 milliseconds (1 second)
            //    });

            //    $(element).on('touchend', function (e) {
            //        clearTimeout(longPressTimer);
            //    });

            //    $(element).on('touchcancel', function (e) {
            //        clearTimeout(longPressTimer);
            //    });
            //}
        }
        else {
            if (reset) {
                tooltip.dispose();
                var newTooltip = new mdb.Tooltip(element, rowOpts);
                //if ($(element).is("span")) {
                //    // add <i> icon to span
                //    $(element).append(' <i class="fas fa-circle-info text-primary"></i>')
                //}
                if ($(element).is(".form-outline")) {
                    var labelJq = $(element).find("label");
                    // add <i> icon to span
                    infoIconJq = labelJq.find("i.fa-circle-info");
                    if (infoIconJq.length == 0) {
                        labelJq.append(' <i class="fas fa-circle-info text-primary"></i>');
                    }

                }
            }
        }

    });
}

ConSyst.setModalEvents = function (selector) {
    $(selector).each(function (index, element) {
        mdb.Modal.getOrCreateInstance(element);
    });
    // Modal default behaviour (auto stack)
    $(selector).on('show.mdb.modal', function (modalEvent) {
        ConSyst.openedModals++;
        var modalZIndex = 1050 + (5 * ConSyst.openedModals);
        var modalJq = $(modalEvent.currentTarget)
        modalJq.css('z-index', modalZIndex);
        //console.log(`show ${modalJq.attr("id")}, ${ConSyst.openedModals}`);
    });
    $(selector).on('shown.mdb.modal', function (modalEvent) {
        ConSyst.setInputsKeys();
        var modalJq = $(modalEvent.currentTarget);
        var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
        shouldHide = modalJq.data("should-hide");
        if (shouldHide) {
            modalJq.data("should-hide", null);
            modalJq.removeAttr("data-should-hide");
            modal.hide();
            //console.log(`* should-hide shown ${modalJq.attr("id")}, ${ConSyst.openedModals}`);
        }
        else {
            var modalZIndex = parseInt($(modalEvent.currentTarget).css('z-index'));
            var backdropJq = $(modal._backdrop._element);
            backdropJq.css('z-index', modalZIndex - 4);
        }
        //console.log(`shown ${modalJq.attr("id")}, ${ConSyst.openedModals}`);
    });
    $(selector).on('hide.mdb.modal', function (modalEvent) {
        ConSyst.openedModals--;
        modalJq = $(modalEvent.currentTarget);
        var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
        // Restore the added backdrop z-index
        var backdropJq = $(modal._backdrop._element);
        backdropJq.css('z-index', 1050);
        // Check remaining modals and update its z-index accordingly
        var maxZIndex = 1050 + (5 * ConSyst.openedModals);
        var modalsJq = $(".modal.show");
        modalsJq.each(function (index, element) {
            var modalJq = $(element);
            modalZIndex = parseInt(modalJq.css('z-index'));
            if (modalZIndex > maxZIndex) {
                modalJq.css('z-index', maxZIndex);
                var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
                var backdropJq = $(modal._backdrop._element);
                backdropJq.css('z-index', maxZIndex-4);
            }
        });

        //console.log(`hide ${modalJq.attr("id")}, ${ConSyst.openedModals}`);
    });
    $(selector).on('hidden.mdb.modal', function (modalEvent) {
        modalJq = $(modalEvent.currentTarget);
        // Restore the added backdrop z-index
        modalJq.css('z-index', 1055);

        shouldHide = modalJq.data("should-hide");
        if (shouldHide) {
            modalJq.data("should-hide", null);
            modalJq.removeAttr("data-should-hide");
            //console.log(`* should-hide hidden ${modalJq.attr("id")}, ${ConSyst.openedModals}`);
        }
        if (ConSyst.focusInputAfterModal != null) {
            $(ConSyst.focusInputAfterModal).trigger("focus");
            ConSyst.focusInputAfterModal = null;
        }
        //console.log(`hidden ${modalJq.attr("id")}, ${ConSyst.openedModals}`);
    });
}

ConSyst.buildUnitTypeFilterFunction = function (unitCategories) {
    return (value) => {
        return unitTypes.filter((item) => {
            valueParts = value.split(" ");
            return unitCategories.includes(item.SCategory) && (valueParts.every(subStr => item.SCode.toLowerCase().includes(subStr.toLowerCase())) || valueParts.every(subStr => item.SName.toLowerCase().includes(subStr.toLowerCase())));
        });
    };
}

function calculateSimilarityIndex(searchString, resultString) {
    if (!resultString || !searchString) return 0;
    if (!resultString.toUpperCase().includes(searchString.toUpperCase())) {
        return 0;
    } else {
        return resultString.length > 0 ? searchString.length / resultString.length : 0;
    }
}

ConSyst.unitTypeFilterFunction = (value) => {
    // Split criteria by comma and trim whitespace
    const criteria = value.split(',').map(crit => crit.trim()).filter(Boolean);

    // Filter items matching any of the criteria
    let filtered = unitTypes.filter((item) => {
        return criteria.some(crit => {
            const critParts = crit.split(" ").filter(Boolean);
            return critParts.every(subStr =>
                item.SCode.toLowerCase().includes(subStr.toLowerCase()) ||
                item.SName.toLowerCase().includes(subStr.toLowerCase())
            );
        });
    });

    // Calculate similarity and sort
    filtered = filtered.map(item => {
        item._similarity = Math.max(
            ...criteria.map(crit => Math.max(
                calculateSimilarityIndex(crit, item.SCode),
                calculateSimilarityIndex(crit, item.SName)
            ))
        );
        return item;
    });

    filtered.sort((a, b) => {
        // Sort first by similarity descending
        if (b._similarity !== a._similarity) {
            return b._similarity - a._similarity;
        }
        // Then by SCode alphabetically
        return a.SCode.localeCompare(b.SCode);
    });

    // Clean up temporary field
    filtered.forEach(item => { delete item._similarity; });

    return filtered;
};



// Html sanitization function
ConSyst.sanitizeHtml = function (input) {
    // Create a temporary jQuery object holding the HTML
    const $temp = $('<div>').html(input);

    // Define harmful tags and attributes
    const harmfulTags = ['script', 'object', 'embed', 'link'];
    const harmfulAttrs = ['onload', 'onclick', 'onerror'];

    // Remove harmful tags
    harmfulTags.forEach(tag => {
        $temp.find(tag).remove();
    });

    // Remove harmful attributes from all elements
    $temp.find('*').each(function () {
        const $element = $(this);
        harmfulAttrs.forEach(attr => {
            $element.removeAttr(attr);
        });
    });

    // Serialize back to HTML string
    let sanitizedHtml = $temp.html();

    // Replace &quot; with "
    sanitizedHtml = sanitizedHtml.replace(/&quot;/g, '"');

    return sanitizedHtml;
}

ConSyst.unescapeViewStringForJs = function (viewSafeString) {
    if (viewSafeString == null) {
        return null;
    }
    // Decode HTML entities
    
    let decodedString = viewSafeString.replaceAll('"', '\\"')

    decodedString = decodedString.replace(/&quot;/g, '"');
    decodedString = decodedString.replace(/&amp;/g, '&');
    decodedString = decodedString.replace(/&lt;/g, '<');    
    decodedString = decodedString.replace(/&gt;/g, '>');
    decodedString = decodedString.replace(/&nbsp;/g, ' ');
    decodedString = decodedString.replace(/&apos;/g, "'");

    //decodedString = decodedString.replace(/&copy;/g, '©');
    //decodedString = decodedString.replace(/&reg;/g, '®');
    //decodedString = decodedString.replace(/&trade;/g, '™');
    //decodedString = decodedString.replace(/&euro;/g, '€');
    //decodedString = decodedString.replace(/&dollar;/g, '$');
    //decodedString = decodedString.replace(/&pound;/g, '£');
    //decodedString = decodedString.replace(/&yen;/g, '¥');
    //decodedString = decodedString.replace(/&cent;/g, '¢');
    //decodedString = decodedString.replace(/&sect;/g, '§');
    //decodedString = decodedString.replace(/&copy;/g, '©');
    //decodedString = decodedString.replace(/&reg;/g, '®');

    return decodedString;
}

// Tool function
ConSyst.isSameOrigin = function (url) {
    const parsedUrl = new URL(url, window.location.origin);
    return parsedUrl.origin === window.location.origin;
}

ConSyst.getListStateKey = function (listKey = null) {
    const resolvedListKey = listKey || (ConSyst.controllerName ? `${ConSyst.controllerName}.Index` : null);
    return resolvedListKey ? `ConSyst.ListState.${resolvedListKey}` : null;
}

ConSyst.navigationTrailKey = 'ConSyst.NavigationTrail';
ConSyst.navigationTrailMaxItems = 5;
ConSyst.navigationTrailMemory = [];

ConSyst.getPageTitleText = function () {
    const titleElement = document.querySelector('#divTitleText h4');
    return titleElement?.textContent?.trim() || document.title.replace(/\s*-\s*ConSyst\s*$/i, '').trim();
}

ConSyst.getCurrentRelativeUrl = function () {
    return `${window.location.pathname}${window.location.search}`;
}

ConSyst.getNavigationTrail = function () {
    if (!window.sessionStorage) {
        return [...ConSyst.navigationTrailMemory];
    }

    try {
        const rawValue = sessionStorage.getItem(ConSyst.navigationTrailKey);
        const trail = rawValue ? JSON.parse(rawValue) : [];
        if (Array.isArray(trail) && trail.length > 0) {
            ConSyst.navigationTrailMemory = [...trail];
            return trail;
        }

        return [...ConSyst.navigationTrailMemory];
    }
    catch {
        return [...ConSyst.navigationTrailMemory];
    }
}

ConSyst.saveNavigationTrail = function (trail) {
    const normalizedTrail = trail.slice(-ConSyst.navigationTrailMaxItems);
    ConSyst.navigationTrailMemory = [...normalizedTrail];

    if (!window.sessionStorage) {
        return;
    }

    sessionStorage.setItem(ConSyst.navigationTrailKey, JSON.stringify(normalizedTrail));
}

ConSyst.upsertNavigationTrailEntry = function (entry, replaceCurrent = false) {
    if (!entry?.url) {
        return;
    }

    const normalizedEntry = {
        url: entry.url,
        title: entry.title || ConSyst.getPageTitleText(),
        controller: entry.controller || ConSyst.controllerName || '',
        action: entry.action || ConSyst.actionName || '',
        updatedAt: new Date().toISOString()
    };

    const trail = ConSyst.getNavigationTrail();
    const lastEntry = trail[trail.length - 1];

    if (!lastEntry) {
        trail.push(normalizedEntry);
    }
    else if (replaceCurrent || lastEntry.url === normalizedEntry.url) {
        trail[trail.length - 1] = { ...lastEntry, ...normalizedEntry };
    }
    else {
        trail.push(normalizedEntry);
    }

    ConSyst.saveNavigationTrail(trail);
}

ConSyst.captureCurrentNavigationStep = function (replaceCurrent = false) {
    ConSyst.upsertNavigationTrailEntry({
        url: ConSyst.getCurrentRelativeUrl(),
        title: ConSyst.getPageTitleText(),
        controller: ConSyst.controllerName,
        action: ConSyst.actionName
    }, replaceCurrent);
}

ConSyst.buildUrl = function (path, params = {}) {
    const searchParams = new URLSearchParams();

    Object.entries(params || {}).forEach(([key, value]) => {
        if (value == null || value === '') {
            return;
        }

        if (Array.isArray(value)) {
            value
                .filter(item => item != null && item !== '')
                .forEach(item => searchParams.append(key, item));
            return;
        }

        searchParams.append(key, value);
    });

    const queryString = searchParams.toString();
    return queryString ? `${path}?${queryString}` : path;
}

ConSyst.rememberListState = function (listKey, url, title = null) {
    const storageKey = ConSyst.getListStateKey(listKey);
    if (!storageKey || !window.sessionStorage) {
        return;
    }

    sessionStorage.setItem(storageKey, JSON.stringify({
        url,
        title: title || ConSyst.getPageTitleText(),
        updatedAt: new Date().toISOString()
    }));
}

ConSyst.getRememberedListState = function (listKey = null) {
    const storageKey = ConSyst.getListStateKey(listKey);
    if (!storageKey || !window.sessionStorage) {
        return null;
    }

    try {
        const rawValue = sessionStorage.getItem(storageKey);
        return rawValue ? JSON.parse(rawValue) : null;
    }
    catch {
        return null;
    }
}

ConSyst.captureCurrentListState = function (listKey = null, title = null) {
    if (ConSyst.actionName !== 'Index') {
        return;
    }

    const resolvedListKey = listKey || `${ConSyst.controllerName}.Index`;
    const currentUrl = ConSyst.getCurrentRelativeUrl();
    ConSyst.rememberListState(resolvedListKey, currentUrl, title);
}

ConSyst.syncListHistory = function (listKey, path, params = {}, replace = false, title = null) {
    const url = ConSyst.buildUrl(path, params);
    const state = {
        cstListKey: listKey,
        cstUrl: url
    };

    if (replace) {
        window.history.replaceState(state, '', url);
    }
    else {
        window.history.pushState(state, '', url);
    }

    ConSyst.rememberListState(listKey, url, title);
    ConSyst.upsertNavigationTrailEntry({
        url,
        title: title || ConSyst.getPageTitleText(),
        controller: ConSyst.controllerName,
        action: ConSyst.actionName
    }, true);
    ConSyst.renderPageBreadcrumbs();
    return url;
}

ConSyst.getReturnUrl = function (controllerName = null) {
    const urlParams = new URLSearchParams(window.location.search);
    const referrerParam = urlParams.get('referrer');
    if (referrerParam && ConSyst.isSameOrigin(referrerParam)) {
        return new URL(referrerParam, window.location.origin).pathname + new URL(referrerParam, window.location.origin).search;
    }

    const targetController = controllerName || ConSyst.controllerName;
    const rememberedState = ConSyst.getRememberedListState(targetController ? `${targetController}.Index` : null);
    if (rememberedState?.url) {
        const currentUrl = `${window.location.pathname}${window.location.search}`;
        if (rememberedState.url !== currentUrl) {
            return rememberedState.url;
        }
    }

    if (document.referrer) {
        try {
            if (ConSyst.isSameOrigin(document.referrer)) {
                const parsedReferrer = new URL(document.referrer, window.location.origin);
                const referrerParts = parsedReferrer.pathname.split('/').filter(Boolean);
                const referrerController = referrerParts[0] || '';
                const referrerAction = referrerParts[1] || 'Index';

                if (targetController
                    && referrerController.toLowerCase() === targetController.toLowerCase()
                    && referrerAction.toLowerCase() !== 'index') {
                    return null;
                }

                return `${parsedReferrer.pathname}${parsedReferrer.search}`;
            }
        }
        catch {
            return null;
        }
    }

    return null;
}

ConSyst.renderPageBreadcrumbs = function () {
    const trail = document.getElementById('pageBreadcrumbTrail');
    if (!trail) {
        return;
    }

    const navigationTrail = ConSyst.getNavigationTrail();
    if (navigationTrail.length === 0) {
        ConSyst.captureCurrentNavigationStep(true);
    }

    const trailEntries = ConSyst.getNavigationTrail();
    const crumbs = trailEntries.length > 0
        ? trailEntries.map((entry, index, allEntries) => ({
            label: entry.title || entry.controller || entry.url,
            url: entry.url,
            current: index === allEntries.length - 1
        }))
        : [{
            label: ConSyst.getPageTitleText(),
            url: ConSyst.getCurrentRelativeUrl(),
            current: true
        }];

    trail.innerHTML = crumbs.map(crumb => {
        if (crumb.current) {
            return `<li class="breadcrumb-item cst-breadcrumb-item active" aria-current="page"><span class="cst-breadcrumb-pill">${crumb.label}</span></li>`;
        }

        return `<li class="breadcrumb-item cst-breadcrumb-item"><a href="${crumb.url}" class="cst-breadcrumb-pill">${crumb.label}</a></li>`;
    }).join('');
}

ConSyst.navigateBack = function () {
    const returnUrl = ConSyst.getReturnUrl();
    if (returnUrl) {
        window.location.href = returnUrl;
    } else {
        window.history.back();
    }
}

ConSyst.setDateInputsBlur = function (selector = null) {
    if (selector == null) {
        selector = 'input[type="date"], input[type="datetime-local"]';
    }
    $(selector).off('blur');
    // Inputs desired behaviours
    $(selector).on('blur', function () {
        var inputValue = $(this).val();
        if (!inputValue) return;
        
        var isDateTimeLocal = $(this).attr("type") === "datetime-local";
        var datePart, timePart = "";
        
        if (isDateTimeLocal) {
            // Split datetime-local format: YYYY-MM-DDTHH:mm or YYYY-MM-DDTHH:mm:ss
            var parts = inputValue.split('T');
            if (parts.length === 2) {
                datePart = parts[0];
                timePart = parts[1];
            } else {
                datePart = inputValue;
            }
        } else {
            datePart = inputValue;
        }
        
        var dateParts = datePart.split('-');
        
        if (dateParts.length === 3 && (dateParts[0].length === 2 || dateParts[0].startsWith("0"))) {
            var fullYear;
            var yearStr = dateParts[0];
            
            // Handle years starting with "00" (e.g., "0026" -> "2026")
            if (yearStr.startsWith("00") && yearStr.length > 2) {
                var yearDigits = yearStr.substring(2);
                var yearInt = parseInt(yearDigits, 10);
                if (yearInt >= 60 && yearInt <= 99) {
                    fullYear = '19' + (yearDigits.length === 1 ? '0' + yearDigits : yearDigits);
                } else {
                    fullYear = '20' + (yearDigits.length === 1 ? '0' + yearDigits : yearDigits);
                }
            }
            else {
                // Handle 2-digit or 3-digit years starting with 0: 00-59 -> 2000-2059, 60-99 -> 1960-1999
                var yearInt = parseInt(yearStr, 10);
                if (yearInt >= 0 && yearInt <= 99) {
                    var paddedYear = yearInt < 10 ? '0' + yearInt : yearInt.toString();
                    if (yearInt >= 60 && yearInt <= 99) {
                        fullYear = '19' + paddedYear;
                    } else {
                        fullYear = '20' + paddedYear;
                    }
                } else {
                    fullYear = yearStr; // Already 4 digits or invalid
                }
            }
            
            var newDateValue = fullYear + '-' + dateParts[1] + '-' + dateParts[2];
            
            if (isDateTimeLocal && timePart) {
                newDateValue = newDateValue + 'T' + timePart;
            }
            
            $(this).val(newDateValue);
        }
    });
}

ConSyst.navigationKeyFunction = function (ev, current, keyCode, shiftKey) {
    // Get the index of the current input field in the inputs collection
    var index = ConSyst.inputs.index(current);
    // Saves move idx positions
    var move = 0;
    // Declare a variable to store the next or previous input field
    var next = null;
    // Check which arrow key was pressed
    switch (keyCode) {
        case 9:
            if (shiftKey) {
                move = -1;
            }
            else {
                move = 1;
            }
            next = ConSyst.inputs.eq(index + move);
            break;

        case 13:
            if (ConSyst.isMobile()) {
                if (shiftKey) {
                    move = -1;
                }
                else {
                    move = 1;
                }
                next = ConSyst.inputs.eq(index + move);
                break;
            }
            break;
        case 38: // Up arrow key
            // Get the closest row element
            var row = current.closest(".row");
            // Get the previous row element, if any
            var prevRow = row.prev(".row");
            // Get the column index of the current input field
            var colIndex = current.parent().index();
            // Get the input field in the same column of the previous row, if any
            next = prevRow.find(".col:eq(" + colIndex + ")").find("input:not([readonly])");
            break;
        case 40: // Down arrow key
            // Get the closest row element
            var row = current.closest(".row");
            // Get the next row element, if any
            var nextRow = row.next(".row");
            // Get the column index of the current input field
            var colIndex = current.parent().index();
            // Get the input field in the same column of the next row, if any
            next = nextRow.find(".col:eq(" + colIndex + ")").find("input:not([readonly])");
            break;
        default:
            return;
    }

    // Check if there is a next or previous input field
    if (next != null && next.length > 0) {
        var newMove = move;
        while (!next.is(selector) || next.is(":hidden")) {
            newMove = newMove + move;
            next = ConSyst.inputs.eq((index + newMove) % ConSyst.inputs.length);
        }
        // Move the focus to the next or previous input field
        //current.blur();
        if (current.hasClass("autocomplete-input")) {
            //autocomplete = mdb.Autocomplete.getInstance(current.parent()[0]);
            //autocomplete.close();
            //ev.preventDefault();
        }
        else {
            ev.preventDefault();
            next.focus();
        }
    }

}


ConSyst.keyDownFunction = function (ev) {
    // Get the current input field
    var current = $(this);
    
    switch (ev.which ?? ev.keyCode) {
        case 9:
        case 13:
            if (ConSyst.isMobile()) {
                ConSyst.navigationKeyFunction(ev, current, ev.which ?? ev.keyCode, ev.shiftKey);
            }
            break;
        case 38: // Up arrow key
        case 40: // Down arrow key
            ConSyst.navigationKeyFunction(ev, current, ev.which ?? ev.keyCode, ev.shiftKey);
            break;
        case 46: // Delete key
            // Check if the current input field is a textarea
            if (current.is('textarea')) break;
            current.val(null);
            current.trigger("input");
            if (current.hasClass("select-input")) {
                ConSyst.setSelectValue(current.parent().parent().find("select")[0], null)
            }
            break;
        default:
            if (current.hasClass("select-input")) {
                // For select input validations
                var closestParent = $(this).closest('.select-wrapper');
                var letter = ev.key.toUpperCase();
                var optionsJq = closestParent.find('select option');
                var firstMatch = optionsJq.filter(function () {
                    return $(this).text().toUpperCase().startsWith(letter);
                });
                if (firstMatch.length > 1) {
                    // Try filtering more with value
                    var secondMatch = firstMatch.filter(function () {
                        return $(this).attr("value").toUpperCase().startsWith(letter);
                    });
                    // Return to previous if nothing matches code
                    var firstMatch = secondMatch.length > 0 ? secondMatch : firstMatch;
                }
                else if (firstMatch.length == 0) {
                    firstMatch = optionsJq.filter(function () {
                        return $(this).attr("value").toUpperCase().startsWith(letter);
                    });
                }
                // Check result 
                if (firstMatch.length > 1) {
                    var select = mdb.Select.getInstance(closestParent.find(".select")[0]);
                    select?.open();
                    $(".select-filter-input").val(letter);
                    $(".select-filter-input").trigger("input");
                }
                else if (firstMatch.length == 1) {
                    closestParent.find(".select").val(firstMatch.attr("value"));
                    ConSyst.setSelectValue(closestParent.find(".select"), firstMatch.attr("value"), true, true);
                }
            }
            else if (!ev.altKey && (current.attr("type") == 'date' || current.attr("type") == 'datetime-local')) {
                // For date inputs
                var letter = ev.key?.toUpperCase();
                const isOnlyDate = current.attr("type") == 'date';
                //next = inputs.eq(index + 1);
                switch (letter) {
                    case "T": // Today
                        var today = new Date();
                        var formattedDate = ConSyst.formatDateTimeString(today, isOnlyDate);
                        current.val(formattedDate);
                        break;
                    case "Y": // Yesterday for past and year for future
                        if (current.hasClass("past-date")) {
                            var yesterday = new Date();
                            yesterday.setDate(yesterday.getDate() - 1);
                            var formattedDate = ConSyst.formatDateTimeString(yesterday, isOnlyDate); 
                            current.val(formattedDate);
                        }
                        else {
                            var date = new Date();
                            date.setFullYear(date.getFullYear() + 1);
                            var formattedDate = ConSyst.formatDateTimeString(date, isOnlyDate);
                            current.val(formattedDate);
                        }
                        break;
                    case "M": // 1 Month after for future and 1 month before for past
                        var monthChg = current.hasClass("past-date") ? -1 : 1;
                        var date = new Date();
                        var newMonth = date.getMonth() + monthChg;
                        if (newMonth > 12) {
                            var newYear = date.getFullYear() + 1;
                            newMonth = 1;
                        }
                        else if (newMonth < 1) {
                            var newYear = date.getFullYear() - 1;
                            newMonth = 12;
                        }
                        else {
                            var newYear = date.getFullYear()
                        }
                        date.setFullYear(newYear, newMonth);
                        var formattedDate = ConSyst.formatDateTimeString(date, isOnlyDate);
                        current.val(formattedDate);
                        break;
                    case "E": // End of month
                        var date = new Date();
                        date.setMonth(date.getMonth() + 1);
                        date.setDate(0);
                        var formattedDate = ConSyst.formatDateTimeString(date, isOnlyDate);
                        current.val(formattedDate);
                        var date = new Date();
                        break;
                    case "B": // Begin of month
                        var date = new Date();
                        date.setDate(1);
                        var formattedDate = ConSyst.formatDateTimeString(date, isOnlyDate);
                        current.val(formattedDate);
                        break;
                    case "S": // TEC 6, 2 months after for future and 2 months before for past
                        var monthChg = current.hasClass("past-date") ? -2 : 2;
                        var date = new Date();
                        var newMonth = date.getMonth() + monthChg;
                        if (newMonth > 12) {
                            var newYear = date.getFullYear() + 1;
                            newMonth = 1;
                        }
                        else if (newMonth < 1) {
                            var newYear = date.getFullYear() - 1;
                            newMonth = 12;
                        }
                        else {
                            var newYear = date.getFullYear()
                        }
                        date.setFullYear(newYear, newMonth);
                        var formattedDate = ConSyst.formatDateTimeString(date, isOnlyDate);
                        current.val(formattedDate);
                        break;
                    default:
                        break;

                }
            }
            return;
    }

}

ConSyst.formatDateTimeString = function(date, isOnlyDate){
    var dateStr = date.toISOString();
    if (isOnlyDate) {
        dateStr = dateStr.split('T')[0];
    }
    return dateStr
}

// Default input keys behaviour
ConSyst.setInputsAutoCaps = function () {

    // If useAutoCaps, set onchange event to input fields
    if (ConSyst.useAutoCaps) {
        $('input[type="text"], textarea')
            .not('.allow-lower-case')
            // remove any existing autoCaps handler…
            .off('change.autoCaps')
            // …then attach exactly one
            .on('change.autoCaps', function () {
                this.value = this.value.toUpperCase();
            });
    }


}

// Default input keys behaviour
ConSyst.setInputsKeys = function (selector = null) {


    // Skip readonly inputs with tab key
    $('input[readonly]').not(".select-input").attr('tabindex', '-1');
    if (selector == null) {
        selector = 'input:not(:disabled):not(:hidden):not([readonly]):not([type="hidden"]):not(.wysiwyg input), input.select-input:not([type="hidden"]):not([disabled]), textarea:not(:disabled):not([readonly]):not(.wysiwyg-textarea), button:visible:not(:disabled):not(.wysiwyg button):not(.trailing button), a.btn:visible:not(:disabled), .wysiwyg-content, .modal input:not(:disabled):not([readonly]):not([type="hidden"]):not(.wysiwyg input)'
    }
    // Get all the input fields that are not readonly
    ConSyst.inputs = $(selector);
    // Replace previous keydowns on this same function for new one
    ConSyst.inputs.off('keydown', ConSyst.keyDownFunction);
    // Add a keydown event handler to each input field
    ConSyst.inputs.keydown(ConSyst.keyDownFunction);


}

// Function to toggle scrollbar 
ConSyst.toggleScrollbar = function () {
    var style = document.getElementById('hideScrollbarCss');
    if (style) {
        // Show scrollbar
        style.remove();
    }
    else {
        // Hide scrollbar
        var style = document.createElement('style');
        style.id = 'hideScrollbarCss';
        style.innerHTML = `::-webkit-scrollbar {display: none;}`;
        document.head.appendChild(style);
    }
}

ConSyst.getQueryParams = function () {
    const params = {};
    const queryParams = new URLSearchParams(window.location.search);

    queryParams.forEach((value, key) => {
        // Split comma-separated lists into arrays
        const values = value.includes(',')
            ? value.split(',').map(v => v.trim()).filter(Boolean)
            : [value];
    
        if (params[key])
        {
            if (Array.isArray(params[key]))
            {
                params[key].push(...values);
            }
            else
            {
                params[key] = [params[key], ...values];
            }
        }
        else
        {
            params[key] = values.length > 1 ? values : values[0];
        }
    });

    return params;
};
    
ConSyst.openOptions = function (btn) {
    ConSyst.toggleScrollbar();
    var sideNavJq = $("#sidenav-right");
    const sidenav = mdb.Sidenav.getInstance(sideNavJq[0]);
    sidenav.toggle();
    ConSyst.toggleScrollbar();
}

ConSyst.showDeleteModal = function (id = null, code = null, extras = null) {
    var deleteActionName = ConSyst.deleteActionName ?? "Delete";
    var idPath = id != null ? `/${id}` : "";
    var codePath = code != null ? `?code=${code}` : "";
    if (extras != null) {
        const queryParams = $.param(extras, true);
        if (codePath.length > 0) {
            codePath += "&";
        }
        else {
            codePath += "?";
        }
        codePath += `${queryParams}`;
    }
    $('#deleteForm').attr('action', `/${ConSyst.controllerName}/${deleteActionName}${idPath}${codePath}`);
    $('#deleteIdLabel').text(id ?? code ?? Object.values(extras)[0]);
    var modalJq = $('#deleteModal');
    var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
    modal.show();
}

/// ConSyst.showcompletedModal
ConSyst.showcompletedModal = function (id = null, code = null, extras = null) {
    var completedActionName = ConSyst.completedActionName ?? "completed";
    var idPath = id != null ? `/${id}` : "";
    var codePath = code != null ? `?code=${code}` : "";
    if (extras != null) {
        const queryParams = $.param(extras, true);
        if (codePath.length > 0) {
            codePath += "&";
        }
        else {
            codePath += "?";
        }
        codePath += `${queryParams}`;
    }
    $('#completedForm').attr('action', `/${ConSyst.controllerName}/${completedActionName}${idPath}${codePath}`);
    $('#completedIdLabel').text(id ?? code ?? Object.values(extras)[0]);
    var modalJq = $('#completedModal');
    var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
    modal.show();
}



ConSyst.showCancelModal = function (id = null, code = null, extras = null) {
    var cancelActionName = ConSyst.cancelActionName ?? "Cancel";
    var idPath = id != null ? `/${id}` : "";
    var codePath = code != null ? `?code=${code}` : "";
    if (extras != null) {
        const queryParams = $.param(extras, true);
        if (codePath.length > 0) {
            codePath += "&";
        }
        else {
            codePath += "?";
        }
        codePath += `${queryParams}`;
    }
    $('#cancelForm').attr('action', `/${ConSyst.controllerName}/${cancelActionName}${idPath}${codePath}`);
    $('#cancelIdLabel').text(id ?? code ?? Object.values(extras)[0]);
    var modalJq = $('#cancelModal');
    var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
    modal.show();
}

ConSyst.loadStandardCodeAutocomplete = function (div, type, codeInput, nameInput, codes) {
    const autocompleteDivJq = $(div);
    const codesFilterFunction = (value) => {
        return codes.filter((item) => {
            return (type==null || item.CType==type) && (item.SDescription.toLowerCase().includes(value.toLowerCase()) || item.SId.toString().toLowerCase().includes(value.toLowerCase()) || item.SAltCode.toLowerCase().includes(value.toLowerCase()));
        });
    };
    new mdb.Autocomplete(autocompleteDivJq[0], {
        filter: codesFilterFunction,
        displayValue: (value) => value.SCode,
        autoSelect: true,
        itemContent: (value) => {
            return `
                <div class="autocomplete-custom-item-content">
                    <span><b>${value.SId}</b> - ${value.SDescription} - ${value.SAltCode}</span> 
                </div>`;
        },
    });
    var codeInputJq = $(codeInput);
    autocompleteDivJq.on('itemSelect.mdb.autocomplete', (event) => {
        codeInputJq.val(event.value.SCode);
        ConSyst.changeStandardCode(codeInput, nameInput, codes, event.value.SId, event.value.SDescription);
    });
    codeInputJq.on('change', (event) => {
        ConSyst.changeStandardCode(codeInput, nameInput, codes, null, null);
    });
}

ConSyst.changeStandardCode = function (codeInput, nameInput, codes, code = null, name = null) {
    var codeInputJq = $(codeInput);
    var codeObj = codes.find((c) => c.SId == codeInputJq.val()?.toUpperCase());
    var codeName = "";
    var codeId = "";
    if (codeObj == null) {
        if (name == null) {
            codeName = "";
        }
        else {
            codeName = name;
        }
        if (code == null) {
            codeId = "";
        }
        else {
            codeId = code;
        }
    }
    else {
        codeName = codeObj.SDescription;
        codeId = codeObj.SId;
    }
    var nameInputJq = $(nameInput);
    nameInputJq.val(codeName);
    nameInputJq.addClass("active");
    codeInputJq.val(codeId);
    codeInputJq.addClass("active");
}

ConSyst.loadCustomerAutocomplete = function (
    div,
    codeInput,
    nameInput,
    idInput,
    gradeSelect,
    labelsSelect,
    customers,
    emptyString = "N/A",
    subCustomerSelect = null,
    relationsSelect = null,
    isLessee = false,
    poolsSelect = null,
    unitTypeInput = null) {
    const autocompleteDivJq = $(div);
    const customerFilterFunction = (value) => {
        var localCusts = customers;
        if (localCusts == null) {
            localCusts = isLessee ? ConSyst.lessees : ConSyst.customers;
        }
        return localCusts.filter((item) => {
            return (item.SFullName.toLowerCase().includes(value.toLowerCase()) || item.IId.toString().toLowerCase().includes(value.toLowerCase()) || item.SCode.toLowerCase().includes(value.toLowerCase()));
        });
    };

    var existAutocomplete = mdb.Autocomplete.getInstance(autocompleteDivJq[0]);
    if (existAutocomplete != null) {
        existAutocomplete.dispose();
    }

    new mdb.Autocomplete(autocompleteDivJq[0], {
        filter: customerFilterFunction,
        displayValue: (value) => value.SCode,
        autoSelect: true,
        itemContent: (value) => {
            return `
                <div class="autocomplete-custom-item-content">
                    <span><b>${value.SCode}</b> - ${value.SFullName} - ${value.IId}</span> 
                </div>`;
        },
    });

    var codeInputJq = $(codeInput);
    autocompleteDivJq.on('itemSelect.mdb.autocomplete', (event) => {
        codeInputJq.val(event.value.SCode);
        ConSyst.changeCustomer(codeInput, nameInput, customers, idInput, gradeSelect, labelsSelect, event.value.SCode, event.value.SFullName, emptyString, subCustomerSelect, relationsSelect, isLessee, poolsSelect, unitTypeInput);
    });

    codeInputJq.on('change', (event) => {
        ConSyst.changeCustomer(codeInput, nameInput, customers, idInput, gradeSelect, labelsSelect, null, null, emptyString, subCustomerSelect, relationsSelect, isLessee, poolsSelect, unitTypeInput);
    });
};

ConSyst.loadLesseeAutocomplete = function (div, codeInput, nameInput, idInput, lessees) {
    ConSyst.loadCustomerAutocomplete(div, codeInput, nameInput, idInput, null, null, lessees,null,null,null,true)
}

ConSyst.changeCustomer = function (
    codeInput,
    nameInput,
    customers = null,
    idInput = null,
    customerGradeSelect = null,
    customerLabelsSelect = null,
    code = null,
    name = null,
    emptyItemStr = "N/A",
    subCustomerSelect = null,
    relationsSelect = null,
    isLessee = false,
    poolsSelect = null,
    unitTypeInput = null
) {
    var codeInputJq = $(codeInput);
    var inputVal = codeInputJq.val()?.toUpperCase() || "";

    // If the user has not entered anything, clear and exit
    if (inputVal.trim() === "") {
        $(nameInput).val("").removeClass("active");
        codeInputJq.val("").removeClass("active");
        if (idInput != null) {
            $(idInput).val("").trigger("change");
        }
        return;
    }

    if (customers == null) {
        customers = isLessee ? ConSyst.lessees : ConSyst.customers;
    }


    // Filter clients by exact code match first
    var filteredCustomers = customers.filter((c) =>
        (c.SCode && c.SCode.toUpperCase() == inputVal)
    );

    if (filteredCustomers.length == 0) {
        // Filter clients by code, id or name (case insensitive)
        filteredCustomers = customers.filter((c) =>
            (c.SCode && c.SCode.toUpperCase().includes(inputVal)) ||
            (c.IId && c.IId.toString().toUpperCase().includes(inputVal)) ||
            (c.SFullName && c.SFullName.toUpperCase().includes(inputVal))
        );
    }

    // If there are matches, select the first one
    var customer = null;
    if (filteredCustomers.length > 0) {
        customer = filteredCustomers[0];
    }

    var customerId = null;
    var customerName = "";
    var customerCode = "";

    if (customer == null) {
        customerId = parseInt(codeInputJq.val());
        if (!isNaN(customerId)) {
            customer = customers.find((c) => c.IId == customerId);
            customerName = customer ? customer.SFullName : (name || "");
            customerCode = customer ? customer.SCode : (code || "");
        } else {
            customerId = null;
            customerCode = "";
            customerName = "";
        }
    } else {
        customerId = customer.IId;
        customerName = customer.SFullName;
        customerCode = customer.SCode;
    }

    var nameInputJq = $(nameInput);
    nameInputJq.val(customerName);
    nameInputJq.addClass("active");
    codeInputJq.val(customerCode);
    codeInputJq.addClass("active");

    if (idInput != null) {
        $(idInput).val(customerId);
        $(idInput).trigger("change");
    }

    var grades = [];
    var custLabels = [];
    var subCust = [];
    if (customerGradeSelect != null) {
        // Get the unit type from the input field
        var unitType = unitTypeInput ? $(unitTypeInput).val() : null;
        $.getJSON('/Customer/GetGrades', { customerId: customerId, unitType: unitType }, function (gradesObj) {
            grades = gradesObj.LGrades;
            var areGradesCustomer = gradesObj.BAreGradesCustomer;
            if (grades.length > 0) {
                ConSyst.customerGrades = grades;
                ConSyst.areGradesCustomer = areGradesCustomer;
            }
            $("#BIsGradeCustomer").val(areGradesCustomer);
            var gradeSelectJq = $(customerGradeSelect);
            $.each(gradeSelectJq, function (selIdx, select) {
                var oldVal = $(select).val();
                if (oldVal == "" || oldVal == null || oldVal == undefined) {
                    oldVal = $(select).data("cst-old-value");
                }
                $(select).empty();
                var gradesToAdd = grades;
                if ($(select).hasClass("customer-criteria")) {
                    gradesToAdd = grades.filter((gr) => gr.BIsCriteria == true);
                }
                if (emptyItemStr != null && $(select).attr('multiple') == undefined) {
                    $(select).append($('<option/>', {
                        value: '',
                        text: emptyItemStr
                    }));
                }

                if ($(select).prop("multiple"))
                {
                    $.each(gradesToAdd, function (gradeIdx, grade) {
                        $(select).append($('<option/>', {
                            value: grade.SCode,
                            text: grade.SName,
                            selected: oldVal != null && oldVal.includes(grade.SCode)
                        }));
                    });
                }
                else
                {
                    $.each(gradesToAdd, function (gradeIdx, grade) {
                        $(select).append($('<option/>', {
                            value: grade.SCode,
                            text: grade.SName,
                            selected: oldVal == grade.SCode
                        }));
                    });
                }

            });
        });
    }
    if (customerLabelsSelect != null) {
        $.getJSON('/Customer/GetLabels', { customerId: customerId, includeMeta: true }, function (result) {
            // Support both the old array and the new extended payload
            const labels = Array.isArray(result) ? result : (result.LLabels || []);

            // Keep legacy cache
            if (labels.length > 0) {
                ConSyst.customerLabels = labels;
            }

            // Populate the select(s)
            var labelSelectsJq = $(customerLabelsSelect);
            $.each(labelSelectsJq, function (index, select) {
                $(select).empty();
                $.each(labels, function (i, label) {
                    $(select).append($('<option/>', {
                        value: label.SCode,
                        text: label.SName
                    }));
                });
            });

            // If extended payload, hydrate globals so _CustomerLabelsControl.jsx can build inputs
            if (!Array.isArray(result)) {
                window.customerLabelsFull = result.customerLabelsFull || [];
                window.possibleFieldTypes = result.possibleFieldTypes || [];
            }
            /// onclick in the customerLabelsSelect to refresh the _CustomerLabelsControl.jsx
            $(customerLabelsSelect).trigger('change');

        });
    }
    if (subCustomerSelect != null) {
        $.getJSON('/Customer/GetSubCustomers', { customerId: customerId }, function (subCustomers) {
            subCust = subCustomers;
            if (subCust.length > 0) {
                ConSyst.subCustomers = subCust;
            }
            var subCustomerSelectJq = $(subCustomerSelect);
            $.each(subCustomerSelectJq, function (index, select) {
                $(select).empty();
                if (emptyItemStr != null && $(select).attr('multiple') == undefined) {
                    $(select).append($('<option/>', {
                        value: '',
                        text: emptyItemStr
                    }));
                }
                $.each(subCust, function (index, subCustomer) {
                    $(select).append($('<option/>', {
                        value: subCustomer.ICode,
                        text: subCustomer.SName
                    }));
                });
            });
        });
    }
    if (relationsSelect != null) {
        $.getJSON('/Customer/GetRelations', { customerId: customerId }, function (relations) {
            ConSyst.lessees = relations;
            var relationsSelectJq = $(relationsSelect);
            if (relationsSelectJq.is("select")) {
                relationsSelectJq.empty();
                if (emptyItemStr != null && relationsSelectJq.attr('multiple') === undefined) {
                    relationsSelectJq.append($('<option/>', {
                        value: '',
                        text: emptyItemStr
                    }));
                }
                $.each(relations, function (index, relation) {
                    relationsSelectJq.append($('<option/>', {
                        value: relation.ICode,
                        text: relation.SName
                    }));
                });
            } else {
                ConSyst.refreshAutocompleteSearch(relationsSelectJq.parent()[0]);
            }
        });
    }
    if (poolsSelect != null) {
        $.getJSON('/Customer/GetPools', { customerId: customerId }, function (pools) {
            ConSyst.pools = pools;
            var poolsSelectJq = $(poolsSelect);
            if (poolsSelectJq.is("select")) {
                poolsSelectJq.empty();
                if (emptyItemStr != null && poolsSelectJq.attr('multiple') === undefined) {
                    poolsSelectJq.append($('<option/>', {
                        value: '',
                        text: emptyItemStr
                    }));
                }
                $.each(pools, function (index, pool) {
                    poolsSelectJq.append($('<option/>', {
                        value: pool.ICode,
                        text: pool.SName
                    }));
                });
            } else {
                ConSyst.refreshAutocompleteSearch(poolsSelectJq.parent()[0]);
            }
        });
    }
};


ConSyst.refreshAutocompleteSearch = function (element) {
    const autocompleteInstance = mdb.Autocomplete.getInstance(element);
    if (!autocompleteInstance) return;
    var autocompleteVal = $(element).find('input').val();
    autocompleteInstance.filter(autocompleteVal);
}


ConSyst.changeLessee = function (codeInput, nameInput, lessees, idInput = null, code = null, name = null) {
    ConSyst.changeCustomer(codeInput, nameInput, lessees, idInput, null, null, code, name, null, null, null, true)
}

ConSyst.changeCountry = function (countryCodeSelect, stateCodeSelect, countyCodeSelect, districtCodeSelect, emptyItem = null) {
    var svSelectedCountry = $(countryCodeSelect).val();
    if (svSelectedCountry != null && svSelectedCountry != '') {
        $.getJSON('/Country/GetStates', { countryCode: svSelectedCountry }, function (lpStates) {
            var ovStateSelect = $(stateCodeSelect);
            ovStateSelect.empty();
            if (emptyItem != null) {
                ovStateSelect.append($('<option/>', {
                    value: 0,
                    text: emptyItem
                }));
            }
            $.each(lpStates, function (index, opState) {
                ovStateSelect.append($('<option/>', {
                    value: opState.ICode,
                    text: opState.SName
                }));
            });
            ovStateSelect[0].selectedIndex = 0;
            var svSelectedState = ovStateSelect.val();
            if (lpStates.length == 0) {
                ovStateSelect.siblings('.form-outline').find('.form-label').text("");
                if (countyCodeSelect != null) {
                    var ovCountySelect = $(countyCodeSelect);
                    ovCountySelect.empty();
                    if (emptyItem != null) {
                        ovCountySelect.append($('<option/>', {
                            value: 0,
                            text: emptyItem
                        }));
                    }
                    ovCountySelect.siblings('.form-outline').find('.form-label').text("");
                    if (districtCodeSelect != null) {
                        var ovDistrictSelect = $(districtCodeSelect);
                        ovDistrictSelect.empty();
                        if (emptyItem != null) {
                            ovDistrictSelect.append($('<option/>', {
                                value: 0,
                                text: emptyItem
                            }));
                        }
                        ovDistrictSelect.siblings('.form-outline').find('.form-label').text("");
                    }
                }
            }
            if (svSelectedState != null && svSelectedState != '' && countyCodeSelect != null) {
                $.getJSON('/Country/GetCounties', { countryCode: svSelectedCountry, stateCode: svSelectedState }, function (lpCounties) {
                    var ovCountySelect = $(countyCodeSelect);
                    ovCountySelect.empty();
                    if (emptyItem != null) {
                        ovCountySelect.append($('<option/>', {
                            value: 0,
                            text: emptyItem
                        }));
                    }
                    $.each(lpCounties, function (index, opCounty) {
                        ovCountySelect.append($('<option/>', {
                            value: opCounty.ICode,
                            text: opCounty.SName
                        }));
                    });
                    ovCountySelect[0].selectedIndex = 0;
                    var svSelectedCounty = ovCountySelect.val();
                    if (districtCodeSelect != null) {
                        $.getJSON('/Country/GetDistricts', { countryCode: svSelectedCountry, stateCode: svSelectedState, cityCode: svSelectedCounty }, function (lpDistricts) {
                            var ovDistrictSelect = $(districtCodeSelect);
                            ovDistrictSelect.empty();
                            if (emptyItem != null) {
                                ovDistrictSelect.append($('<option/>', {
                                    value: 0,
                                    text: emptyItem
                                }));
                            }
                            $.each(lpDistricts, function (index, opDistrict) {
                                ovDistrictSelect.append($('<option/>', {
                                    value: opDistrict.ICode,
                                    text: opDistrict.SName
                                }));
                            });
                            ovDistrictSelect[0].selectedIndex = 0;
                        });
                    }
                });
            }
        });
    }
}

ConSyst.changeState = function (countryCodeSelect, stateCodeSelect, countyCodeSelect, districtCodeSelect, emptyItem=null) {
    var svSelectedCountry = $(countryCodeSelect).val();
    var svSelectedState = $(stateCodeSelect).val();
    if (svSelectedCountry != null && svSelectedCountry != '' && svSelectedState != null && svSelectedState != '') {
        $.getJSON('/Country/GetCounties', { countryCode: svSelectedCountry, stateCode: svSelectedState }, function (lpCounties) {
            var ovCountySelect = $(countyCodeSelect);
            ovCountySelect.empty();
            if (emptyItem != null) {
                ovCountySelect.append($('<option/>', {
                    value: 0,
                    text: emptyItem
                }));
            }
            $.each(lpCounties, function (index, opCounty) {
                ovCountySelect.append($('<option/>', {
                    value: opCounty.ICode,
                    text: opCounty.SName
                }));
            });
            ovCountySelect[0].selectedIndex = 0;
            var svSelectedCounty = ovCountySelect.val();
            if (svSelectedCounty != null && svSelectedCounty != '' && districtCodeSelect!=null) {
                $.getJSON('/Country/GetDistricts', { countryCode: svSelectedCountry, stateCode: svSelectedState, cityCode: svSelectedCounty }, function (lpDistricts) {
                    var ovDistrictSelect = $(districtCodeSelect);
                    ovDistrictSelect.empty();
                    if (emptyItem != null) {
                        ovDistrictSelect.append($('<option/>', {
                            value: 0,
                            text: emptyItem
                        }));
                    }
                    $.each(lpDistricts, function (index, opDistrict) {
                        ovDistrictSelect.append($('<option/>', {
                            value: opDistrict.ICode,
                            text: opDistrict.SName
                        }));
                    });
                    ovDistrictSelect[0].selectedIndex = 0;
                });
            }
        });
    }
}

ConSyst.changeCounty = function (countryCodeSelect, stateCodeSelect, countyCodeSelect, districtCodeSelect, emptyItem = null) {
    var svSelectedCountry = $(countryCodeSelect).val();
    var svSelectedState = $(stateCodeSelect).val();
    var svSelectedCounty = $(countyCodeSelect).val();
    if (svSelectedCountry != null && svSelectedCountry != '' && svSelectedCounty != null && svSelectedCounty != '' && svSelectedState != null && svSelectedState != '') {
        $.getJSON('/Country/GetDistricts', { countryCode: svSelectedCountry, stateCode: svSelectedState, cityCode: svSelectedCounty }, function (districts) {
            var ovDistrictSelect = $(districtCodeSelect);
            ovDistrictSelect.empty();
            if (emptyItem != null) {
                ovDistrictSelect.append($('<option/>', {
                    value: 0,
                    text: emptyItem
                }));
            }
            $.each(districts, function (index, opDistrict) {
                ovDistrictSelect.append($('<option/>', {
                    value: opDistrict.ICode,
                    text: opDistrict.SName
                }));
            });
            ovDistrictSelect[0].selectedIndex = 0;
        });
    }
}

ConSyst.serviceResults = {
    Success: 83,
    Error: 69,
    Warning: 87,
    Info: 73
};

ConSyst.callService = function (urlOrController, method, actionName = null, data = null, headers = {}, contentType="json") {
    // Construct URL if controller and action name are provided
    var url = actionName ? `/${urlOrController}/${actionName}` : urlOrController;

    if (method === 'GET' && data) {
        const queryParams = $.param(data,true);
        url += `?${queryParams}`;
    }

    return new Promise((resolve, reject) => {
        $.ajax({
            url: url,
            type: method,
            data: method === 'GET' ? null : JSON.stringify(data),
            headers: headers,
            contentType: `application/${contentType}; charset=utf-8`,
            //dataType: contentType,
            success: function (response) {

                resolve(response);
            },
            error: function (xhr, status, error) {
                var errorContent = xhr.responseText;
                if (errorContent != null && errorContent.trim() != "") {
                    if (contentType == 'json') {
                        var sysResult = JSON.parse(errorContent);
                        if (sysResult != null) {
                            ConSyst.showMessage(sysResult.SName, "danger");
                        }
                    }
                }
                reject(new Error(`Status: ${status}, Error: ${error}, Response: ${xhr.responseText}`));

            }
        });
    });
}

ConSyst.waitModalTime = 500; // Wait half second for modal animation to finish before opening a new one.

ConSyst.showResultModal = async function (title = null, message = null, color = null) {
    var modalJq = $('#genericWaitResultModal');
    modalJq.find('.modal-footer button').attr("disabled",true);
    var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
    if (modal._isShown || modal._isTransitioning) {
        await ConSyst.sleep(ConSyst.waitModalTime);
    }
    if (title != null) {
        modalJq.find('.modal-title-txt').text(title);
    }
    if (message != null) {
        modalJq.find('.modal-message').html(`<span class="spinner-grow spinner-grow-sm" role="status"></span> ${message}`);
    }
    if (color != null) {
        modalJq.find('.modal-header').removeClass("bg-success").removeClass("bg-warning").removeClass("bg-danger").removeClass("bg-primary").removeClass("bg-secondary").addClass(`bg-${color}`);
        modalJq.find('.modal-footer button').removeClass("bg-success").removeClass("bg-warning").removeClass("bg-danger").removeClass("bg-primary").removeClass("bg-secondary").addClass(`bg-${color}`);
    }
    //Ensure that the loading modal appears on top of all other modals.
    setTimeout(() => {
        ConSyst.ensureModalOnTop(modalJq[0]);
    }, 10);

    modal.show();
    return [modalJq, modal];
}

ConSyst.waitResultModal = async function (action, title = null, message = null, color = null) {
    const [modalJq, modal] = await ConSyst.showResultModal(title, message, color);

    //Ensure correct z-index after displaying the modal
    setTimeout(() => {
        ConSyst.ensureModalOnTop(modalJq[0]);
    }, 50);
    try {
        const result = await Promise.resolve(action());

        if (modal._isTransitioning) {
            await ConSyst.sleep(100);
        }
        modalJq.data("should-hide", true);
        modal.hide();
        return result;
    } catch (error) {

        if (modal._isTransitioning) {
            await ConSyst.sleep(100);
        }
        modalJq.data("should-hide", true);
        modal.hide();
        throw error;
    }
}

ConSyst.defaultColorForIcon = { "success": "fa-check-circle", "danger": "fa-times-circle", "warning": "fa-exclamation-triangle", "info": "fa-info-circle", "primary": "fa-chevron-circle-right", "secondary": "fa-chevron-circle-right" }

ConSyst.showConfirmModal = async function (title, message, color, icon=null, confirmBtnMsg="Confirm", cancelBtnMsg="Cancel") {
    if (icon == null) {
        icon = ConSyst.defaultColorForIcon[color]
    }
    return new Promise(async (resolve, reject) => {
        try {
            var modalJq = $('#genericConfirmationModal');
            var modal = mdb.Modal.getOrCreateInstance(modalJq[0]);
            if (modal._isShown || modal._isTransitioning) {
                await ConSyst.sleep(ConSyst.waitModalTime);
            }
            modalJq.find('.modal-title-txt').text(title);
            modalJq.find('.modal-title-ico').removeClass().addClass(`modal-title-ico icon fas ${icon}`);
            modalJq.find('.modal-message').html(message);
            modalJq.find('.modal-header').removeClass('bg-success').removeClass('bg-warning').removeClass('bg-danger').removeClass('bg-primary').removeClass('bg-secondary').addClass(`bg-${color}`);
            var confirmIconJq = modalJq.find('.btn-confirm .icon');
            confirmIconJq.removeClass().addClass(`icon fas ${icon}`);
            modalJq.find('.btn-confirm').removeClass('bg-success').removeClass('bg-warning').removeClass('bg-danger').removeClass('bg-primary').removeClass('bg-secondary').addClass(`bg-${color}`).text(` ${ConSyst.localizeText(confirmBtnMsg)}`).prepend(confirmIconJq);

            modalJq.find('.btn-confirm').removeAttr("onclick");
            modalJq.find('.btn-confirm').off('click');
            modalJq.find('.btn-confirm').on('click', function () {
                resolve(1);
                modal.hide();
            });

            var cancelIconJq = modalJq.find('.btn-cancel .icon');
            modalJq.find('.btn-cancel').removeAttr("onclick");
            modalJq.find('.btn-cancel').off('click').text(` ${ConSyst.localizeText(cancelBtnMsg)}`).prepend(cancelIconJq);
            modalJq.find('.btn-cancel').on('click', function () {
                resolve(0);
                modal.hide();
            });
            modalJq.find('.btn-close').on('click', function () {
                resolve(-1);
                modal.hide();
            });

            // Show the modal
            modal.show();

            // After a brief delay, ensure modal is on top level
            // (useful when there are multiple modals or elements with high z-index)
            setTimeout(() => {
                ConSyst.ensureModalOnTop(modalJq[0]);
            }, 10);

        } catch (error) {
            reject(new Error(`Error: ${error.message}`));
        }
    });
}

/** ===== NEW HELPER FUNCTION FOR Z-INDEX MANAGEMENT =====
 * Ensures a modal is displayed on top of all other modals by managing z-index values
 * This function finds the highest z-index among currently open modals and sets
 * the target modal to a higher z-index to guarantee it appears on top
 * @param {HTMLElement} modalElement - The modal DOM element to bring to front
 * @returns {number} The new z-index value assigned to the modal
 */
ConSyst.ensureModalOnTop = function (modalElement) {
    // Convert DOM element to jQuery object for easier manipulation
    var $modal = $(modalElement);

    // Set initial baseline z-index (Bootstrap's default modal z-index is 1050)
    var highestZIndex = 1050;

    // Find the highest z-index among currently open modals
    // Iterate through all modals that have the 'show' class (currently visible)
    $('.modal.show').each(function () {
        // Get the current modal's z-index, default to 1050 if not set or invalid
        var currentZIndex = parseInt($(this).css('z-index')) || 1050;

        // Update highest z-index if current modal has a higher value
        if (currentZIndex > highestZIndex) {
            highestZIndex = currentZIndex;
        }
    });

    // Calculate new z-index with sufficient gap to avoid conflicts
    // Adding 20 ensures clear separation from other modals
    var newZIndex = highestZIndex + 20;

    // Apply the new z-index to the target modal
    $modal.css('z-index', newZIndex);

    // Handle modal backdrop z-index adjustment
    // The backdrop should be below the modal but above other elements
    var modal = mdb.Modal.getInstance(modalElement);
    if (modal && modal._backdrop && modal._backdrop._element) {
        // Set backdrop z-index to 10 units below the modal
        // This ensures proper layering: backdrop behind modal, but above other content
        $(modal._backdrop._element).css('z-index', newZIndex - 10);
    }

    // Log the operation for debugging purposes
    // Helps track which modal was moved and to what z-index value
    console.log(`Modal ${$modal.attr('id')} z-index set to: ${newZIndex}`);

    // Return the assigned z-index value for potential use by calling code
    return newZIndex;
};

/* ===== OPTIONAL DEBUGGING FUNCTION =====
 * Debug utility function that logs detailed information about all modals in the DOM
 * This function helps developers troubleshoot modal layering issues by providing
 * a complete overview of all modal states, z-index values, and visibility status
 * Useful for debugging complex modal interactions and z-index conflicts
 */
ConSyst.debugModalStack = function () {
    // Print debug session header for easy identification in console
    console.log('=== Start Modal Stack Debug ===');

    // Iterate through ALL modal elements in the DOM (not just visible ones)
    $('.modal').each(function () {
        // Get jQuery reference to current modal for easier property access
        var $modal = $(this);

        // Extract modal identification - ID attribute for unique identification
        var id = $modal.attr('id');

        // Get current z-index CSS property value
        // This shows the actual computed z-index, whether set by CSS or JavaScript
        var zIndex = $modal.css('z-index');

        // Check visibility status by looking for Bootstrap's 'show' class
        // 'show' class indicates the modal is currently displayed to the user
        var isShown = $modal.hasClass('show');

        // Log comprehensive modal information in a readable format
        // Format: Modal ID, current z-index, and visibility status
        console.log(`Modal: ${id}, Z-Index: ${zIndex}, Shown: ${isShown}`);
    });

    // Print debug session footer to clearly mark the end of the output
    console.log('=== End Modal Stack Debug ===');
};

ConSyst.showMessageHtml = function (alertHtml) {
    alertJq = $(alertHtml);
    alertHtml = alertJq.prop('outerHTML');
    alertJq.remove();
    messagesDiv = $("#messagesDiv");
    messagesDiv.append(alertHtml);
    alertId = alertJq.attr('id');
    animation = mdb.Animate.getInstance(document.getElementById(alertId));
    if (animation != null) {
        animation.stopAnimation();
        animation.dispose();
    }
    animation = mdb.Animate.getOrCreateInstance(document.getElementById(alertId));
    animation.changeAnimationType('fade-in-right');
    animation.startAnimation();
    olAlert = new mdb.Alert(document.getElementById(alertId), {position:"top-right"});
    olAlert.show();
}

ConSyst.dismissAlert = async function (btn, sleep = null) {
    if (sleep != null) {
        await ConSyst.sleep(sleep);
    }
    $(btn).trigger("click");
}

ConSyst.dismissAlertAnimation = async function (btn) {
    var alertElem = document.querySelector($(btn).attr('data-mdb-animation-target'))
    if (alertElem != null) {
        var alert = mdb.Alert.getOrCreateInstance(alertElem);
        alert.hide();
        var animation = mdb.Animate.getInstance(alertElem);
        if (animation != null) {
            animation.stopAnimation();
            animation.dispose();
        }
        animation = mdb.Animate.getOrCreateInstance(alertElem);
        animation.changeAnimationType('fade-out-right');
        animation.startAnimation();
    }
}

ConSyst.showMessage = function (message, color, icon = null, alertId = null, inputName=null, formSel=null, position=null) {
    if (typeof message === 'string') {
        text = message;
    }
    else {
        text = message.SMessage;
        color = message.SColor;
        icon = message.SIcon;
        alertId = `alert-js-${message.IId}`
    }
    if (position == null) {
        position = "bottom";
    }
    alertId = alertId ?? 'alert-default';
    color = color ?? 'primary';
    $(`#${alertId} button`).trigger("click");
    $(`#${alertId}`).remove();
    messagesDiv = $("#messagesDiv");
    //Do something
    var messageHtml = `
        <div class="alert alert-dismissible fade" id="${alertId}" role="alert"
             data-mdb-color="${color}"
             data-mdb-position="${position}-right"
             data-mdb-stacking="true"
             data-mdb-append-to-body="true"
             data-mdb-hidden="true"
             data-mdb-autohide="${(color!="danger")?"true":"false"}"
             data-mdb-animation="fade-in-right"
             data-mdb-animation-start="onLoad">

            <i class="fas ${icon} me-3"></i> <span></span>
            <button type="button" class="btn-close" onclick="ConSyst.dismissAlertAnimation(this);" data-mdb-dismiss="alert" aria-label="Close" data-mdb-animation-target="#${alertId}"></button>
        </div>
        `;
    messagesDiv.append(messageHtml);
    animation = mdb.Animate.getInstance(document.getElementById(alertId));
    if (animation != null) {
        animation.stopAnimation();
        animation.dispose();
    }
    animation = mdb.Animate.getOrCreateInstance(document.getElementById(alertId));
    animation.changeAnimationType('fade-in-right');
    animation.startAnimation();
    var alert = new mdb.Alert(document.getElementById(alertId));
    alert.update({
        color: color
    });
    $(`#${alertId} i`).remove();
    $(`#${alertId} span`).remove();
    if (icon == null) {
        icon = ConSyst.defaultColorForIcon[color];
    }
    $(`#${alertId}`).prepend(`<i class="fas ${icon} me-3"></i> <span>${text}</span>`);
    alert.show();
    if (inputName != null) {
        var errors = {};
        errors[inputName] = text;
        if (formSel == null) {
            formSel = "#form";
        }
        var formJq = $(formSel);
        var inputJq = formJq.find(`input[name="${inputName}"]`)
        if (inputJq.length>0) {
            formJq.validate().showErrors(errors);
            $(`#${inputName}`).trigger("focus");
        }
    }
    if (color == "success" || color=="info") {
        
        ConSyst.dismissAlert($(`#${alertId} button`)[0],5000);
    }
}

ConSyst.showMessages = function (messages, inputName = null) {
    // If an inputName is provided, find the first error message to associate with it.
    let firstError = null;
    if (inputName && messages?.length > 0) {
        // Find the first message of type 'Error'.
        firstError = messages.find(m => m.EType === ConSyst.serviceResults.Error);
    }

    // Display all messages as toasts.
    for (var i = 0; i < messages.length; i++) {
        const message = messages[i];
        let currentInputName = null;

        // If we found an error, only associate the first one with the input.
        if (firstError) {
            if (message === firstError) {
                currentInputName = inputName;
            }
        } else if (i === 0) {
            // If no errors, associate the first message with the input.
            currentInputName = inputName;
        }

        ConSyst.showMessage(message, null, null, null, currentInputName);
    }
}
ConSyst.validateUnitNumber = async function (unitInputOrId, unitType, customerId, eventType = null, eventKey = null, otherUnits = null, preclearCode = null, showWaitModal = true, returnMessages = null, isUnit = false, isChassis = false, isGenset = false, dateTime=null, hasPools = null, grades = null) {
    var unitInputJq = $(unitInputOrId);
    var unitInputName = unitInputJq.attr("name");
    var unitId = unitInputJq?.val()
    if (unitInputJq.length == 0) {
        unitId = unitInputOrId; //Is id if can't find input
    }
    unitId = unitId.toUpperCase().replaceAll("_", "").replaceAll(" ", "");
    if ((unitId.length >= 2) && (unitId.length <= 11) || (!isNaN(unitId.charAt(0)) && (unitId.length >= 1) && (unitId.length <= 11))) {
        var isUnitValidCode = 0; //0 is invalid, 1 is valid, 10 is new and invalid, 11 is new and valid and any negative is error
        try {
            data = {
                unitId: unitId,
                unitType: unitType,
                customerId: customerId,
                eventType: eventType != null ? eventType[0] : null,
                eventKey: eventKey,
                preclearCode: preclearCode,
                isUnit: isUnit,
                isChassis: isChassis,
                isGenset: isGenset,
                tDocTime: dateTime,
                hasPools: hasPools,
            }
            //Assign lists only if valid ones are present
            if (otherUnits != null) {
                data.otherUnits = otherUnits;
            }
            if (grades != null) {
                data.grades = grades;
            }

            var result = {};
            if (showWaitModal) {
                result = await ConSyst.waitResultModal(() => {
                    return ConSyst.callService("Unit", "GET", "GetUnitValidation", data);
                }, ConSyst.localizeText("Validating..."), ConSyst.localizeText("Please wait while your unit gets validated..."));
            }
            else {
                result = await ConSyst.callService("Unit", "GET", "GetUnitValidation", data);
            }
            var messages = JSON.parse(result.SValue);
            switch (result.EType) {
                case ConSyst.serviceResults.Success:
                case ConSyst.serviceResults.Info: // Success or info only show every message received adn return true
                    if (returnMessages == null) {
                        ConSyst.showMessages(messages);
                    }
                    else {
                        returnMessages.push(...messages);
                    }
                    isUnitValidCode = 1;
                    break;
                case ConSyst.serviceResults.Error: // Error only show whatever message received and return false
                    if (result.SCode < 0) {
                        ConSyst.showMessage(result.SName, "danger", null, null, unitInputName);
                        isUnitValidCode = result.SCode;
                    }
                    else {
                        if (returnMessages == null) { // Show messages, not returning them
                            //Get only error messages
                            var errorMessages = messages.filter((m) => m.EType == ConSyst.serviceResults.Error);
                            ConSyst.showMessages(errorMessages, unitInputName);
                            var otherMessages = messages.filter((m) => m.EType != ConSyst.serviceResults.Error);
                            ConSyst.showMessages(otherMessages);
                        }
                        else { // Want them returned, not shown
                            returnMessages.push(...messages);
                        }
                        isUnitValidCode = 0;
                    }
                    break;
                case ConSyst.serviceResults.Warning:
                default: // Warning is default behaviour of checking each message and asking for confirmation if there's a warning only
                    var infoMessages = []
                    var infoCount = 0;
                    allConfirm = true;
                    for (var i = 0; i < messages.length; i++) {
                        var message = messages[i];
                        if (message.EType == ConSyst.serviceResults.Warning) {
                            var messConfirm = ConSyst.overrideWarnings ? await ConSyst.showConfirmModal(ConSyst.localizeText("Warning"), `${message.SMessage}. ${ConSyst.localizeText("Are you sure you want to use it?")}`, message.SColor, message.SIcon) : 0;
                            if (messConfirm != 1) {
                                if (returnMessages == null) {
                                    ConSyst.showMessage(message, null, null, null, unitInputName);
                                }
                                else {
                                    returnMessages.push(message);
                                }
                            }
                            allConfirm = allConfirm && messConfirm
                        }
                        else {
                            infoCount++;
                            message.IId = infoCount;
                            infoMessages.push(message);
                        }
                    }
                    if (allConfirm) { // Show info messages for reference if confirmed warnings
                        if (returnMessages == null) {
                            ConSyst.showMessages(infoMessages);
                        }
                        else {
                            returnMessages.push(...infoMessages);
                        }
                        isUnitValidCode = 1;
                    }

            }
            if (isUnitValidCode) {
                unitId = result.SName;
                if (unitInputJq.length > 0) {
                    // Validation went ok
                    unitInputJq.val(unitId);
                    unitInputJq.attr("value", unitId);
                    var inputMask = unitInputJq.attr("data-mdb-input-mask");
                    if (inputMask != null && inputMask != "") {
                        ConSyst.loadInputMask(unitInputOrId, unitId);
                    }
                    unitInputJq.data("cst-validated", true);
                    unitInputJq.attr("data-cst-validated", true);
                    $(`#${unitInputJq.attr("id")}-error`).remove();
                }
            }
            if (result.SCode >= 0) { //is new unit
                //10-19 are for new units, 20-29 are for re entries
                isUnitValidCode += (10 * result.SCode);
            }
            return isUnitValidCode;
        } catch (error) {
            console.error("Error calling service:", error);
            return -1;
        }
        //if (unitId.length != 11) {
        //    var confirm = await ConSyst.showConfirmModal(ConSyst.localizeText("Warning"),ConSyst.localizeText("Unit number is not 11 characters long, check digit can't be validated. Are you sure you want to use it?"), "danger","fa-triangle-exclamation" );
        //}
    }
    else {
        if (isNaN(unitId.charAt(0))) {
            ConSyst.showMessage(ConSyst.localizeText(`Unit ${unitId} must be 2-11 characters (with prefix)`), "danger", null, null, unitInputName);
        }
        else {
            ConSyst.showMessage(ConSyst.localizeText(`Unit ${unitId} must be 1-11 characters (without prefix)`), "danger", null, null, unitInputName);
        }
        return 0;
    }
}

ConSyst.loadSelects = function (selector, values=null) {
    var selectJq = $(selector);
    selectJq.each(function (index, select) {
        var oldValue = null;
        if (values != null) {
            selectId = $(select).attr("id")
            if (selectId in values) {
                sentVal = values[selectId];
            }
            else{
                sentVal = null;
            }
            var oldValue = sentVal;
        }
        if (oldValue == null) {
            oldValue = $(select).val();
        }
        //var parent = $(select).parent();
        //if (parent.hasClass("select-wrapper")) {
        //    $(select).parent().parent().append(select);
        //    parent.remove();
        //}
        ConSyst.setSelectValue(select, oldValue);
    });
}

ConSyst.setSelectValue = function (select, value, triggerOnChange = false, setFocus = false) {
    var selectJq = $(select);
    var select = mdb.Select.getInstance(selectJq[0]);
    var isDisposed = false;
    if (select != null) {
        select.dispose();
        isDisposed = true;
    }
    select = new mdb.Select(selectJq[0]);
    select.setValue(value);
    if (triggerOnChange) {
        selectJq.trigger('change');
    }
    var opts = selectJq.find('option');
    opts.removeProp("selected");
    opts.removeAttr("selected");
    opts.filter(`[value="${value}"]`).prop("selected", "selected");
    opts.filter(`[value="${value}"]`).attr("selected", "selected");
    // Dispose might get it lost on the input key order, need to set it again
    if (isDisposed) {
        ConSyst.setInputsKeys();
    }
    if (setFocus) {
        selectJq.parent().find("input").trigger("focus");
    }
    if (selectJq.hasClass("no-arrow")) {
        selectJq.parent().find(".select-arrow").hide();
    }
    selectJq.attr("value", value);
    //var svSelTxt = $(`${select} option:selected`).text();
    //var ivIptSelect = ovSelect.siblings('.form-outline').find('.form-label.select-fake-value.active');
    //ivIptSelect.text(svSelTxt);
    //var ivIptSelect = ovSelect.siblings('.form-outline').find('.select-input');
    //ivIptSelect.val(svSelTxt);
}

ConSyst.loadUnitIdInputMask = function (selector, unitCategory = null) {
    if (unitCategory == null) {

        // Set input mask depending on combo type (any type for Stacked/Multiple)
        $(selector).attr("data-mdb-custom-validator", "[a-zA-Z ],[aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ ]");
        $(selector).attr("data-mdb-custom-mask", "A,T");
        $(selector).attr("data-mdb-input-mask", "AAAT9999999");
        ConSyst.loadInputMask(selector, null, true);
        $(selector).addClass("active");
    }
    else if (ConSyst.containerCategoryCodes.includes(unitCategory)) {

        // Set input mask depending on combo type (only container units for Standard)
        $(selector).attr("data-mdb-custom-validator", "[a-zA-Z],[uUxXbBoO ]");  
        $(selector).attr("data-mdb-custom-mask", "A,U");
        $(selector).attr("data-mdb-input-mask", "AAAU9999999");
        ConSyst.loadInputMask(selector, null, true);
        $(selector).addClass("active");
    }
    else if (ConSyst.chassisCategoryCodes.includes(unitCategory)) {
      
        // Set input mask depending on combo type (only container units for Standard)
        $(selector).attr("data-mdb-custom-validator", "[a-zA-Z ],[zZcCqQxXtTsSuUfFmMlLjJwWhHaApPeEnN ]");
        $(selector).attr("data-mdb-custom-mask", "A,Z");
        $(selector).attr("data-mdb-input-mask", "AAAZ9999999");
        ConSyst.loadInputMask(selector, null, true);
        $(selector).addClass("active");
    }
    else if (ConSyst.gensetCategoryCodes.includes(unitCategory)) {

        // Set input mask depending on combo type (only container units for Standard)
        $(selector).attr("data-mdb-custom-validator", "[a-zA-Z ],[aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ ]");
        $(selector).attr("data-mdb-custom-mask", "A,J");
        $(selector).attr("data-mdb-input-mask", "AAAJ9999999");
        ConSyst.loadInputMask(selector, null, true);
        $(selector).addClass("active");
    }
}

ConSyst.loadInputMask = function (selector, setValue = null, disposeExisting = false) {
    $(selector).each(function (index, element) {
        var value = setValue;
        if (value == null) {
            value = $(element).val();
        }
        var placeholder = $(element).attr('data-mdb-char-placeholder');
        var maskStr = $(element).attr('data-mdb-input-mask');
        var inputMask = Inputmask.getInstance(element);
        if (inputMask == null) {
            inputMask = new Inputmask(element);
        }
        else {
            if (disposeExisting) {
                inputMask.dispose();
                inputMask = new Inputmask(element);
            }
        }
        if (inputMask != null) {
            inputMask._previousValue = value;
            inputMask._value = value;
            inputMask._initialValue = value;
            inputMask._isEmpty = (value == "") || (value == null);
            inputMask._futureCaretPosition = value.length;
            if (placeholder != null && placeholder != "") {
                inputMask._inputPlaceholder = placeholder.repeat(maskStr.length);
            }
        }

        if (placeholder != null && placeholder != "") {
            //Place cursor on current value or beginning of input if there's placeholder
            $(element).on("focus", async function () {
                await ConSyst.sleep(100);
                var inputJq = $(this);
                var val = inputJq.val();
                inputJq.val(val); // Trigger input re-evaluation by setting its value to itself
                var val = val.replaceAll(placeholder, "");
                this.setSelectionRange(val.length, val.length); // Set cursor position
            });
        }
    });
}

ConSyst.removeUnitIdInputMask = function (selector, { clearValue = false } = {}) {
    $(selector).each(function (_, element) {
        const inst = Inputmask.getInstance?.(element);
        if (inst && typeof inst.dispose === 'function') {
            inst.dispose();
        } else if (typeof Inputmask?.remove === 'function') {
            Inputmask.remove(element);
        }
        element.removeAttribute('data-mdb-input-mask');
        element.removeAttribute('data-mdb-custom-mask');
        element.removeAttribute('data-mdb-custom-validator');
        element.removeAttribute('data-mdb-char-placeholder');
        element.removeAttribute('placeholder');
    });
};

ConSyst.preserveCaret = function (selector) {
    const input = document.querySelector(selector);
    if (!input) return;

    var selectionStartIndex = 0;
    var selectionEndIndex = 0;
    var previousValueLength = input.value.length;
    var lastKeyPressed = "";
    var lastInputType = "";

    // Capture caret + value length before any mutation
    var snapshotState = (e) => {
        lastKeyPressed = e?.key || "";
        selectionStartIndex = input.selectionStart ?? 0;
        selectionEndIndex = input.selectionEnd ?? selectionStartIndex;
        previousValueLength = input.value.length;
    };

    var on = (type, handler) => input.addEventListener(type, handler, true);
    on("beforeinput", (e) => { lastInputType = e.inputType || ""; snapshotState(e); });
    on("keydown", snapshotState);
    on("click", snapshotState);
    on("input", () => {
        var newValueLength = input.value.length;
        var selectedLength = selectionEndIndex - selectionStartIndex;

        var isDeletion =
            lastKeyPressed === "Delete" ||
            lastKeyPressed === "Backspace" ||
            lastInputType?.startsWith("delete");

        var isBackwardDeletion =
            lastKeyPressed === "Backspace" ||
            lastInputType?.endsWith("Backward");

        // How many characters were effectively inserted
        var insertedCount = Math.max(newValueLength - previousValueLength + selectedLength, 0);

        var caretPos = isDeletion
            ? (selectedLength
                ? selectionStartIndex
                : (isBackwardDeletion ? Math.max(selectionStartIndex - 1, 0) : selectionStartIndex))
            : selectionStartIndex + insertedCount;

        requestAnimationFrame(() => { try { input.setSelectionRange(caretPos, caretPos); } catch { } });
        lastKeyPressed = lastInputType = "";
    });
}


ConSyst.getSorted = function (selector, spAttrName, bpDesc=false) {
    return $($(selector).toArray().sort(function (a, b) {
        var aVal = parseInt(a.getAttribute(spAttrName)),
            bVal = parseInt(b.getAttribute(spAttrName));
        if (bpDesc) {
            return bVal - aVal;
        }
        else {
            return aVal - bVal;
        }
    }));
}

ConSyst.langChange = function (btn) {
    $("#cultureCode").val($(btn).attr('data-lang'));
}

ConSyst.menuChange = function (btn) {
    $("#spMenu").val($(btn).attr('data-menu'));
    $("#controllerName").val($(btn).attr('data-controller'));
    $("#actionName").val($(btn).attr('data-action'));
}

ConSyst.localizeText = function (text) {
    formatString = ConSyst.localizer != null ? (text in ConSyst.localizer ? ConSyst.localizer[text] : text) : text;
    if (arguments.length > 1) {
        const placeholders = Array.from({ length: arguments.length - 1 }, (_, index) => `{${index}}`);
        var formattedString = placeholders.reduce(
            (result, placeholder, index) => result.replace(placeholder, arguments[index + 1]),
            formatString
        );
    }
    else {
        var formattedString = formatString;
    }
    return formattedString;
}

ConSyst.initTextEditor = function () {
    var wysiwygElement = document.getElementsByClassName('wysiwyg')[0];
    var wysiwygInstance = new WYSIWYG(wysiwygElement, {
        wysiwygTranslations: {
            paragraph: ConSyst.localizeText("Paragraph"),
            textStyle: ConSyst.localizeText("Text Style"),
            heading: ConSyst.localizeText("Heading"),
            preformatted: ConSyst.localizeText('Pre-formatted'),
            bold: ConSyst.localizeText('Bold'),
            italic: ConSyst.localizeText('Italic'),
            strikethrough: ConSyst.localizeText('Strike Through'),
            underline: ConSyst.localizeText('Underline'),
            textcolor: ConSyst.localizeText('Text Color'),
            textBackgroundColor: ConSyst.localizeText('Text Background Color'),
            alignLeft: ConSyst.localizeText('Align Left'),
            alignCenter: ConSyst.localizeText('Align Center'),
            alignRight: ConSyst.localizeText('Align Right'),
            alignJustify: ConSyst.localizeText('Align Justify'),
            insertLink: ConSyst.localizeText('Insert Link'),
            insertPicture: ConSyst.localizeText('Insert Picture'),
            unorderedList: ConSyst.localizeText('Unordered List'),
            orderedList: ConSyst.localizeText('Ordered List'),
            increaseIndent: ConSyst.localizeText('Increase Indent'),
            decreaseIndent: ConSyst.localizeText('Decrease Indent'),
            insertHorizontalRule: 'Wstaw linię poziomą',
            showHTML: 'Pokaż kod HTML',
            undo: 'Cofnij',
            redo: 'Przywróć',
            addLinkHead: 'Dodaj Link',
            addImageHead: 'Dodaj Obraz',
            linkUrlLabel: 'Wpisz adres:',
            linkDescription: 'Wpisz opis',
            imageUrlLabel: 'Wpisz adres obrazka:',
            okButton: 'OK',
            cancelButton: 'Anuluj',
            moreOptions: 'Pokaż więcej opcji',
        }
    });
}

ConSyst.replaceURLParam = function (url, paramName, paramValue) {
    if (paramValue == null) {
        paramValue = '';
    }
    var pattern = new RegExp('\\b(' + paramName + '=).*?(&|#|$)');
    if (url.search(pattern) >= 0) {
        return url.replace(pattern, '$1' + paramValue + '$2');
    }
    url = url.replace(/[?#]$/, '');
    return url + (url.indexOf('?') > 0 ? '&' : '?') + paramName + '=' + paramValue;
}

ConSyst.removeURLParam = function (url, paramName) {
    //prefer to use l.search if you have a location/link object
    var urlparts = url.split('?');
    if (urlparts.length >= 2) {

        var prefix = encodeURIComponent(paramName) + '=';
        var pars = urlparts[1].split(/[&;]/g);

        //reverse iteration as may be destructive
        for (var i = pars.length; i-- > 0;) {
            //idiom for string.startsWith
            if (pars[i].lastIndexOf(prefix, 0) !== -1) {
                pars.splice(i, 1);
            }
        }

        return urlparts[0] + (pars.length > 0 ? '?' + pars.join('&') : '');
    }
    return url;
}

ConSyst.globalTabCount = 1;

ConSyst.redirectToPage = function (url) {
    if (ConSyst.globalTabCount >= 5) {
        void (window.open(url), 'child_window');
    }
    else {
        void (window.open(url));
    }
    return false;
}

ConSyst.parseFloat = function (value, defVal = null) {
    const result = parseFloat(value);
    if (defVal == null) {
        return result;
    }
    else {
        return isNaN(result) ? defVal : result;
    }
}

ConSyst.supportCreate = function (url) {

    var newUrl = '/Support/Create?callerUrl=' + encodeURIComponent(url);

    if (ConSyst.globalTabCount >= 5) {
        void (window.open(newUrl, 'child_window'));
    } else {
        void (window.open(newUrl));
    }
    return false;
}

ConSyst.updateGradesForType = async function (customerId, unitType, gradesSelect, emptyString="N/A") {

    // Validate required parameters
    if (!customerId || !unitType) {
        console.warn("updateGradesForType: Invalid parameters", { customerId, unitType });
        return [];
    }

    if (gradesSelect == null || gradesSelect == '') {
        gradesSelect = '.customer-grade';
    }

    try {
        // Call the GetGrades endpoint to fetch grades filtered by customer ID and unit type
        const result = await ConSyst.callService("Customer", "GET", "GetGrades", {
            customerId: customerId,
            unitType: unitType,
            criteriaOnly: false
        });

        if (result && Array.isArray(result.LGrades) && result.LGrades.length >= 0) {

            const grades = result.LGrades || [];
            const areGradesCustomer = result.BAreGradesCustomer || false;

            // Update the global variables for later use
            if (grades.length > 0) {
                ConSyst.customerGrades = grades;
                ConSyst.areGradesCustomer = areGradesCustomer;
            }

            // Update the select element if provided

            if (gradesSelect) {
                const $select = $(gradesSelect);

                for (var s = 0; s < $select.length; s++) {

                    var selectJq = $($select[s]);
                    var prevVal = selectJq.val();
                    if (prevVal == "" || prevVal == null || prevVal == undefined) {
                        prevVal = $($select).data("cst-old-value");
                    }
                    selectJq.empty();

                    var gradesToAdd = grades;
                    if (selectJq.hasClass("customer-criteria")) {
                        gradesToAdd = grades.filter((gr) => {
                            return gr.BIsCriteria == true;
                        });
                    }

                    if (emptyString != null && emptyString != "" && selectJq.attr('multiple') == undefined) {
                        // Add empty option
                        selectJq.append($('<option/>', {
                            value: '',
                            text: emptyString
                        }));
                    }

                    // Add grade options
                    $.each(gradesToAdd, function (index, grade) {
                        selectJq.append($('<option/>', {
                            value: grade.SCode,
                            text: grade.SName
                        }));
                        
                    });

                    // Update Select UI component if necessary
                    if (selectJq.hasClass("select")) {
                        ConSyst.setSelectValue(selectJq[0], prevVal);
                    }
                }
            }

            // Update BIsGradeCustomer hidden input
            $("#BIsGradeCustomer").val(areGradesCustomer);

            return grades;

        } else {
            const errorMessage = result?.SName || ConSyst.localizeText("Unknown error in grade service");
            //ConSyst.showMessage(errorMessage, "danger");
            return [];
        }
    } catch (error) {
        console.error("Error fetching grades for unit type:", error);
        //ConSyst.showMessage(ConSyst.localizeText("Error fetching grade criteria"), "danger");
        return [];
    }
};

ConSyst.updateSortIcons = function () {
    // Get the current sort parameters from the URL
    const urlParams = new URLSearchParams(window.location.search);
    const sort = urlParams.get('sort');
    const sortOrder = urlParams.get('sortOrder');

    // Reset all icons first
    $('.datatable-sort-icon').css('transform', '');
    const $sortIcon = $(`i[data-mdb-sort="${sort}"]`);
    $sortIcon.addClass('active');
    // If we have a sort parameter and it's descending order
    if (sort && sortOrder === "desc") {
        // Find the icon with the matching data-mdb-sort attribute
       
        if ($sortIcon.length) {
            // Rotate the icon 180 degrees to indicate descending order
            $sortIcon.css('transform', 'rotate(180deg)');
        }
    }
}
