import WebpackModules from "./webpackModules";

const normalizedPrefix = "da";
const randClass = new RegExp(`^(?!${normalizedPrefix}-)((?:[A-Za-z]|[0-9]|-)+)-(?:[A-Za-z]|[0-9]|-|_){6}$`);

export default new class ClassNormalizer {
    constructor(){
        window.Lightcord.BetterDiscord.ClassNormalizer = this
    }
    stop() {
        if (!this.hasPatched) return;
        this.unpatchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
        this.revertElement(document.querySelector("#app-mount"));
        this.hasPatched = false;
    }

    start() {
        if (this.hasPatched) return;
        this.patchClassModules(WebpackModules.findAll(this.moduleFilter.bind(this)));
        this.normalizeElement(document.querySelector("#app-mount"));
        this.hasPatched = true;
        this.patchDOMMethods();
    }

    patchClassModules(modules) {
        for (const module of modules) {
            this.patchClassModule(normalizedPrefix, module);
        }
    }

    unpatchClassModules(modules) {
        for (const module of modules) {
            this.unpatchClassModule(normalizedPrefix, module);
        }
    }

    shouldIgnore(value) {
        if (!isNaN(value)) return true;
        if (value.endsWith("px") || value.endsWith("ch") || value.endsWith("em") || value.endsWith("ms")) return true;
        if (value.startsWith("layerContainer-")) return true;
        if (value.startsWith("#") && (value.length == 7 || value.length == 4)) return true;
        if (value.includes("calc(") || value.includes("rgba")) return true;
        return false;
    }

    moduleFilter(module) {
        if (typeof module !== "object" || Array.isArray(module)) return false;
        if (module.__esModule) return false;
        if (!Object.keys(module).length) return false;
        for (const baseClassName in module) {
            const value = module[baseClassName];
            if (typeof value !== "string") return false;
            if (this.shouldIgnore(value)) continue;
            if (value.split("-").length === 1) return false;
            if (!randClass.test(value.split(" ")[0])) return false;
        }

        return true;
    }

    patchClassModule(componentName, classNames) {
        for (const baseClassName in classNames) {
            const value = classNames[baseClassName];
            if (this.shouldIgnore(value)) continue;
            const classList = value.split(" ");
            for (const normalClass of classList) {
                const match = normalClass.match(randClass);
                if (!match || !match.length || match.length < 2) continue; // Shouldn't ever happen since they passed the moduleFilter, but you never know
                const camelCase = match[1].split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
                classNames[baseClassName] += ` ${componentName}-${camelCase}`;
            }
        }
    }

    unpatchClassModule(componentName, classNames) {
        for (const baseClassName in classNames) {
            const value = classNames[baseClassName];
            if (this.shouldIgnore(value)) continue;
            let newString = "";
            const classList = value.split(" ");
            for (const normalClass of classList) {
                if (normalClass.startsWith(`${componentName}-`)) continue;
                newString += ` ${normalClass}`;
            }
            classNames[baseClassName] = newString.trim();
        }
    }

    normalizeElement(element) {
        if (!(element instanceof Element)) return;
        const classes = element.classList;
        for (let c = 0, clen = classes.length; c < clen; c++) {
            if (!randClass.test(classes[c])) continue;
            const match = classes[c].match(randClass)[1];
            const newClass = match.split("-").map((s, i) => i ? s[0].toUpperCase() + s.slice(1) : s).join("");
            element.classList.add(`${normalizedPrefix}-${newClass}`);
        }
        for (const child of element.children) this.normalizeElement(child);
    }

    revertElement(element) {
        if (!(element instanceof Element)) return;
        if (element.children && element.children.length) this.revertElement(element.children[0]);
        if (element.nextElementSibling) this.revertElement(element.nextElementSibling);
        const classes = element.classList;
        const toRemove = [];
        for (let c = 0; c < classes.length; c++) {
            if (classes[c].startsWith(`${normalizedPrefix}-`)) toRemove.push(classes[c]);
        }
        element.classList.remove(...toRemove);
    }

    patchDOMMethods() {
        const contains = DOMTokenList.prototype.contains;
        DOMTokenList.prototype.contains = function(token) {
            // const tokens = token.split(" ");
            return Reflect.apply(contains, this, [token.split(" ")[0]]);
            // return tokens.every(t => contains.call(this, t));
        };

        const add = DOMTokenList.prototype.add;
        DOMTokenList.prototype.add = function(...tokens) {
            for (let t = 0; t < tokens.length; t++) {
                tokens[t] = tokens[t].split(" ")[0];
            }
            return Reflect.apply(add, this, tokens);
        };

        const remove = DOMTokenList.prototype.remove;
        DOMTokenList.prototype.remove = function(...tokens) {
            for (let t = 0; t < tokens.length; t++) {
                tokens[t] = tokens[t].split(" ")[0];
            }
            return Reflect.apply(remove, this, tokens);
        };
    }

};