import * as utils from "./utility/helper-functions.js";
import * as state from "./state/state.js";
import * as translationsUI from "./components/translations-ui.js";
import * as paletteUI from "./components/palettes-ui.js";
import * as fontsUI from "./components/fonts-ui.js";
import * as gridThemeUI from "./components/grid-themes-ui.js";
import * as settingsUI from "./components/settings-ui.js";
import * as stylesUI from "./components/styles-ui.js";
import * as mappings from "./utility/mappings.js";

const saveChanges = document.querySelectorAll(".save-changes");
const chartContainerClass = ".vitara-theme-custom.vitara-chart-container";

function doPost(type, data, action, mode = "normal") {
    if ([type, data, action].every(strVal => strVal.trim() !== "")) {
        let file = "";
        const csrfToken = document.querySelector('input[name="csrfToken"]').value;
        switch (type) {
            case "settings":
                file = mode === "normal" ? "global.txt" : "IBCSGlobal.txt";
                break;
            case "styles":
                file = mode === "normal" ? "customStyles.css" : "customIBCSStyles.css";
                break;
            case "translations":
                file = translationsUI.translationFiles[translationsUI.languageDropdown.value];
                break;
            default:
                throw new Error("Invalid type provided for file update");
        }

        fetch(`CustomEditor/backend.jsp`, {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            body: `file=custom/${file}&type=${type}&data=${encodeURIComponent(utils.decodeHtmlEntities(data))}&action=${action}&ts=${new Date().getTime()}&csrfToken=${csrfToken}`
        }).catch(error => {
            throw new Error("Error updating file -", error);
        });
    }
}

// ==================== Events handling ==================== //

window.onload = (async (e) => {
    try {
        // Load global.txt and customStyles.css files
        const settingsResponse = await fetch(`CustomEditor/backend.jsp?file=custom/global.txt&type=setting&ts=${new Date().getTime()}`);
        const settingsJson = await settingsResponse.json();
        const stylesResponse = await fetch(`CustomEditor/backend.jsp?file=custom/customStyles.css&type=style&ts=${new Date().getTime()}`);
        const stylesJson = await stylesResponse.json();
        const rawSettingsText = settingsJson.data || "";
        const settingLines = rawSettingsText.split(/\r?\n/).map(line => line.trim()).filter(line => line && !line.startsWith('#'));
        const rawStylesText = stylesJson.data || "";
        const styleLines = utils.cleanAndFlattenCSS(rawStylesText).split(/\r?\n/);
        
        // Load IBCSGlobal.txt and customIBCSStyles.css files
        const ibcsSettingsResponse = await fetch(`CustomEditor/backend.jsp?file=custom/IBCSGlobal.txt&type=setting&ts=${new Date().getTime()}`);
        const ibcsSettingsJson = await ibcsSettingsResponse.json();
        const ibcsStylesResponse = await fetch(`CustomEditor/backend.jsp?file=custom/customIBCSStyles.css&type=style&ts=${new Date().getTime()}`);
        const ibcsStylesJson = await ibcsStylesResponse.json();
        const rawIBCSSettingsText = ibcsSettingsJson.data || "";
        const ibcsSettingLines = rawIBCSSettingsText.split(/\r?\n/).map(line => line.trim()).filter(line => line && !line.startsWith('#'));
        const rawIBCSStylesText = ibcsStylesJson.data || "";
        const ibcsStyleLines = utils.cleanAndFlattenCSS(rawIBCSStylesText).split(/\r?\n/);

        settingLines.forEach(line => {
            const [key, value] = line.split('=');
            if (key && value) {
                const collectionKeys = ["font.customFonts", "palette.list", "gridThemes.themes", "shortFormatSymbols", "accessControlLevels"];
                if (collectionKeys.includes(key.trim())) {
                    state.updateSettingsData(key.trim(), value.split(","));
                } else {
                    state.updateSettingsData(key.trim(), value.trim());
                }
            }
        });

        ibcsSettingLines.forEach(line => {
            const [key, value] = line.split('=');
            if (key && value) {
                state.updateIBCSSettingsData(key.trim(), value.trim());
            }
        });

        // Handle custom palettes
        const defaultPaletteLine = utils.extractLineFromRawText(rawSettingsText, "palette.defaultPalette");
        const activePalettesLine = utils.extractLineFromRawText(rawSettingsText, "palette.list");
        const defaultPalette = defaultPaletteLine ? defaultPaletteLine.split("=")[1].trim() : null;
        const activePalettes = activePalettesLine ? activePalettesLine.split("=")[1].split(",").map(s => s.trim()) : [];
        const palettes = settingLines.filter(line => {
            return line.startsWith('palette.') &&
                !line.startsWith('palette.list') &&
                !line.startsWith('palette.defaultPalette')
        }).map(line => {
            const [key, value] = line.split('=').map(string => string.trim());
            state.updateSettingsData(key, value.split(","));
            const name = key.split('.')[1];
            const colors = value.split(',').map(color => color.trim());
            return {
                name: name,
                colors: colors,
                isActive: activePalettes.includes(name),
                isDefault: name === defaultPalette
            };
        });

        paletteUI.addPaletteToList(palettes);
        [...paletteUI.getPaletteList()].reverse().forEach((palette) => {
            paletteUI.createPaletteUI(palette.name, palette.colors, palette.isActive, palette.isDefault);
        });

        if (paletteUI.getPaletteList().length) {
            paletteUI.savedPalettesSection.classList.add("visible");
        }

        // Create Grid Themes UI
        const gridThemesLine = utils.extractLineFromRawText(rawSettingsText, "gridThemes.themes");
        const defaultGridThemeLine = utils.extractLineFromRawText(rawSettingsText, "gridThemes.defaultTheme");
        const gridThemes = gridThemesLine ? gridThemesLine.split("=")[1].split(",").map(s => s.trim()) : [];
        const defaultGridTheme = defaultGridThemeLine ? defaultGridThemeLine.split("=")[1].trim() : null;
        gridThemeUI.addThemeToList(gridThemes);
        [...gridThemeUI.getGridThemeList()].reverse().forEach((theme) => {
            gridThemeUI.createGridThemeUI(theme, theme === defaultGridTheme);
        });

        if (gridThemeUI.getGridThemeList().length) {
            gridThemeUI.savedGridThemesSection.classList.add("visible");
        }

        // Fill other global.txt settings
        if (state.getSettingsData()["chart.animation"]) {
            settingsUI.chartAnimations.checked = state.getSettingsData()["chart.animation"] === "1" ? true : false;
        }

        if (state.getSettingsData()["enablePrivilegeForEditor"] && state.getSettingsData()["enablePrivilegeForEditor"] === "1") {
            settingsUI.enablePrivilege.checked = true;
        } else {
            settingsUI.useDefaultPrivilege.disabled = true;
            settingsUI.useCustomPrivilege.disabled = true;
            settingsUI.customPrivilegeValue.disabled = true;
        }

        if (state.getSettingsData()["checkPrivilegeForPropertiesEditor"] && state.getSettingsData()["checkPrivilegeForPropertiesEditor"] !== "125") {
            settingsUI.useCustomPrivilege.checked = true;
            settingsUI.customPrivilegeValue.value = state.getSettingsData()["checkPrivilegeForPropertiesEditor"];
        } else {
            settingsUI.useDefaultPrivilege.checked = true;
            settingsUI.customPrivilegeValue.disabled = true;
        }

        if (state.getSettingsData()["gridThemes.baseThemes"]) {
            settingsUI.baseGridTheme.value = state.getSettingsData()["gridThemes.baseThemes"];
        }

        if (state.getSettingsData()["security.allowURLLinks"]) {
            settingsUI.linksAccess.checked = state.getSettingsData()["security.allowURLLinks"] === "1" ? true : false;
        }

        if (state.getSettingsData()["statePresentation.contextMenu"]) {
            settingsUI.contextMenuAccess.checked = state.getSettingsData()["statePresentation.contextMenu"] === "1" ? true : false;
        }

        if (state.getSettingsData()["rtlSupport"]) {
            settingsUI.rtlSupport.checked = state.getSettingsData()["rtlSupport"] === "true";
        }

        if (state.getSettingsData()["metricNegativeValueFormat"]) {
            settingsUI.negativeValueFormat.value = state.getSettingsData()["metricNegativeValueFormat"];
        }

        if (state.getSettingsData()["seriesLimit"]) {
            settingsUI.seriesColorsLimit.value = state.getSettingsData()["seriesLimit"];
        }

        if (state.getSettingsData()["shortFormatSymbols"]) {
            settingsUI.metricSuffixes.forEach((metricSuffix, index) => {
                metricSuffix.value = state.getSettingsData()["shortFormatSymbols"][index];
            });
        }

        if (state.getSettingsData()["accessControlLevels"]) {
            settingsUI.accessControlLevels.forEach((accessLevel) => {
                const accessLevelCode = accessLevel.getAttribute("data-access-level");
                accessLevel.checked = state.getSettingsData()["accessControlLevels"].includes(accessLevelCode);
            });
        }

        // Fill IBCSGlobal.txt settings
        if (state.getIBCSSettingsData()["UseIBCSAsDefaultViewMode"]) {
            settingsUI.ibcsByDefault.checked = Boolean(state.getIBCSSettingsData()["UseIBCSAsDefaultViewMode"]);
        }

        if (state.getIBCSSettingsData()["DT.AC"]) {
            settingsUI.actualDataType.value = state.getIBCSSettingsData()["DT.AC"];
        }

        if (state.getIBCSSettingsData()["DT.FC"]) {
            settingsUI.forecastDataType.value = state.getIBCSSettingsData()["DT.FC"];
        }

        if (state.getIBCSSettingsData()["DT.PL"]) {
            settingsUI.plannedDataType.value = state.getIBCSSettingsData()["DT.PL"];
        }

        if (state.getIBCSSettingsData()["DT.PY"]) {
            settingsUI.previousDataType.value = state.getIBCSSettingsData()["DT.PY"];
        }

        if (state.getIBCSSettingsData()["negativeMetrics"]) {
            settingsUI.negativeMetrics.value = state.getIBCSSettingsData()["negativeMetrics"];
        }

        // Handle custom fonts
        const customFontsLine = utils.extractLineFromRawText(rawSettingsText, "font.customFonts");
        const defaultFontLine = utils.extractLineFromRawText(rawSettingsText, "font.defaultFont");
        const customFonts = customFontsLine ? customFontsLine.split("=")[1].split(",").map(s => s.trim()) : [];
        const defaultFont = defaultFontLine ? defaultFontLine.split("=")[1].trim() : null;
        fontsUI.addFontToList(customFonts);

        // Save custom styles to state
        const userDefinedStyles = styleLines.filter((line) => {
            if (line.includes("@font-face")) {
                state.addFontFaceDefinition(line);
                return false;
            }

            if (line.includes("vitara-")) {
                const className = line.split("{")[0].trim();
                const content = line.match(/\{([^}]*)\}/)[1].trim();
                const parsedCss = utils.parseCssBlock(content);
                let isUserDefined = true;

                if (className === chartContainerClass) {
                    // Chart container styles
                    state.updateStylesData(chartContainerClass, parsedCss);
                    isUserDefined = false;
                } else {
                    for (const componentKey in mappings.stylePropertyMapping) {
                        if (!Object.hasOwn(mappings.stylePropertyMapping, componentKey)) continue;
                        if (mappings.stylePropertyMapping[componentKey].stylingTarget === "highcharts-d3-styling" || 
                            mappings.stylePropertyMapping[componentKey].stylingTarget === "grid-styling") {
                            const gridTheme = gridThemeUI.getGridThemeFromStyleBlock(className, mappings.stylePropertyMapping[componentKey].classList[0]);
                            // Global styles for a component
                            if (className === mappings.stylePropertyMapping[componentKey].classList[0]) {
                                Object.entries(parsedCss).forEach(([property, value]) => {
                                    stylesUI.updateStyleDefinitions(property, value, "global", componentKey);
                                });
                                isUserDefined = false;
                                break;
                            } else if (gridTheme !== "") {
                                // Custom Grid Theme styles
                                Object.entries(parsedCss).forEach(([property, value]) => {
                                    stylesUI.updateStyleDefinitions(property, value, gridTheme, componentKey);
                                });
                                isUserDefined = false;
                                break;
                            } else {
                                // Chart specific styles for a component
                                for (const chartClass in mappings.chartClassMapping) {
                                    if (!Object.hasOwn(mappings.chartClassMapping, chartClass)) continue;
                                    if (className === `.${chartClass} ${mappings.stylePropertyMapping[componentKey].classList[0]}`) {
                                        Object.entries(parsedCss).forEach(([property, value]) => {
                                            stylesUI.updateStyleDefinitions(property, value, chartClass, componentKey);
                                        });
                                        isUserDefined = false;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                return isUserDefined;
            }
            return true;
        });
        state.updateStylesData("userDefinedStyles", userDefinedStyles);

        // Generate font list from customStyles.css and global.txt
        if (state.getStylesData()?.[chartContainerClass]?.["font-family"]) {
            const cleanedFontlist = state.getStylesData()[chartContainerClass]["font-family"].replace(/\s*!important\s*$/i, '');
            const stylesheetFonts = cleanedFontlist ? cleanedFontlist.split(',').map(f => utils.stripQuotes(f.trim())).filter(Boolean) : [];

            // Check if customStyles.css contains any fonts that are not present in global.txt
            const fontDifference = utils.difference(new Set(stylesheetFonts), new Set(fontsUI.getFontList()));
            if (fontDifference.size > 0) {
                fontsUI.addFontToList(Array.from(fontDifference));
            }
        }

        // Create custom font list UI
        const fontFamilySelect = document.querySelector(".font-family-styling");
        if (fontsUI.getFontList().length > 0) {
            [...fontsUI.getFontList()].reverse().forEach((customFont) => {
                fontsUI.createCustomFontsUI(customFont, (defaultFont === customFont));
                fontFamilySelect.prepend(new Option(customFont, customFont));
            });
            fontFamilySelect.prepend(new Option("Default", ""));
            fontsUI.savedFontsSection.classList.add("visible");
            if (!state.getStylesData()[chartContainerClass]) {
                state.updateStylesData(chartContainerClass, {});
            }
            // Update font list in state if new fonts were added from global.txt
            state.updateStylesData(chartContainerClass, {
                ...state.getStylesData()[chartContainerClass],
                "font-family": utils.generateFontFamilyString(fontsUI.getFontList(), defaultFont)
            });
            state.updateSettingsData("font.customFonts", fontsUI.getFontList());
        } else {
            fontFamilySelect.append(new Option("No custom fonts available", ""));
        }

        // Save IBCS styles to state
        ibcsStyleLines.forEach((line) => {
            if (line !== "") {
                const className = line.split("{")[0].trim();
                const content = line.match(/\{([^}]*)\}/)[1].trim();
                const parsedCss = utils.parseCssBlock(content);
                for (const componentKey in mappings.stylePropertyMapping) {
                    if (!Object.hasOwn(mappings.stylePropertyMapping, componentKey)) continue;
                    if (mappings.stylePropertyMapping[componentKey].stylingTarget === "ibcs-standard-styling" ||
                        mappings.stylePropertyMapping[componentKey].stylingTarget === "ibcs-waterfall-styling") {
                        if (className.startsWith(mappings.stylePropertyMapping[componentKey].classList[0])) {
                            Object.entries(parsedCss).forEach(([property, value]) => {
                                stylesUI.updateStyleDefinitions(property, value, "ibcs", componentKey);
                            });
                            break;
                        }
                    }
                }
            }
        });

        // Create styling scope selector
        stylesUI.createStylingScopeSelector("normal-stylesheet");

        // Create styling component selector for normal styles by default
        stylesUI.createStylingComponentSelector("highcharts-d3-styling");

        // Create grid theme selector
        gridThemeUI.getGridThemeList().forEach((theme) => {
            gridThemeUI.addOrRemoveThemeFromScope(theme, "add");
        });

        // Fill style editor fields
        stylesUI.refreshStyleSelectors();

        // Fill translation values
        const defaultLanguage = translationsUI.languageDropdown.value;
        translationsUI.createTranslationsUI(defaultLanguage);

        // Set tab navigation logic
        document.querySelectorAll(".tab-navigation").forEach((tabNav) => {
            tabNav.addEventListener("click", (e) => {
                let allowSwitch = true;
                if (state.hasUnsavedChanges()) {
                    allowSwitch = confirm("You have unsaved changes. Proceed?");
                }

                if (allowSwitch) {
                    state.setUnsavedChanges(false);
                    document.querySelectorAll(".tab-navigation").forEach(tabNavAlt => tabNavAlt.classList.remove("active"));
                    e.target.classList.add("active");
                    document.querySelectorAll(".tab").forEach(tab => tab.classList.remove("visible"));
                    document.querySelector(`.tab.${e.target.getAttribute("data-tab-key")}`).classList.add("visible");

                    if (e.target.getAttribute("data-tab-key") === "styles") {
                        stylesUI.refreshStyleSelectors();
                    }
                }
            });
        });
    } catch (error) {
        state.addBlockedPage("settings", "An error occurred while loading initial data");
        console.error("Error loading initial data:", error);
        alert("An error occurred while loading the initial data. Please try again later.");
    }
});

function postSaveChanges() {
    // Update grid theme selector
    gridThemeUI.emptyThemeOptions();
    gridThemeUI.getGridThemeList().forEach((theme) => {
        gridThemeUI.addOrRemoveThemeFromScope(theme, "add");
    });

    // Update font-family selector
    const fontFamilySelect = document.querySelector(".font-family-styling");
    fontFamilySelect.options.length = 0;
    [...fontsUI.getFontList()].reverse().forEach((customFont) => {
        fontFamilySelect.prepend(new Option(customFont, customFont));
    });
    fontFamilySelect.prepend(new Option("Default", ""));
}

window.addEventListener('beforeunload', function (event) {
    if (state.hasUnsavedChanges()) {
        event.preventDefault();
        event.returnValue = ''; // This triggers the browser dialog
    }
});

document.querySelectorAll("form").forEach((form) => {
    form.addEventListener("submit", (e) => {
        e.preventDefault();
    });
});

saveChanges.forEach(saveChangesBtn => {
    saveChangesBtn.addEventListener("click", (e) => {
        if (e.detail === 0) {
            return;
        }

        const confirmation = confirm("WARNING: You are about to save global changes to VitaraCharts that will affect all users. Are you sure you want to proceed?");
        if (!confirmation) {
            return; // Abort if user cancels
        }

        const formType = e.target.getAttribute("data-form-type");
        const message = "Generated using Chart Customization UI";
        switch (formType) {
            case "settings":
                // Check if settings page is blocked from saving
                if (state.getBlockedPages().blockedPages.includes("settings")) {
                    alert(`Cannot save changes: ${state.getBlockedPages().reason}`);
                    return;
                }

                // Update global.txt file
                const settings = Object.entries(state.getSettingsData()).map(setting => setting.join("="));
                settings.unshift(`#${message}`);
                doPost("settings", settings.join("\n"), "update", "normal");

                // Update IBCSGlobal.txt file
                const ibcsSettings = Object.entries(state.getIBCSSettingsData()).map(setting => setting.join("="));
                ibcsSettings.unshift(`#${message}`);
                doPost("settings", ibcsSettings.join("\n"), "update", "ibcs");

                // Upload fonts
                // if (fontUploadData.length) {
                //     fontUploadData.forEach((fontInfo) => {
                //         const urlString = `fontName=${encodeURIComponent(fontInfo.fontName)}&fileName=${fontInfo.fileName}`;
                //         const formData = new FormData();
                //         formData.append('fontFile', fontInfo.fontFile);
                //         fetch(`CustomEditor/backend.jsp?file=custom/global.txt&type=font&action=upload&${urlString}`, {
                //             method: 'POST',
                //             body: formData
                //         })
                //         .then((response) => response.json())
                //         .catch((error) => {
                //             throw new Error("Font upload failed - ", error);
                //         });
                //     });
                //     fontUploadData = [];
                // }

                // Delete fonts
                // if (fontDeleteData.length) {
                //     fontDeleteData.forEach((fileName) => {
                //         fetch(`CustomEditor/backend.jsp?file=custom/global.txt&type=font&action=delete&fileName=${fileName}`);
                //     });
                //     fontDeleteData = [];
                // }

                postSaveChanges();

                // "break" keyword deliberately omitted, need fallthrough to styles case
            case "styles":
                // Update normal styles
                const styles = [];
                let stylesPresent = false;
                if (state.getStylesData()[chartContainerClass]) {
                    stylesPresent = true;
                    styles.push(`${chartContainerClass} { ${utils.stringifyCssBlock(state.getStylesData()[chartContainerClass])} }`);
                }
                if (state.getStylesData()["editorDefinedStyles"]) {
                    stylesPresent = true;
                    Object.entries(state.getStylesData()["editorDefinedStyles"]).forEach(([selector, properties]) => {
                        styles.push(`${selector} { ${properties} }`);
                    });
                }
                if (state.getStylesData()["userDefinedStyles"]) {
                    stylesPresent = true;
                    styles.push(...state.getStylesData()["userDefinedStyles"]);                    
                }
                if (state.getFontFaceDefinitions().length) {
                    stylesPresent = true;
                    styles.unshift(...state.getFontFaceDefinitions());
                }
                if (stylesPresent) {
                    styles.unshift(`/* ${message} */`);
                    doPost("styles", styles.join("\n"), "update", "normal");
                }

                // Update IBCS styles
                if (state.getIBCSStylesData()["editorDefinedStyles"]) {
                    const ibcsStyles = [];
                    Object.entries(state.getIBCSStylesData()["editorDefinedStyles"]).forEach(([selector, properties]) => {
                        ibcsStyles.push(`${selector} { ${properties} }`);
                    });
                    ibcsStyles.unshift(`/* ${message} */`);
                    doPost("styles", ibcsStyles.join("\n"), "update", "ibcs");
                }
                break;
            case "translations":
                if (state.getBlockedPages().blockedPages.includes("translations")) {
                    alert(`Cannot save changes: ${state.getBlockedPages().reason}`);
                    return;
                }
                // Update translations
                const translations = Object.entries(state.getTranslationsData()).map(translation => translation.join("=")).join("\n");
                doPost("translations", translations, "update");
                break;
            default:
                throw new Error("Unknown form type given");
        }

        state.setUnsavedChanges(false);
        alert("Successfully applied changes");
    });
});

// For setting styles to sticky header
const formHeaders = document.querySelectorAll(".form-header");
formHeaders.forEach((formHeader) => {
    const observer = new IntersectionObserver(([entry]) => {
        if (entry.isIntersecting) {
            entry.target.classList.add("is-sticky");
        } else {
            entry.target.classList.remove("is-sticky");
        }
    }, {
        root: formHeader.parentElement,
        rootMargin: '-1px 0px 0px 0px',
        threshold: [1],
      });
    
    observer.observe(formHeader);
});
