/**
 * @since Last modified: 2024-08-13 22:33:47
 * @class Spy
 * @description Warnings and tips.
 * @param {getEl} elem - Target element.
 * @param {object} [options] - Parameters
 * @see {@link https://codepen.io/axui/pen/mdgyYwO|demo @ codepen}
 * @example
 * ax.Spy('#id');
 */
import getEl from '../utils/getEl';
import PubBase from './PubBase';
import PubListen from './PubListen';
import PubCommon from './PubCommon';
import pipe from '../utils/pipe';
import ax from '../ax';
import classes from '../utils/classes';
import optSpy from '../options/optSpy';
import Message from './Message';
import preventDft from '../utils/preventDft';
import scrollTo from '../utils/scrollTo';
import debounce from '../utils/debounce';
import getDataType from '../utils/getDataType';
import allToEls from '../utils/allToEls';
import attrToJson from '../utils/attrToJson';
import renderTpl from '../utils/renderTpl';
import isEmpty from '../utils/isEmpty';
class Spy extends pipe(PubListen, PubCommon)(PubBase) {
    constructor(elem, options) {
        super();
        this.options = {};
        this.ready({
            options,
            maps: optSpy,
            host: elem,
            component: false,
        });
        /**
        * @name Spy.targetEl
        * @description Target element.
        * @type getEl
        */
        /**
        * @name Spy.rawOpts
        * @description Raw Parameters of the instance from map.
        * @type object
        */
        /**
         * @name Spy.dftOpts
         * @description Default Parameters of the instance from merging.The reset method requires this variable.
         * @type object
         */
        /**
         * @name Spy.options
         * @description Parameters of the instance.
         * @type object
         */
        /**
         * @name Spy.destroyed
         * @description Destruction state.
         * @type boolean
         */
        /**
        * @name Spy.plans
        * @description Plans for listening.
        * @type object
        */
        /**
        * @name Spy.timestamp
        * @description Instance creation time.
        * @type number
        */
        this.timestamp = new Date().getTime();
        this.navEl = this.options.navSel ? getEl(this.options.navSel) : null;
        this.subEls = this.navEl && this.options.subSel ? this.navEl.querySelectorAll(this.options.subSel) : [];
        this.rootEl = getEl(this.options.root);
        this.getActive();
        super.listen({ name: 'ready' });
        this.init();
    }
    /**
    * @description Initialization instance.
    * @param {fn_cb_none} [cb] - Callback execution after initialization.
    * @returns {*} Return the instance itself.
    */
    init(cb) {
        super.listen({ name: 'start' });
        this.items = [];
        this.interactOpt();
        this.interactIns && this.interactIns.disconnect();
        this.interactIns = new IntersectionObserver(entries => {
            entries.forEach(i => {
                let args = [i.target, i.target.spy, i];
                if (i.isIntersecting) {
                    if (i.intersectionRatio === 0) {
                        i.target.spy.state = 'show';
                        super.listen({ name: 'show', params: [...args] });
                    }
                    else if (i.intersectionRatio === 1) {
                        i.target.spy.state = 'shown';
                        super.listen({ name: 'shown', params: [...args] });
                        this.setActiveOnly(i.target);
                        !this.options.repeat && this.interactIns.unobserve(i.target);
                    }
                    else {
                        i.target.spy.state = 'showing';
                        super.listen({ name: 'showing', params: [...args] });
                        if (i.target.spy?.in) {
                            classes.add(i.target, i.target.spy?.in, (name) => {
                                return i.target.classList.contains(name) ? false : true;
                            });
                            ` `;
                        }
                    }
                    super.listen({ name: 'in', params: [...args] });
                }
                else {
                    i.target.spy.state = 'hidden';
                    if (this.options.repeat) {
                        debounce(() => {
                            if (i.target.spy?.out) {
                                classes.add(i.target, i.target.spy?.out);
                            }
                            if (i.target.spy?.in) {
                                classes.remove(i.target, i.target.spy?.in);
                            }
                            if (this.active) {
                                i.target.spy.wing.forEach((k) => {
                                    this.activeIsAttr ? k.removeAttribute(this.active[0]) : classes.remove(k, this.active);
                                });
                            }
                        }, this.options.intvl)();
                    }
                    super.listen({ name: 'out', params: [...args] });
                }
            });
        }, this.interactOpt());
        if (this.options.spySel) {
            this.items = [...this.targetEl.querySelectorAll(this.options.spySel)];
        }
        else {
            this.items.push(this.targetEl);
        }
        let flag = this.options.onBefore ? this.options.onBefore.call(this, this.items) : true;
        if (flag) {
            this.items.forEach((k) => {
                this.setSpyAttr(k);
                this.interactIns.observe(k);
            });
        }
        super.listen({ name: 'init', cb });
        return this;
    }
    getActive() {
        let activeTrim = this.options.active.trim();
        if (activeTrim.startsWith('[') && activeTrim.endsWith(']')) {
            this.activeIsAttr = true;
            this.active = activeTrim.replace('[', '').replace(']', '').replace(' ', '').split('=');
        }
        else {
            this.activeIsAttr = false;
            this.active = this.options.active;
        }
    }
    setActiveOnly(el) {
        if (!isEmpty(this.active)) {
            el.spy.wing.forEach((k) => {
                if (this.activeIsAttr) {
                    console.log(this.active);
                    if (this.active.length > 0) {
                        k.setAttribute(this.active[0], this.active[1] || '');
                    }
                }
                else {
                    classes.add(k, this.active, (name) => {
                        return k.classList.contains(name) ? false : true;
                    });
                }
            });
            let other = this.items.filter((k) => k !== el && k.spy), otherWings = other.map((k) => k.spy.wing).flat(Infinity);
            otherWings.forEach((k) => {
                this.activeIsAttr ? k.removeAttribute(this.active[0]) : classes.remove(k, this.active);
            });
        }
    }
    setSpyAttr(el) {
        if (!getDataType(el).includes('HTML')) {
            return false;
        }
        let attrs = attrToJson(el, 'spy');
        el.spy = {
            observed: true,
            wing: this.getWings(el),
            in: attrs.in || this.options.in,
            out: attrs.out || this.options.out,
        };
    }
    getWings(el) {
        let arrFromId = [], arrFromSpy = [], arrFromOpt = [], wingArr = [];
        if (el.id) {
            if (this.subEls.length > 0 && this.subEls[0].hasAttribute('href')) {
                let navWing = this.subEls.find((k) => k.getAttribute('href') === `#${k.id}`);
                navWing ? arrFromId.push(navWing) : null;
            }
            else {
                arrFromId = [...document.querySelectorAll(`[href='#${el.id}']`)];
            }
        }
        else {
            let index = this.items.findIndex((k) => k == el);
            index !== -1 && this.subEls[index] ? arrFromId.push(this.subEls[index]) : null;
        }
        if (el.hasAttribute('spy')) {
            let attrs = attrToJson(el, 'spy');
            if (attrs.wing) {
                arrFromSpy = allToEls(attrs.wing);
            }
        }
        if (this.options.wing) {
            arrFromOpt = allToEls(this.options.wing);
        }
        wingArr = [...arrFromId, ...arrFromSpy, ...arrFromOpt].filter(Boolean);
        wingArr.forEach(k => {
            k.onclick = (e) => {
                preventDft(e);
                scrollTo(el, { root: this.rootEl, duration: this.options.duration, offset: this.options.offset });
            };
        });
        return wingArr;
    }
    interactOpt() {
        this.margin = this.options.margin.replace(/\s*/g, "").split(",");
        if (this.margin.length == 1) {
            this.margin.push(...this.margin);
        }
        else if (this.margin.length > 2) {
            this.margin.splice(0, 2);
        }
        return {
            root: this.rootEl,
            rootMargin: this.margin[0] + ' 0px ' + this.margin[1] + ' 0px',
            threshold: this.options.threshold,
        };
    }
    getEls(data) {
        let dataType = getDataType(data), els = [];
        if (dataType === 'Array') {
            els = data.map((k) => getEl(k)).filter(Boolean);
        }
        else if (dataType === 'String') {
            els = allToEls(data).filter(Boolean);
        }
        return els;
    }
    add(data, cb) {
        if (this.destroyed) {
            return this;
        }
        let els = this.getEls(data);
        els.forEach((i) => {
            if (!i.spy) {
                this.setSpyAttr(i);
                this.interactIns.observe(i);
                !this.items.includes(i) ? this.items.push(i) : null;
            }
            else {
                new Message({
                    content: renderTpl(ax.lang.spy.isObserved, { src: i.dataset.src || i.src }),
                    iconShow: true,
                    status: 'info',
                }).show();
            }
        });
        super.listen({ name: 'add', cb, params: [els] });
        return this;
    }
    remove(data, cb) {
        if (this.destroyed) {
            return this;
        }
        let els = this.getEls(data);
        els.forEach((i) => {
            if (this.items.includes(i)) {
                if (i.spy) {
                    Reflect.deleteProperty(i, 'spy');
                    this.interactIns.unobserve(i);
                }
                else {
                    new Message({
                        content: renderTpl(ax.lang.spy.isUnobserved, { src: i.dataset.src || i.src }),
                        iconShow: true,
                        status: 'info',
                    }).show();
                }
            }
        });
        super.listen({ name: 'remove', cb, params: [els] });
        return this;
    }
    /**
     * @description Destroy instance and prohibit use.
     * @param {fn_cb_none} [cb] - Callback execution after destruction.
     * @returns {any} - Return the instance itself.
     */
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        this.interactIns.disconnect();
        this.items.forEach((k) => {
            k.spy.wing.forEach((i) => {
                i.onclick = null;
            });
        });
        this.destroyed = true;
        super.listen({ name: 'destroy', cb });
        return this;
    }
}
Spy.hostType = 'el';
export default Spy;
