'use strict';

import * as chai from 'chai';
import { getDocument } from "../../../../source/dom/util.mjs";
import { chaiDom } from "../../../util/chai-dom.mjs";
import { initJSDOM } from "../../../util/jsdom.mjs";

let expect = chai.expect;
chai.use(chaiDom);

function waitForCondition(check, { timeout = 4000, interval = 25 } = {}) {
	const startedAt = Date.now();
	return new Promise((resolve, reject) => {
		const tick = () => {
			try {
				if (check()) {
					resolve();
					return;
				}
			} catch (error) {
				reject(error);
				return;
			}
			if (Date.now() - startedAt >= timeout) {
				reject(new Error('condition timed out'));
				return;
			}
			setTimeout(tick, interval);
		};
		tick();
	});
}

describe('TableOfContent', function () {
	this.timeout(10000);

	let document;

	before(async function () {
		await initJSDOM({});
		globalThis.IntersectionObserver = class {
			observe() {}
			disconnect() {}
			unobserve() {}
		};
		globalThis.HTMLHeadingElement ??= globalThis.HTMLElement;
		await import("../../../../source/components/navigation/table-of-content.mjs");
		document = getDocument();
	});

	beforeEach(() => {
		let mocks = document.getElementById('mocks');
		mocks.innerHTML = '<div id="target"></div>';
	});

	afterEach(() => {
		let mocks = document.getElementById('mocks');
		mocks.innerHTML = '';
	});

	it('should bind to the first actually scrollable parent', async function () {
		const target = document.getElementById('target');
		const outerScroller = document.createElement('div');
		const innerWrapper = document.createElement('div');
		const tableOfContent = document.createElement('monster-table-of-content');
		const heading = document.createElement('h2');
		const paragraph = document.createElement('p');

		heading.textContent = 'Introduction';
		paragraph.textContent = 'Content';
		tableOfContent.appendChild(heading);
		tableOfContent.appendChild(paragraph);

		Object.defineProperties(outerScroller, {
			clientHeight: { configurable: true, value: 200 },
			scrollHeight: { configurable: true, value: 600 },
			clientWidth: { configurable: true, value: 300 },
			scrollWidth: { configurable: true, value: 300 },
		});

		Object.defineProperties(innerWrapper, {
			clientHeight: { configurable: true, value: 400 },
			scrollHeight: { configurable: true, value: 400 },
			clientWidth: { configurable: true, value: 300 },
			scrollWidth: { configurable: true, value: 300 },
		});

		outerScroller.style.overflowY = 'auto';
		innerWrapper.style.overflowY = 'auto';

		outerScroller.getBoundingClientRect = () => ({
			top: 0,
			bottom: 200,
			left: 0,
			right: 300,
			width: 300,
			height: 200,
			x: 0,
			y: 0,
		});

		tableOfContent.getBoundingClientRect = () => ({
			top: outerScroller.scrollTop ? -20 : 100,
			bottom: outerScroller.scrollTop ? 80 : 200,
			left: 0,
			right: 300,
			width: 300,
			height: 100,
			x: 0,
			y: outerScroller.scrollTop ? -20 : 100,
		});

		innerWrapper.appendChild(tableOfContent);
		outerScroller.appendChild(innerWrapper);
		target.appendChild(outerScroller);

		const navigation = tableOfContent.shadowRoot.querySelector('.navigation');

		await waitForCondition(() => navigation.style.top === '50px');

		outerScroller.scrollTop = 120;
		outerScroller.dispatchEvent(new window.Event('scroll'));

		await waitForCondition(() => navigation.style.top === '170px');
	});
});
