import './picker';

/**
 * EmojiPopover — a reusable, body-mounted floating panel that holds the EmojiPicker. It is anchored to a
 * trigger button (positioned next to it, never covering the composer), has a header with a title + close
 * button, and dismisses on outside-click / Esc; it repositions on scroll & resize. One popover is open at
 * a time. Construct once with { onPick }, then call toggle($anchor) from the trigger's click. Reused by
 * the postbox now and by comments/chat in a later pass.
 */
peepso.class('EmojiPopover', function (name, peepso, $) {
	const cfg = window.peepsoemojidata || {};
	const LANG = cfg.lang || {};
	const SHELL = cfg.template || '';
	const GAP = 6;
	const MARGIN = 8;

	const esc = text => $('<div>').text(text == null ? '' : text).html();

	let openInstance = null; // only one popover open at a time across all surfaces

	return class {
		constructor(opts = {}) {
			this.onPick = opts.onPick || function () {};
			this.$el = null;
			this.$anchor = null;
			this.$backdrop = null;
			this.picker = null;

			this._onDocDown = this._onDocDown.bind(this);
			this._onKey = this._onKey.bind(this);
			this._reposition = this._reposition.bind(this);
		}

		_isMobile() {
			return window.matchMedia('(max-width: 739px)').matches;
		}

		isOpen() {
			return !!this.$el;
		}

		toggle($anchor) {
			this.isOpen() ? this.close() : this.open($anchor);
		}

		open($anchor) {
			if (this.$el) this.close(); // idempotent: re-open re-anchors cleanly (e.g. a different comment)
			if (openInstance && openInstance !== this) openInstance.close();

			this.$anchor = $($anchor);

			const html =
				'<div class="ps-emoji-popover" role="dialog">' +
				'<div class="ps-emoji-popover__header">' +
				`<span class="ps-emoji-popover__title">${esc(LANG.title)}</span>` +
				`<button type="button" class="ps-emoji-popover__close" data-ps="emoji-close" aria-label="${esc(LANG.close)}"><i class="pso-i-cross-small"></i></button>` +
				'</div>' +
				SHELL +
				'</div>';

			this.$el = $(html).appendTo(document.body);
			this.$el.on('click', '[data-ps=emoji-close]', e => {
				e.preventDefault();
				this.close();
			});
			// The postbox (and other composers) listen for mouseup on document to detect outside clicks
			// and collapse. Stop mouseup from leaving the popover so interactions inside it (clicking
			// search input, emoji cells, close button) never trigger that outside-click handler.
			this.$el.on('mouseup', e => e.stopPropagation());

			// On mobile show a backdrop below the drawer and lock body scroll.
			if (this._isMobile()) {
				this.$backdrop = $('<div class="ps-emoji-backdrop"></div>').insertBefore(this.$el);
				$(document.body).addClass('ps-emoji-drawer-open');
			}

			this.picker = new (peepso.class('EmojiPicker'))(this.$el, {
				onPick: g => this.onPick(g),
				onRender: () => { if (!this._isMobile()) this._position(); }
			});
			this.picker.open();

			if (!this._isMobile()) this._position();
			openInstance = this;

			// Defer so the click that opened us doesn't immediately dismiss via the document handler.
			setTimeout(() => {
				$(document).on('mousedown', this._onDocDown);
				$(document).on('keydown', this._onKey);
				// Capture phase so scrolling ANY ancestor (e.g. a modal body) repositions us — scroll doesn't
				// bubble. Native listener, not jQuery: jQuery's .on() treats a trailing `true` as event data,
				// not useCapture, which shifts the handler and throws "Cannot create property 'guid' on true".
				window.addEventListener('scroll', this._reposition, true);
				$(window).on('resize', this._reposition);
			}, 0);
		}

		close() {
			if (!this.$el) return;

			$(document).off('mousedown', this._onDocDown);
			$(document).off('keydown', this._onKey);
			window.removeEventListener('scroll', this._reposition, true);
			$(window).off('resize', this._reposition);

			if (this.$backdrop) {
				this.$backdrop.remove();
				this.$backdrop = null;
			}
			$(document.body).removeClass('ps-emoji-drawer-open');

			this.$el.remove();
			this.$el = null;
			this.picker = null;
			if (openInstance === this) openInstance = null;
		}

		_onDocDown(e) {
			if (!this.$el) return;
			if (this.$el[0].contains(e.target)) return;
			// Clicks on the anchor are handled by its own toggle — don't double-fire.
			if (this.$anchor && this.$anchor[0] && this.$anchor[0].contains(e.target)) return;
			this.close();
		}

		_onKey(e) {
			if (e.key === 'Escape' || e.keyCode === 27) this.close();
		}

		// Anchor beside the trigger. Prefer below (keeps the composer above uncovered); only flip above
		// when there's genuinely more room there. Cap the scrollable grid so the panel always fits the
		// viewport instead of overflowing the fold.
		_position() {
			if (!this.$el || !this.$anchor || !this.$anchor[0]) return;
			if (this._isMobile()) return; // CSS handles drawer placement on mobile

			const a = this.$anchor[0].getBoundingClientRect();
			const el = this.$el[0];
			const list = el.querySelector('.ps-emoji-picker__list');
			const vw = window.innerWidth;
			const vh = window.innerHeight;

			// Measure natural height (with the list unclamped) to know the chrome overhead.
			if (list) list.style.maxHeight = '';
			const fullH = el.offsetHeight;
			const listH = list ? list.offsetHeight : 0;
			const chrome = fullH - listH;

			const spaceBelow = vh - a.bottom - GAP - MARGIN;
			const spaceAbove = a.top - GAP - MARGIN;

			// Stay below unless it doesn't fit and above has more room.
			const placeAbove = fullH > spaceBelow && spaceAbove > spaceBelow;
			const side = placeAbove ? spaceAbove : spaceBelow;

			// Cap the grid so the whole panel fits the chosen side (grid scrolls); floor keeps it usable.
			if (list) {
				const maxListH = Math.max(120, Math.min(listH, side - chrome));
				list.style.maxHeight = maxListH + 'px';
			}

			const ph = el.offsetHeight;
			const pw = el.offsetWidth;
			let top = placeAbove ? a.top - ph - GAP : a.bottom + GAP;
			top = Math.max(MARGIN, Math.min(top, vh - ph - MARGIN));
			const left = Math.max(MARGIN, Math.min(a.left, vw - pw - MARGIN));

			el.style.left = Math.round(left) + 'px';
			el.style.top = Math.round(top) + 'px';
		}

		_reposition() {
			this._position();
		}
	};
});
