(function () {
	'use strict';

	class StickyHeader {
		constructor() {
			this.headers = document.querySelectorAll('.js-header');
			if (!this.headers.length) return;

			this.lastScrollY = window.pageYOffset;
			this.ticking = false;
			this.threshold = 10;
			this.extraOffset = 100;
			this.headerData = [];

			this.init();
		}

		init() {
			this.bindEvents();

			window.requestAnimationFrame(() => {
				const currentScrollY = window.scrollY || window.pageYOffset || document.documentElement.scrollTop;
				this.updateCache();
				this.applyState(currentScrollY);
			});
		}

		/**
		 * Cache header heights and states to prevent layout thrashing.
		 */
		updateCache() {
			// 1. Batch READS
			const updates = Array.from(this.headers).map(header => {
				return {
					el: header,
					height: header.offsetHeight,
					stickyMode: header.getAttribute('data-sticky') || 'down',
					isSticky: header.classList.contains('is-sticky') // Initial state
				};
			});

			// 2. Batch WRITES
			if (updates.length > 0) {
				document.documentElement.style.setProperty('--header-height', updates[0].height + 'px');
				updates.forEach(data => {
					data.el.dataset.height = data.height;
				});
			}

			this.headerData = updates;
		}

		bindEvents() {
			window.addEventListener('scroll', () => {
				if (!this.ticking) {
					window.requestAnimationFrame(() => {
						this.onScroll();
						this.ticking = false;
					});
					this.ticking = true;
				}
			}, { passive: true });

			window.addEventListener('resize', () => {
				clearTimeout(this.resizeTimer);
				this.resizeTimer = setTimeout(() => {
					window.requestAnimationFrame(() => this.updateCache());
				}, 200);
			}, { passive: true });
		}

		onScroll() {
			const currentScrollY = window.scrollY || window.pageYOffset || document.documentElement.scrollTop;
			this.applyState(currentScrollY);
			this.lastScrollY = currentScrollY;
		}

		/**
		 * Optimized state application using Read-first, Write-last pattern.
		 * Minimizes forced reflows by batching class changes and using memory cache.
		 */
		applyState(currentScrollY) {
			const isScrollingUp = currentScrollY < this.lastScrollY;

			// 1. CALCULATION PHASE (READ from cache)
			const updates = this.headerData.map(data => {
				const headerHeight = data.height;
				const stickyMode = data.stickyMode;

				const shouldBeSticky = (stickyMode === 'up' && isScrollingUp && currentScrollY > headerHeight + this.extraOffset) ||
					(stickyMode === 'down' && currentScrollY > headerHeight + this.extraOffset);

				let action = 'keep';
				if (currentScrollY <= 0) {
					action = 'reset';
				} else if (shouldBeSticky) {
					action = 'add';
				} else if (stickyMode === 'up' && !isScrollingUp && currentScrollY > headerHeight) {
					action = 'out';
				} else if (currentScrollY <= headerHeight) {
					action = 'reset';
				}

				return { data, action, isScrollingUp };
			});

			// 2. EXECUTION PHASE (WRITE to DOM)
			updates.forEach(({ data, action, isScrollingUp }) => {
				const header = data.el;

				if (action === 'add') {
					if (!data.isSticky) {
						if (header.stickyTimeout) {
							clearTimeout(header.stickyTimeout);
							header.stickyTimeout = null;
						}
						header.classList.add('is-sticky');
						header.classList.remove('is-sticky-out');
						data.isSticky = true;

						requestAnimationFrame(() => {
							if (data.isSticky) header.classList.add('is-sticky-active');
						});
					}
					header.classList.add(isScrollingUp ? 'is-scrolling-up' : 'is-scrolling-down');
					header.classList.remove(isScrollingUp ? 'is-scrolling-down' : 'is-scrolling-up');

				} else if (action === 'out') {
					if (data.isSticky && !header.classList.contains('is-sticky-out')) {
						header.classList.add('is-sticky-out');
						header.classList.remove('is-scrolling-up', 'is-scrolling-down');

						if (header.stickyTimeout) clearTimeout(header.stickyTimeout);
						header.stickyTimeout = setTimeout(() => {
							header.classList.remove('is-sticky', 'is-sticky-out', 'is-sticky-active');
							data.isSticky = false;
							header.stickyTimeout = null;
						}, 600);
					}
				} else if (action === 'reset') {
					if (data.isSticky || header.classList.contains('is-sticky-out')) {
						if (header.stickyTimeout) {
							clearTimeout(header.stickyTimeout);
							header.stickyTimeout = null;
						}
						header.classList.remove('is-sticky', 'is-sticky-out', 'is-sticky-active', 'is-scrolling-up', 'is-scrolling-down');
						data.isSticky = false;
					}
				}
			});
		}
	}

	document.addEventListener('DOMContentLoaded', () => {
		new StickyHeader();
	});
})();
