import { ContextConsumer } from "@videojs/element/context";
import { noop } from "@videojs/utils/function";

//#region src/store/container-mixin.ts
/**
* Create a mixin that consumes player context and auto-attaches media elements.
*
* @param context - Player context to consume from an ancestor provider.
*/
function createContainerMixin(context) {
	return (BaseClass) => {
		class PlayerContainerElement extends BaseClass {
			#detach = noop;
			#observer = null;
			#contextStore = null;
			constructor(...args) {
				super(...args);
				new ContextConsumer(this, {
					context,
					callback: (value) => {
						this.#contextStore = value ?? null;
						this.#attachMedia();
					},
					subscribe: true
				});
			}
			get store() {
				return this.#contextStore;
			}
			connectedCallback() {
				super.connectedCallback();
				this.#observer = new MutationObserver((records) => {
					if (records.some(hasMediaElement)) this.#attachMedia();
				});
				this.#observer.observe(this, {
					childList: true,
					subtree: true,
					attributes: true,
					attributeFilter: ["name"]
				});
				this.addEventListener("slotchange", this.#onSlotChange);
				this.#attachMedia();
			}
			disconnectedCallback() {
				super.disconnectedCallback();
				this.#observer?.disconnect();
				this.#observer = null;
				this.removeEventListener("slotchange", this.#onSlotChange);
				this.#detach();
			}
			#onSlotChange = () => {
				this.#attachMedia();
			};
			#getSlottedMedia() {
				const slot = this.querySelector("slot[name=\"media\"]");
				if (!slot) return null;
				for (const el of slot.assignedElements({ flatten: true })) if (isMediaElement(el)) return el;
				return null;
			}
			#findMediaElement() {
				const media = Array.from(this.children).find(isMediaElement);
				if (media) return media;
				return null;
			}
			#attachMedia() {
				const store = this.#contextStore ?? this.store;
				if (!store) return;
				const media = this.querySelector("video, audio") ?? this.#findMediaElement() ?? this.#getSlottedMedia();
				if (!media) {
					this.#detach();
					this.#detach = noop;
					return;
				}
				if (isCustomMediaElement(media)) globalThis.customElements?.upgrade?.(media);
				const target = {
					media,
					container: this
				};
				const hasMediaChanged = store.target?.media !== target.media, hasContainerChanged = store.target?.container !== target.container;
				if (hasMediaChanged || hasContainerChanged) {
					this.#detach();
					this.#detach = store.attach(target);
				}
			}
		}
		return PlayerContainerElement;
	};
}
function isMediaElement(node) {
	return node instanceof HTMLMediaElement || isCustomMediaElement(node);
}
function isCustomMediaElement(node) {
	return node instanceof HTMLElement && (node.localName.endsWith("-audio") || node.localName.endsWith("-video"));
}
function isMediaSlotElement(node) {
	return node instanceof HTMLSlotElement && node.name === "media";
}
function hasMediaElement(record) {
	if (isMediaSlotElement(record.target)) return true;
	for (const node of record.addedNodes) if (isMediaElement(node) || isMediaSlotElement(node)) return true;
	for (const node of record.removedNodes) if (isMediaElement(node) || isMediaSlotElement(node)) return true;
	return false;
}

//#endregion
export { createContainerMixin };
//# sourceMappingURL=container-mixin.js.map