import CookieHandler from "./CookieHandler"

export default class GDPRHandler {
    constructor(config) {
        let defaultCb = () => {};
        this.defaults = {
            sections: {
                functional: {
                    enable: defaultCb,
                    disable: defaultCb,
                }
            }
        };

        this.config = {};
        this.config = mergeDeep(this.config, this.defaults);
        this.config = mergeDeep(this.config, config);

        this.interfaceQuickAccessBtn = document.querySelector('.gdpr-quick-access');
        this.interfaceBanner = document.querySelector('.gdpr-banner');
        this.interfaceBackdrop = document.querySelector('.gdpr-backdrop');

        // setup default section callback if none provided
        Object.keys(this.config.sections).forEach((section) => {
            if (typeof this.config.sections[section].enable === 'undefined') {
                this.config.sections[section].enable = defaultCb;
            }
            if (typeof this.config.sections[section].disable === 'undefined') {
                this.config.sections[section].disable = defaultCb;
            }
        });
    }

    init() {
        const versionChanged = CookieHandler.clearCookiesIfVersionChanged();

        this._initGTMConsentDefaults();
    
        // add event listeners
        this._getSectionControls().forEach((control) => {
            control.addEventListener('change', () => {
                this.onControlChange(control, control.checked === true);
            })
        });
        [...document.querySelectorAll('.gdpr-save, .gdpr-close')].forEach((control) => {
            control.addEventListener('click', () => {
                this.onSave();
            });
        });
        [...document.querySelectorAll('.gdpr-accept-all')].forEach((control) => {
            control.addEventListener('click', () => {
                this.onGrantAll();
            });
        });
        [...document.querySelectorAll('.gdpr-deny-all')].forEach((control) => {
            control.addEventListener('click', () => {
                this.onDenyAll();
            });
        });
        document.querySelector('.gdpr-banner .gdpr-configure')?.addEventListener('click', () => {
            this._hideEl(this.interfaceBanner);
        });

        let preInitialized = CookieHandler.hasConsentCookie() && !versionChanged;
        if (preInitialized) {
            this._showEl(this.interfaceQuickAccessBtn);
        } else {
            this._showEl(this.interfaceBanner);
        }

        // retrieve state
        this.syncStates();

        // call callbacks after sync on initialization
        this._getSectionControls().forEach((control) => {
            let section = control.getAttribute('data-gdpr-section');
            this.config.sections[section][control.checked ? 'enable' : 'disable']();
        });
    }

    onGrantAll() {
        this._setControlsState(true);
        this._getSectionControls().forEach((el) => {
            let section = el.getAttribute('data-gdpr-section');
            this.config.sections[section].enable();
        });
        this.config.sections.functional.enable();
        this.persistStates();
        this._closeInterfaces();
    }

    onDenyAll() {
        this._setControlsState(false);
        this._getSectionControls().forEach((el) => {
            let section = el.getAttribute('data-gdpr-section');
            this.config.sections[section].disable();
        });
        this.config.sections.functional.disable();
        this.persistStates();
        this._closeInterfaces();
    }

    onSave() {
        this.persistStates();
        this._closeInterfaces();
    }

    onControlChange(control, state) {
        let section = control.getAttribute('data-gdpr-section');
        if (typeof this.config.sections[section] === 'undefined') {
            console.warn('gdpr section "' + section + '" is not declared');
        }
        this.config.sections[section][state ? 'enable' : 'disable']();
        this.persistStates();
    }

    /**
     * check cookie values and init controls
     */
    syncStates() {
        const consentData = CookieHandler.getConsentCookie();
        if (!consentData) {
            return;
        }

        const analyticsControl = this._getSectionControl('analytics');
        if (analyticsControl) {
            analyticsControl.checked = consentData.analytics_storage === "granted";
        }

        const marketingControl = this._getSectionControl('marketing');
        if (marketingControl) {
            marketingControl.checked = consentData.ad_storage === "granted" &&
                consentData.personalization_storage === "granted";
        }
    }

    /**
     * check controls values and set cookie values
     */
    persistStates() {
        let consentData = CookieHandler.getConsentCookie() || {};

        this._getSectionControls().forEach((control) => {
            let section = control.getAttribute('data-gdpr-section');
            if (section !== 'functional') {
                consentData = CookieHandler.updateConsentCookie(section, control.checked === true, false);
            }
        });
    
        consentData = CookieHandler.updateConsentCookie('functional', true, false);
        CookieHandler.updateGTMConsentState(consentData);
    }

    _getSectionControls() {
        return [...document.querySelectorAll('.gdpr-control:not([data-gdpr-section="functional"])')]
    }

    _getSectionControl(section) {
        return document.querySelector('.gdpr-control[data-gdpr-section="' + section + '"]')
    }

    _setControlsState(state) {
        this._getSectionControls().forEach((control) => {
            let section = control.getAttribute('data-gdpr-section');
            if (section !== 'functional') {
                control.checked = state;
            } else {
                control.checked = true;
            }
        });
    }

    _closeInterfaces() {
        let modal = document.querySelector('#gdpr-modal');
        modal.classList.remove('show');
        modal.removeAttribute('aria-modal');
        modal.removeAttribute('role');
        modal.setAttribute('aria-hidden', 'true');
        modal.setAttribute('style', 'display: none');

        this._hideEl(this.interfaceBanner);
        this._showEl(this.interfaceQuickAccessBtn);
    }

    _showEl(el) {
        el.classList.add('shown');
        if (el === this.interfaceBanner) {
            this.interfaceBackdrop?.classList.add('shown');
        }
    }

    _hideEl(el) {
        el.classList.remove('shown');
        if (el === this.interfaceBanner) {
            this.interfaceBackdrop?.classList.remove('shown');
        }
    }
    
    _initGTMConsentDefaults() {
        if (typeof window === 'undefined') {
            return;
        }

        window.gtag = window.gtag || function() {
            (window.dataLayer = window.dataLayer || []).push(arguments);
        };

        const consentData = CookieHandler.getConsentCookie();
        if (consentData) {
            CookieHandler.updateGTMConsentState(consentData);
            return;
        }

        gtag('consent', 'default', {
            'analytics_storage': 'denied',
            'ad_user_data': 'denied',
            'ad_storage': 'denied',
            'personalization_storage': 'denied',
            'ad_personalization': 'denied',
            'functionality_storage': 'granted',
            'security_storage': 'granted',
            'wait_for_update': 500
        });
    }
};

function isObject(item) {
    return (item && typeof item === 'object' && !Array.isArray(item));
}

/**
 * Deep merge two objects.
 * @param target
 * @param sources
 */
function mergeDeep(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();

    if (isObject(target) && isObject(source)) {
        for (const key in source) {
            if (isObject(source[key])) {
                if (!target[key]) Object.assign(target, {[key]: {}});
                mergeDeep(target[key], source[key]);
            } else {
                Object.assign(target, {[key]: source[key]});
            }
        }
    }

    return mergeDeep(target, ...sources);
}
