import * as chai from "chai";
import { initJSDOM } from "../../../util/jsdom.mjs";

let expect = chai.expect;

let resolveClippingBoundaryElement;
let applyAdaptiveFloatingElementSize;

describe("form floating-ui boundary resolution", function () {
	before(function (done) {
		initJSDOM()
			.then(() => {
				return import(
					"../../../../source/components/form/util/floating-ui.mjs"
				);
			})
			.then((m) => {
				resolveClippingBoundaryElement = m.resolveClippingBoundaryElement;
				applyAdaptiveFloatingElementSize = m.applyAdaptiveFloatingElementSize;
				done();
			})
			.catch((e) => done(e));
	});

	afterEach(() => {
		const mocks = document.getElementById("mocks");
		mocks.innerHTML = "";
	});

	it("should ignore parent popper content wrappers as clipping boundaries for nested controls", function () {
		const mocks = document.getElementById("mocks");
		const wrapper = document.createElement("div");
		const popperHost = document.createElement("div");
		const selectHost = document.createElement("div");

		wrapper.style.overflow = "hidden";
		mocks.appendChild(wrapper);
		wrapper.appendChild(popperHost);

		const popperShadow = popperHost.attachShadow({ mode: "open" });
		popperShadow.innerHTML = `
            <div data-monster-role="popper">
                <div part="content"
                     data-monster-overflow-mode="both"
                     style="overflow: auto;">
                </div>
            </div>
        `;

		popperShadow.querySelector('[part="content"]').appendChild(selectHost);

		const selectShadow = selectHost.attachShadow({ mode: "open" });
		selectShadow.innerHTML = `
            <div data-monster-role="control"></div>
            <div data-monster-role="popper"></div>
        `;

		const control = selectShadow.querySelector('[data-monster-role="control"]');
		const popper = selectShadow.querySelector('[data-monster-role="popper"]');

		expect(resolveClippingBoundaryElement(control, popper)).to.equal(wrapper);
	});

	it("should adapt the content max height to the available popper height", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const header = document.createElement("div");
		const content = document.createElement("div");
		const footer = document.createElement("div");

		popper.style.maxHeight = "300px";
		content.style.maxHeight = "240px";
		content.setAttribute("part", "content");

		popper.appendChild(header);
		popper.appendChild(content);
		popper.appendChild(footer);
		mocks.appendChild(popper);

		header.getBoundingClientRect = () => {
			return {
				width: 180,
				height: 30,
				top: 0,
				left: 0,
				right: 180,
				bottom: 30,
				x: 0,
				y: 0,
			};
		};
		popper.getBoundingClientRect = () => {
			return {
				width: 220,
				height: 200,
				top: 0,
				left: 0,
				right: 220,
				bottom: 200,
				x: 0,
				y: 0,
			};
		};
		footer.getBoundingClientRect = () => {
			return {
				width: 180,
				height: 30,
				top: 0,
				left: 0,
				right: 180,
				bottom: 30,
				x: 0,
				y: 0,
			};
		};
		content.getBoundingClientRect = () => {
			return {
				width: 180,
				height: 140,
				top: 0,
				left: 0,
				right: 180,
				bottom: 140,
				x: 0,
				y: 0,
			};
		};

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 220,
			availableHeight: 160,
		});

		expect(popper.style.maxHeight).to.equal("160px");
		expect(content.style.maxWidth).to.equal("");
		expect(content.style.maxHeight).to.equal("100px");
	});

	it("should not clamp the floating element height when content overflow is visible", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const content = document.createElement("div");

		popper.style.maxHeight = "300px";
		content.style.maxHeight = "240px";
		content.setAttribute("part", "content");
		content.setAttribute("data-monster-overflow-mode", "visible");

		popper.appendChild(content);
		mocks.appendChild(popper);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 220,
			availableHeight: 160,
		});

		expect(popper.style.maxHeight).to.equal("");
		expect(content.style.maxHeight).to.equal("240px");
	});

	it("should keep at least one readable line for scrollable content", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const content = document.createElement("div");

		popper.style.maxHeight = "300px";
		content.setAttribute("part", "content");
		content.textContent = "A long help text that still needs one readable line.";
		content.style.fontSize = "16px";
		content.style.lineHeight = "24px";
		popper.appendChild(content);
		mocks.appendChild(popper);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 220,
			availableHeight: 10,
		});

		expect(content.style.maxHeight).to.equal("24px");
	});

	it("should use the first slotted element line height for the minimum readable size", function () {
		const mocks = document.getElementById("mocks");
		const popperHost = document.createElement("div");
		const slottedParagraph = document.createElement("p");
		slottedParagraph.textContent = "Readable help line";
		slottedParagraph.style.lineHeight = "26px";

		mocks.appendChild(popperHost);
		const shadowRoot = popperHost.attachShadow({ mode: "open" });
		shadowRoot.innerHTML = `
			<div data-monster-role="popper">
				<div part="content">
					<slot></slot>
				</div>
			</div>
		`;

		const popper = shadowRoot.querySelector('[data-monster-role="popper"]');
		const content = shadowRoot.querySelector('[part="content"]');
		popperHost.appendChild(slottedParagraph);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 220,
			availableHeight: 10,
		});

		expect(content.style.maxHeight).to.equal("26px");
	});

	it("should respect a smaller nested scroll container height", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const content = document.createElement("div");
		const options = document.createElement("div");

		content.setAttribute("part", "content");
		content.style.overflowY = "hidden";
		options.style.overflowY = "auto";
		options.style.height = "72px";
		options.style.maxHeight = "72px";
		Object.defineProperty(options, "scrollHeight", {
			configurable: true,
			value: 72,
		});

		content.appendChild(options);
		popper.appendChild(content);
		mocks.appendChild(popper);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 220,
			availableHeight: 180,
		});

		expect(content.style.maxHeight).to.equal("180px");
		expect(options.style.height).to.equal("72px");
		expect(options.style.maxHeight).to.equal("72px");
	});

	it("should keep a preferred-width popper stable instead of growing with content", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const header = document.createElement("div");
		const content = document.createElement("div");
		const options = document.createElement("div");

		popper.dataset.monsterPreferredWidth = "240";
		popper.dataset.monsterWidthBehavior = "preferred";
		popper.style.maxWidth = "600px";
		content.setAttribute("part", "content");
		options.style.overflowY = "auto";

		Object.defineProperty(content, "scrollWidth", {
			configurable: true,
			value: 620,
		});
		Object.defineProperty(options, "scrollWidth", {
			configurable: true,
			value: 620,
		});

		header.getBoundingClientRect = () => {
			return {
				width: 240,
				height: 24,
				top: 0,
				left: 0,
				right: 240,
				bottom: 24,
				x: 0,
				y: 0,
			};
		};
		content.getBoundingClientRect = () => {
			return {
				width: 220,
				height: 120,
				top: 0,
				left: 0,
				right: 220,
				bottom: 120,
				x: 0,
				y: 0,
			};
		};
		popper.getBoundingClientRect = () => {
			return {
				width: 240,
				height: 160,
				top: 0,
				left: 0,
				right: 240,
				bottom: 160,
				x: 0,
				y: 0,
			};
		};

		content.appendChild(options);
		popper.appendChild(header);
		popper.appendChild(content);
		mocks.appendChild(popper);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 600,
			availableHeight: 200,
		});

		expect(popper.style.width).to.equal("240px");
		expect(popper.style.minWidth).to.equal("240px");
	});

	it("should constrain horizontal overlay-aware content on the inline axis", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const content = document.createElement("div");

		content.setAttribute("part", "content");
		content.setAttribute("data-monster-overflow-mode", "horizontal");

		Object.defineProperty(content, "scrollHeight", {
			configurable: true,
			value: 120,
		});

		popper.appendChild(content);
		mocks.appendChild(popper);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 240,
			availableHeight: 200,
		});

		expect(content.style.overflowX).to.equal("auto");
		expect(content.style.overflowY).to.equal("visible");
		expect(content.style.height).to.equal("");
		expect(content.style.maxHeight).to.equal("200px");
	});

	it("should preserve declared nested scroll height when horizontal overlay content fits", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const content = document.createElement("div");
		const options = document.createElement("div");

		content.setAttribute("part", "content");
		content.setAttribute("data-monster-overflow-mode", "horizontal");
		options.style.overflowY = "auto";
		options.style.height = "72px";
		options.style.maxHeight = "72px";

		Object.defineProperty(content, "scrollHeight", {
			configurable: true,
			value: 120,
		});
		Object.defineProperty(options, "scrollHeight", {
			configurable: true,
			value: 72,
		});

		content.appendChild(options);
		popper.appendChild(content);
		mocks.appendChild(popper);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 240,
			availableHeight: 200,
		});

		expect(options.style.height).to.equal("72px");
		expect(options.style.maxHeight).to.equal("72px");
	});

	it("should constrain horizontal overlay-aware content height only when it exceeds available height", function () {
		const mocks = document.getElementById("mocks");
		const popper = document.createElement("div");
		const content = document.createElement("div");

		content.setAttribute("part", "content");
		content.setAttribute("data-monster-overflow-mode", "horizontal");

		Object.defineProperty(content, "scrollHeight", {
			configurable: true,
			value: 320,
		});

		popper.appendChild(content);
		mocks.appendChild(popper);

		applyAdaptiveFloatingElementSize(popper, {
			availableWidth: 240,
			availableHeight: 200,
		});

		expect(content.style.overflowX).to.equal("auto");
		expect(content.style.overflowY).to.equal("auto");
		expect(content.style.height).to.equal("200px");
		expect(content.style.maxHeight).to.equal("200px");
	});
});
