import btn from '../../../elements/btn.hbs';

// ===== HELPERS =====

function normalizeOptions(options) {
    return {
        groupSel: '.accordion.filter',
        panelSel: '.accordion__content-inner',
        itemSel: '.filter__item',

        ctaAppendToSel: '.accordion__content-inner',
        ctaClass: 'filter__facet-cta',
        ctaAction: 'more',
        ctaActionAttr: 'data-action',

        toggleSel: '.accordion__header',

        stateKey: 'showMoreLessState',
        stepAttr: 'data-facet-step',
        defaultStep: 5,

        excludeGroup: null,

        moreText: 'Show more',
        lessText: 'Show less',

        ...options,
    };
}

function makeSelectors(opt) {
    return {
        GROUP: opt.groupSel,
        PANEL: opt.panelSel,
        ITEM: opt.itemSel,
        TOGGLE: opt.toggleSel || null,

        CTA: `.${opt.ctaClass}`,
        CTA_APPEND_TO: opt.ctaAppendToSel,
    };
}

function isExcluded($group, excludeFn) {
    return typeof excludeFn === 'function' ? !!excludeFn($group) : false;
}

function getPanel($group, sel) {
    return $group.find(sel.PANEL).first();
}

function getItems($group, sel) {
    const $panel = getPanel($group, sel);
    if (!$panel.length) return $();

    const $direct = $panel.children(sel.ITEM);
    return $direct.length ? $direct : $panel.find(sel.ITEM);
}

function getStep($group, stepAttr, defaultStep) {
    const raw = ($group.attr(stepAttr) || '').trim();
    const n = parseInt(raw, 10);
    return Number.isFinite(n) && n > 0 ? n : defaultStep;
}

function getState($group, stateKey, total, step) {
    let state = $group.data(stateKey);

    if (!state) state = { visible: step, total };

    state.total = total;

    if (!state.visible || state.visible < step) state.visible = step;
    if (state.visible > total) state.visible = total;

    $group.data(stateKey, state);
    return state;
}

function setInitialState($group, stateKey, total, step) {
    $group.data(stateKey, { visible: step, total });
}

function getGroups($root, groupSel) {
    const $scope = $root && $root.length ? $root : $(document);
    const $self = $scope.is(groupSel) ? $scope : $();
    const $found = $scope.find(groupSel);
    return $self.add($found);
}

// ===== CTA helpers =====

function getCta($group, sel) {
    return $group.find(sel.CTA).first();
}

function removeCta($group, sel) {
    const $cta = getCta($group, sel);
    if ($cta.length) $cta.remove();
}

function ensureCta($group, sel, opt) {
    let $cta = getCta($group, sel);
    if ($cta.length) return $cta;

    const $appendTo = $group.find(sel.CTA_APPEND_TO).first();
    if (!$appendTo.length) return $();

    const html = btn({
        text: opt.moreText,
        class: opt.ctaClass,
        action: opt.ctaAction,
    });

    $appendTo.append(html);

    $cta = getCta($group, sel);

    if ($cta.length && !$cta.attr(opt.ctaActionAttr)) {
        $cta.attr(opt.ctaActionAttr, opt.ctaAction);
    }

    return $cta;
}

function setCtaState($cta, mode, opt) {
    if (!$cta || !$cta.length) return;

    if (mode === 'less') {
        $cta.text(opt.lessText).attr(opt.ctaActionAttr, 'less').show();
    } else {
        $cta.text(opt.moreText).attr(opt.ctaActionAttr, 'more').show();
    }
}

// ===== CORE ACTIONS =====

function applyGroup($group, opt, sel) {
    if (!$group || !$group.length) return;

    // excluded -> CTA never exists
    if (isExcluded($group, opt.excludeGroup)) {
        removeCta($group, sel);
        return;
    }

    const $items = getItems($group, sel);
    const total = $items.length;
    const step = getStep($group, opt.stepAttr, opt.defaultStep);

    // 0 items -> no CTA
    if (total === 0) {
        removeCta($group, sel);
        return;
    }

    const state = getState($group, opt.stateKey, total, step);

    // show/hide items
    $items.each(function (idx) {
        $(this).toggle(idx < state.visible);
    });

    // <= step -> CTA not needed
    if (total <= step) {
        removeCta($group, sel);
        return;
    }

    // > step -> ensure CTA exists and update it
    const $cta = ensureCta($group, sel, opt);
    if (!$cta.length) return;

    if (state.visible >= total) setCtaState($cta, 'less', opt);
    else setCtaState($cta, 'more', opt);
}

function expandGroup($group, opt, sel) {
    if (!$group || !$group.length) return;
    if (isExcluded($group, opt.excludeGroup)) return;

    const total = getItems($group, sel).length;
    if (!total) return;

    const step = getStep($group, opt.stepAttr, opt.defaultStep);
    const state = getState($group, opt.stateKey, total, step);

    state.visible = Math.min(state.visible + step, total);
    $group.data(opt.stateKey, state);

    applyGroup($group, opt, sel);
}

function collapseGroupToInitial($group, opt, sel) {
    if (!$group || !$group.length) return;

    if (isExcluded($group, opt.excludeGroup)) {
        removeCta($group, sel);
        return;
    }

    const total = getItems($group, sel).length;
    const step = getStep($group, opt.stepAttr, opt.defaultStep);

    setInitialState($group, opt.stateKey, total, step);
    applyGroup($group, opt, sel);
}

// ===== MAIN FACTORY =====

export function createShowMoreLess(options = {}) {
    const OPT = normalizeOptions(options);
    const SEL = makeSelectors(OPT);

    if (!SEL.GROUP || !SEL.PANEL || !SEL.ITEM) {
        throw new Error('[createShowMoreLess] Missing required selectors');
    }

    // namespace for delegated events (avoid duplicates if factory created multiple times)
    const NS = `.sml_${Math.random().toString(16).slice(2)}`;

    function init($root) {
        getGroups($root, SEL.GROUP).each(function () {
            const $group = $(this);

            if (isExcluded($group, OPT.excludeGroup)) {
                removeCta($group, SEL);
                return;
            }

            const total = getItems($group, SEL).length;
            const step = getStep($group, OPT.stepAttr, OPT.defaultStep);

            setInitialState($group, OPT.stateKey, total, step);
            applyGroup($group, OPT, SEL);
        });
    }

    function resetAll($root) {
        getGroups($root, SEL.GROUP).each(function () {
            collapseGroupToInitial($(this), OPT, SEL);
        });
    }

    // CTA click
    $(document)
        .off(`click${NS}`, SEL.CTA)
        .on(`click${NS}`, SEL.CTA, function (e) {
            e.preventDefault();

            const $cta = $(this);
            const $group = $cta.closest(SEL.GROUP);

            if (!$group.length || isExcluded($group, OPT.excludeGroup)) return;

            const action = ($cta.attr(OPT.ctaActionAttr) || 'more').toLowerCase();

            if (action === 'less') collapseGroupToInitial($group, OPT, SEL);
            else expandGroup($group, OPT, SEL);
        });

    // Toggle/header click -> collapse to initial
    if (SEL.TOGGLE) {
        $(document)
            .off(`click${NS}`, SEL.TOGGLE)
            .on(`click${NS}`, SEL.TOGGLE, function () {
                const $group = $(this).closest(SEL.GROUP);
                if (!$group.length || isExcluded($group, OPT.excludeGroup)) return;
                collapseGroupToInitial($group, OPT, SEL);
            });
    }

    return { init, resetAll };
}
