/**
 * @param {HTMLElement} wrapper .dsm-readmore-wrapper
 * @return {{ desktop: string, tablet: string, phone: string }}
 */
function dsmReadmoreParseHeights(wrapper) {
	const raw = wrapper.getAttribute('data-readmore-heights');
	if (!raw) {
		const fallback = wrapper.getAttribute('data-height') || '100px';
		return { desktop: fallback, tablet: fallback, phone: fallback };
	}
	try {
		const o = JSON.parse(raw);
		const d = o.desktop || '100px';
		return {
			desktop: d,
			tablet: o.tablet || d,
			phone: o.phone || o.tablet || d,
		};
	} catch (e) {
		const fallback = wrapper.getAttribute('data-height') || '100px';
		return { desktop: fallback, tablet: fallback, phone: fallback };
	}
}

/**
 * Collapsed max-height for current viewport (matches Divi phone/tablet max-width defaults or theme settings).
 *
 * @param {HTMLElement} wrapper
 * @return {string}
 */
function dsmReadmoreGetActiveHeight(wrapper) {
	const h = dsmReadmoreParseHeights(wrapper);
	const phoneMax = parseInt(
		wrapper.getAttribute('data-readmore-phone-max') || '767',
		10
	);
	const tabletMax = parseInt(
		wrapper.getAttribute('data-readmore-tablet-max') || '980',
		10
	);
	const w = window.innerWidth;
	if (w <= phoneMax) {
		return h.phone;
	}
	if (w <= tabletMax) {
		return h.tablet;
	}
	return h.desktop;
}

function dsmReadmoreDebounce(fn, ms) {
	let t;
	return function debounced(...args) {
		clearTimeout(t);
		t = setTimeout(() => fn.apply(this, args), ms);
	};
}

/**
 * Element to align to viewport top: Read More module root, not a generic row module.
 *
 * @param {HTMLElement} wrapper .dsm-readmore-wrapper
 * @return {HTMLElement}
 */
function dsmReadmoreGetScrollTargetElement(wrapper) {
	return (
		wrapper.closest('[class*="dsm_readmore"]') ||
		wrapper.closest('.et_pb_module') ||
		wrapper
	);
}

/**
 * End in-flight smooth scrolling so the next scrollIntoView always runs (fixes "only first time").
 *
 * @param {HTMLElement} fromEl Start walking from this element's ancestors.
 */
function dsmReadmoreSnapOngoingScrolls(fromEl) {
	const root = document.scrollingElement || document.documentElement;
	const seen = new Set();

	let node = fromEl;
	while (node) {
		const { overflowY } = window.getComputedStyle(node);
		if (
			/(auto|scroll|overlay)/.test(overflowY) &&
			node.scrollHeight > node.clientHeight + 2 &&
			!seen.has(node)
		) {
			seen.add(node);
			node.scrollTo({
				left: node.scrollLeft,
				top: node.scrollTop,
				behavior: 'auto',
			});
		}
		if (node === root) {
			break;
		}
		node = node.parentElement;
	}

	if (!seen.has(root)) {
		root.scrollTo({
			left: root.scrollLeft,
			top: root.scrollTop,
			behavior: 'auto',
		});
	}

	window.scrollTo({
		left: window.scrollX,
		top: window.scrollY,
		behavior: 'auto',
	});
}

/**
 * Scroll so the Read More module top sits at the top of the visible scroll area.
 * Uses scrollIntoView so nested theme/Divi scroll containers update every time.
 *
 * @param {HTMLElement} wrapper .dsm-readmore-wrapper
 */
function dsmReadmoreScrollModuleToTop(wrapper) {
	const target = dsmReadmoreGetScrollTargetElement(wrapper);
	const adminBar = document.getElementById('wpadminbar');
	const adminOffset =
		adminBar && window.getComputedStyle(adminBar).position === 'fixed'
			? adminBar.offsetHeight
			: 0;

	const prevMargin = target.style.scrollMarginTop;
	target.style.scrollMarginTop =
		adminOffset > 0 ? `${adminOffset}px` : '';

	dsmReadmoreSnapOngoingScrolls(target);

	window.requestAnimationFrame(() => {
		window.requestAnimationFrame(() => {
			target.scrollIntoView({
				block: 'start',
				inline: 'nearest',
				behavior: 'smooth',
			});

			window.setTimeout(() => {
				target.style.scrollMarginTop = prevMargin;
			}, 700);
		});
	});
}

class DSMReadMoreModule {
	constructor() {
		this.wrappers = document.querySelectorAll('.dsm-readmore-wrapper');
		this.onResize = dsmReadmoreDebounce(() => this.applyResponsiveHeights(), 150);
		this.init();
	}

	init() {
		if (!this.wrappers.length) {
			return;
		}

		this.wrappers.forEach((wrapper) => {
			this.setupModule(wrapper);
		});

		window.addEventListener('resize', this.onResize);
	}

	/**
	 * Update max-height when viewport or expanded state changes.
	 */
	applyResponsiveHeights() {
		this.wrappers.forEach((wrapper) => {
			if (wrapper.classList.contains('dsm-expanded')) {
				wrapper.style.maxHeight = `${wrapper.scrollHeight}px`;
			} else {
				wrapper.style.maxHeight = dsmReadmoreGetActiveHeight(wrapper);
			}
		});
	}

	setupModule(wrapper) {
		const settings = {
			isEnabled: wrapper.dataset.showReadmore === 'on',
			closeText: wrapper.dataset.closeText || 'Read Less',
			openText: wrapper.dataset.openText || 'Read More',
		};

		if (!settings.isEnabled) {
			return;
		}

		const btnWrapper = wrapper.nextElementSibling;

		if (
			!btnWrapper ||
			!btnWrapper.classList.contains('dsm_button_wrapper')
		) {
			return;
		}

		const btn = btnWrapper.querySelector('.dsm-readmore-button');
		if (!btn) {
			return;
		}

		btn.innerText = settings.closeText;

		btn.classList.add('dsm-btn-close');
		btn.classList.remove('dsm-btn-open');

		wrapper.style.overflow = 'hidden';
		wrapper.style.transition = 'max-height 0.5s ease-in-out';

		this.setInitialHeight(wrapper);

		btn.addEventListener('click', (e) => {
			e.preventDefault();
			this.toggleContent(wrapper, btn, settings);
		});
	}

	setInitialHeight(wrapper) {
		if (!wrapper.classList.contains('dsm-expanded')) {
			wrapper.style.maxHeight = dsmReadmoreGetActiveHeight(wrapper);
		}
	}

	toggleContent(wrapper, btn, settings) {
		const isExpanded = wrapper.classList.contains('dsm-expanded');

		if (isExpanded) {
			wrapper.style.maxHeight = dsmReadmoreGetActiveHeight(wrapper);
			wrapper.classList.remove('dsm-expanded');

			btn.classList.remove('dsm-btn-open');
			btn.classList.add('dsm-btn-close');

			btn.innerText = settings.closeText;

			const scrollOnCollapse =
				wrapper.getAttribute('data-collapse-scroll-to-top') === 'on';
			if (scrollOnCollapse) {
				/* Wait for max-height transition (fixed delay is unreliable on repeat toggles). */
				let scrolled = false;
				const runScroll = () => {
					if (scrolled) {
						return;
					}
					scrolled = true;
					wrapper.removeEventListener('transitionend', onTransitionEnd);
					window.clearTimeout(fallbackId);
					window.requestAnimationFrame(() => {
						dsmReadmoreScrollModuleToTop(wrapper);
					});
				};
				const onTransitionEnd = (event) => {
					if (
						event.target !== wrapper ||
						event.propertyName !== 'max-height'
					) {
						return;
					}
					runScroll();
				};
				wrapper.addEventListener('transitionend', onTransitionEnd);
				const fallbackId = window.setTimeout(runScroll, 650);
			}
		} else {
			const scrollHeight = wrapper.scrollHeight;
			wrapper.style.maxHeight = scrollHeight + 'px';
			wrapper.classList.add('dsm-expanded');

			btn.classList.remove('dsm-btn-close');
			btn.classList.add('dsm-btn-open');

			btn.innerText = settings.openText;
		}
	}
}

document.addEventListener('DOMContentLoaded', () => {
	if (
		typeof document !== 'undefined' &&
		document.body?.classList?.contains('et-fb')
	) {
		return;
	}
	new DSMReadMoreModule();
});
