// assets/js/viewer.js
(() => {
	const video = document.getElementById("wpsl-video");
	const logEl = document.getElementById("wpsl-log");
	const messagesEl = document.getElementById("wpsl-messages");
	const nameEl = document.getElementById("wpsl-name");
	const msgEl = document.getElementById("wpsl-msg");

    let lastId = 0;
    let hls = null;
    const chatEnabled = !!(window.WPSL_VIEW && Number(WPSL_VIEW.chat_enabled));
    const canModerate = !!(window.WPSL_VIEW && Number(WPSL_VIEW.can_moderate));
    const usersVisible = !!(window.WPSL_VIEW && Number(WPSL_VIEW.users_visible));
    const initialName = (window.WPSL_VIEW && window.WPSL_VIEW.user_name) || '';
    const emojiBtn = document.getElementById('wpsl-emoji-btn');
    const emojiPopup = document.getElementById('wpsl-emoji-popup');
    try { if (msgEl) msgEl.placeholder = 'Message...'; } catch {}
    try { if (emojiBtn) emojiBtn.textContent = '😊'; } catch {}
    // Populate emoji popup to avoid encoding issues from HTML
    try {
        if (emojiPopup) {
            const EMOJIS = [
                "\u{1F642}", // 🙂
                "\u{1F60A}", // 😊
                "\u{1F603}", // 😃
                "\u{1F602}", // 😂
                "\u{1F44D}", // 👍
                "\u{1F44F}", // 👏
                "\u{2764}\u{FE0F}", // ??
                "\u{1F389}", // 🎉
            ];
            emojiPopup.innerHTML = EMOJIS.map(e => `<button type="button" data-emoji="${e}">${e}</button>`).join('');
        }
    } catch {}
    const onlineCountEl = document.getElementById('wpsl-online-count');
    const onlineListEl = document.getElementById('wpsl-online-list');
    const usersListEl = document.getElementById('wpsl-users-list');
    const statsEl = document.getElementById('wpsl-stats');
    const toggleUsersBtn = document.getElementById('wpsl-toggle-users');
    const usersSidebarEl = document.getElementById('wpsl-users-sidebar');
    const chatContainerEl = document.querySelector('.wpsl-chat');

	function ts() {
		const d = new Date();
		return d.toLocaleTimeString(undefined, { hour12:false }) + "." + String(d.getMilliseconds()).padStart(3, "0");
	}
	function log(...a) {
		const line = `[${ts()}] ` + a.join(" ");
		console.log(line);
		if (logEl) logEl.textContent += line + "\n";
	}

	let lastSend = 0;
	function canSend() {
		const now = Date.now();
		if (now - lastSend < 1200) return false; // 1.2s
		lastSend = now;
		return true;
	}

	// viewer debug UI removed; log remains console-only

	// ----- playback
	function destroyHls() {
		try { if (hls && hls.destroy) hls.destroy(); } catch {}
		hls = null;
		try { video.removeAttribute('src'); video.load?.(); } catch {}
	}

	async function ensureHlsLib() {
		if (!(window.Hls && window.Hls.isSupported && window.Hls.isSupported())) {
			try {
				const mod = await import(WPSL_VIEW.hls_url);
				window.Hls = mod.default || mod.Hls || window.Hls;
			} catch (e) { log('hls module import failed:', e?.message||e); }
		}
		return !!(window.Hls && window.Hls.isSupported && window.Hls.isSupported());
	}

	async function loadPlaylist(url) {
		destroyHls();
		const src = url + (url.includes('?') ? '&' : '?') + 't=' + Date.now();
		if (video.canPlayType('application/vnd.apple.mpegurl')) {
			log('Native HLS path');
			video.src = src;
			video.play().catch(e => log('play() error:', e?.message||e));
			return;
		}
        if (await ensureHlsLib()) {
            log('hls.js path');
            const H = window.Hls;
            const inst = new H({
                // Push closer to live edge (non-LL-HLS)
                lowLatencyMode: false,
                backBufferLength: 10,
                maxBufferLength: 4,
                liveSyncDuration: 1.0,
                liveMaxLatencyDuration: 3.0,
                maxLiveSyncPlaybackRate: 1.0,
                maxBufferHole: 0.5,
                maxSeekHole: 0.5,
            });
			inst.on(H.Events.ERROR, (_, data) => {
				log('HLS ERROR:', data.type, data.details, 'fatal=', data.fatal);
				if (data.fatal) {
					if (data.type === H.ErrorTypes.NETWORK_ERROR) inst.startLoad();
					else if (data.type === H.ErrorTypes.MEDIA_ERROR) inst.recoverMediaError();
					else inst.destroy();
				}
			});
			inst.loadSource(src);
			inst.attachMedia(video);
			inst.on(H.Events.MANIFEST_PARSED, () => { video.play().catch(() => {}); });
			hls = inst;
		} else {
			log('HLS not supported on this browser');
		}
	}

// Choose source: prefer VOD when available unless explicitly forced to live
try {
    const hash = String(location.hash || '').toLowerCase();
    if (WPSL_VIEW.vod_playlist) {
        if (hash.includes('live')) {
            loadPlaylist(WPSL_VIEW.playlist);
        } else {
            loadPlaylist(WPSL_VIEW.vod_playlist);
        }
    } else {
        loadPlaylist(WPSL_VIEW.playlist);
    }
} catch { loadPlaylist(WPSL_VIEW.playlist); }

	// Mode toggle if replay exists
	try {
		if (WPSL_VIEW.vod_playlist) {
			const bl = document.getElementById('wpsl-mode-live');
			const br = document.getElementById('wpsl-mode-replay');
			bl?.addEventListener('click', () => loadPlaylist(WPSL_VIEW.playlist));
			br?.addEventListener('click', () => loadPlaylist(WPSL_VIEW.vod_playlist));
		}
	} catch {}

	// ----- chat
	function esc(s) {
		return String(s).replace(/[&<>"']/g, c => ({
			"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"
		}[c]));
	}

	function shortTime(s) {
		if (!s) return '';
		// Expect "YYYY-MM-DD HH:MM:SS" -> keep HH:MM
		if (typeof s === 'string' && s.length >= 16) return s.substring(11, 16);
		return String(s);
	}

   function appendMsg(m) {
       const div = document.createElement("div");
       div.className = "wpsl-msg";
       const uname = String(m.user_name||'');
       const unameEsc = esc(uname);
       let mod = '';
       // place moderation control before username; include delete for admins
       const delBtn = canModerate ? ` <button class="wpsl-del-btn" data-id="${Number(m.id)||0}">Delete</button>` : '';
       div.innerHTML = `<span class="t">${esc(shortTime(m.created_at))}</span>${mod} <b class="n">${unameEsc}</b>: ${esc(m.message)}${delBtn}`;
       // ensure raw username is stored on the button for accurate ban/unban
       const b = div.querySelector('.wpsl-ban-btn');
       if (b) { try { b.dataset.name = uname; } catch {} }
       messagesEl.appendChild(div);
       messagesEl.scrollTop = messagesEl.scrollHeight;
   }

	async function poll() {
		try {
			const url = new URL(WPSL_VIEW.rest + "/chat/poll");
			url.searchParams.set("stream_id", String(WPSL_VIEW.stream_id));
			url.searchParams.set("after_id", String(lastId));
			const res = await fetch(url.toString(), { cache: "no-store" });
			const json = await res.json();
			(json.messages || []).forEach(m => {
				lastId = Math.max(lastId, Number(m.id));
				appendMsg(m);
			});
        } catch (e) {
            // silent
        } finally {
            const delay = (window.WPSL_VIEW && Number(WPSL_VIEW.poll_ms)) || 1500;
            setTimeout(poll, delay);
        }
    }
    if (chatEnabled && messagesEl) poll();

    // ----- presence (online users)
    // uid per stream stored in localStorage
    function genUid() {
        try {
            const a = new Uint8Array(8);
            crypto.getRandomValues(a);
            return Array.from(a).map(x => x.toString(16).padStart(2,'0')).join('');
        } catch { return String(Math.random()).slice(2); }
    }
    const uidKey = 'wpsl_uid_' + String(WPSL_VIEW.stream_id);
    let uid = null;
    try { uid = localStorage.getItem(uidKey); } catch {}
    if (!uid) { uid = genUid(); try { localStorage.setItem(uidKey, uid); } catch {} }

    function currentPresenceName() {
        const n = (initialName || nameEl?.value || '').trim();
        return n || 'Guest';
    }

    async function presencePing() {
        try {
            const url = WPSL_VIEW.rest + '/presence/ping';
            const res = await fetch(url, {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
                body: new URLSearchParams({ stream_id: String(WPSL_VIEW.stream_id), uid, name: currentPresenceName() })
            });
            await res.text();
        } catch {}
    }
    // ping every 15s
    setInterval(presencePing, 15000);
    presencePing();

    // Admin-only: fetch list
    async function presenceList() {
        if (!canModerate || !onlineCountEl) return;
        try {
            const url = new URL(WPSL_VIEW.rest + '/presence/list');
            url.searchParams.set('stream_id', String(WPSL_VIEW.stream_id));
            const res = await fetch(url.toString(), { cache:'no-store', headers: { 'X-WP-Nonce': WPSL_VIEW.nonce || '' } });
            const json = await res.json();
            const count = Number(json.count || 0);
            if (onlineCountEl) onlineCountEl.textContent = 'Online: ' + count;
            if (usersListEl) {
                const users = Array.isArray(json.users) ? json.users : [];
                usersListEl.innerHTML = users.map(u => {
                    const name = String(u.name || 'Guest');
                    const nameEsc = esc(name);
                    return `<div class="wpsl-user-item"><div class="n">${nameEsc}</div><button class="ban" data-name="${nameEsc}">Ban</button></div>`;
                }).join('');
            }
            if (onlineListEl) {
                const users = Array.isArray(json.users) ? json.users : [];
                onlineListEl.innerHTML = users.map(u => `<div class="wpsl-presence-item">${esc(String(u.name||'Guest'))}</div>`).join('');
            }
        } catch {}
    }
    async function presenceStats() {
        if (!canModerate || !statsEl) return;
        try {
            const url = new URL(WPSL_VIEW.rest + '/presence/stats');
            url.searchParams.set('stream_id', String(WPSL_VIEW.stream_id));
            const res = await fetch(url.toString(), { cache:'no-store', headers: { 'X-WP-Nonce': WPSL_VIEW.nonce || '' } });
            const json = await res.json();
            const uq = Number(json.unique_24h || 0);
            statsEl.textContent = 'Unique (24h): ' + uq;
            if (onlineCountEl && typeof json.online === 'number') {
                onlineCountEl.textContent = 'Online: ' + json.online;
            }
        } catch {}
    }

    if (canModerate) {
    // Public presence (non-admin) if enabled
		async function publicPresence() {
			if (!usersVisible) return;

			try {
				const url = new URL(WPSL_VIEW.rest + '/presence/public');
				url.searchParams.set('stream_id', String(WPSL_VIEW.stream_id));

				const res = await fetch(url.toString(), { cache: 'no-store' });
				if (!res.ok) throw new Error('HTTP error');

				const json = await res.json();
				const count = Number(json.count || 0);

				if (onlineCountEl) {
					onlineCountEl.textContent = 'Online: ' + count;
				}

				if (usersListEl) {
					const users = Array.isArray(json.users) ? json.users : [];

					usersListEl.innerHTML = users.map(u => `
                <div class="wpsl-user-item">
                    <div class="n">${escapeHtml(u.name ?? '')}</div>
                </div>
            `).join('');
				}
			} catch (e) {
				console.error('publicPresence error:', e);
			}
		}

		if (!canModerate && usersVisible) {
        setInterval(publicPresence, 10000);
        publicPresence();
    }
        setInterval(presenceList, 10000);
        setInterval(presenceStats, 10000);
        presenceList();
        presenceStats();
    }

    function toggleUsersSidebar() {
        if (!usersSidebarEl) return;
        const currentlyVisible = usersSidebarEl.style.display !== 'none';
        if (currentlyVisible) {
            usersSidebarEl.style.display = 'none';
            if (chatContainerEl) chatContainerEl.classList.add('wpsl-no-users');
            try { if (toggleUsersBtn) toggleUsersBtn.textContent = 'Show Users'; } catch {}
        } else {
            usersSidebarEl.style.display = '';
            if (chatContainerEl) chatContainerEl.classList.remove('wpsl-no-users');
            try { if (toggleUsersBtn) toggleUsersBtn.textContent = 'Hide Users'; } catch {}
        }
    }
    toggleUsersBtn?.addEventListener('click', toggleUsersSidebar);

    // Initialize correct label
    (function(){
        try {
            if (!usersSidebarEl) return;
            const vis = usersSidebarEl.style.display !== 'none';
            if (toggleUsersBtn) toggleUsersBtn.textContent = vis ? 'Hide Users' : 'Show Users';
        } catch {}
    })();

    // Ban from sidebar list
    usersListEl?.addEventListener('click', async (e) => {
        if (!canModerate) return;
        const btn = e.target && e.target.closest ? e.target.closest('button.ban') : null;
        if (!btn) return;
        const name = btn.getAttribute('data-name') || '';
        if (!name) return;
        try {
            const selfName = (WPSL_VIEW.user_name || '').trim();
            if (selfName && selfName.toLowerCase() === name.toLowerCase()) {
                btn.disabled = true; btn.textContent = 'Self';
                return;
            }
        } catch {}
        try {
            const res = await fetch(WPSL_VIEW.rest + '/chat/mod/ban', {
                method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'X-WP-Nonce': WPSL_VIEW.nonce || '' },
                body: new URLSearchParams({ stream_id: String(WPSL_VIEW.stream_id), name })
            });
            await res.json();
            btn.disabled = true; btn.textContent = 'Banned';
        } catch {}
    });

    function insertAtCursor(input, text) {
        try {
            const start = input.selectionStart ?? input.value.length;
            const end = input.selectionEnd ?? input.value.length;
            const before = input.value.slice(0, start);
            const after = input.value.slice(end);
            input.value = before + text + after;
            const pos = start + text.length;
            input.selectionStart = input.selectionEnd = pos;
            input.focus();
        } catch {
            input.value += text;
        }
    }

    if (chatEnabled && emojiBtn && emojiPopup && msgEl) {
        emojiBtn.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            const vis = emojiPopup.style.display !== 'none';
            emojiPopup.style.display = vis ? 'none' : '';
            emojiBtn.setAttribute('aria-expanded', vis ? 'false' : 'true');
        });
        emojiPopup.addEventListener('click', (e) => {
            const btn = e.target && e.target.closest ? e.target.closest('button[data-emoji]') : null;
            if (btn && btn.dataset && btn.dataset.emoji) {
                e.preventDefault();
                e.stopPropagation();
                insertAtCursor(msgEl, btn.dataset.emoji);
                emojiPopup.style.display = 'none';
                emojiBtn.setAttribute('aria-expanded', 'false');
            }
        });
        document.addEventListener('click', (e) => {
            if (!emojiPopup.contains(e.target) && !emojiBtn.contains(e.target)) {
                emojiPopup.style.display = 'none';
                emojiBtn.setAttribute('aria-expanded', 'false');
            }
        });
    }

    async function send() {
		if (!canSend()) return;

		const name = (initialName || nameEl.value || "").trim() || "Guest";
        const message = (msgEl.value || "").trim();
        if (!message) return;

        try {
            const url = WPSL_VIEW.rest + "/chat/send";
            const res = await fetch(url, {
                method: "POST",
                headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" },
                body: new URLSearchParams({
                    stream_id: String(WPSL_VIEW.stream_id),
                    name,
                    message,
                }),
            });
            if (!res.ok) {
                if (res.status === 403) {
                    // banned: disable input and notify
                    msgEl.value = '';
                    msgEl.disabled = true;
                    msgEl.placeholder = 'You are banned from chat';
                }
                return;
            }
            await res.json();
            msgEl.value = "";
            if (nameEl && nameEl.style.display !== 'none') {
                try { nameEl.style.display = 'none'; } catch {}
                try { msgEl.style.flex = '1'; msgEl.style.maxWidth = '100%'; } catch {}
            }
        } catch (e) {}
    }

    if (chatEnabled && msgEl) {
        msgEl.addEventListener("keydown", (e) => {
            if (e.key === "Enter") send();
        });
        const sb = document.getElementById("wpsl-send");
        if (sb) sb.style.display = 'none';
    }

    // Prefill and hide name if WP user provided
    if (initialName && nameEl) {
        nameEl.value = initialName;
        nameEl.readOnly = true;
        nameEl.title = 'Using your WordPress username';
        // hide name input and let message take full width
        try { nameEl.style.display = 'none'; } catch {}
        try { msgEl.style.flex = '1'; msgEl.style.maxWidth = '100%'; } catch {}
    }

    // --- Moderation in viewer ---
    const bannedNames = new Set();
    async function refreshBanned() {
        if (!canModerate) return;
        try {
            const url = new URL(WPSL_VIEW.rest + '/chat/mod/banned_list');
            url.searchParams.set('stream_id', String(WPSL_VIEW.stream_id));
            const res = await fetch(url.toString(), { cache: 'no-store', headers: { 'X-WP-Nonce': WPSL_VIEW.nonce || '' } });
            const json = await res.json();
            (json.names||[]).forEach(n => bannedNames.add(String(n).toLowerCase()));
        } catch {}
    }
    if (canModerate) refreshBanned();

    messagesEl?.addEventListener('click', async (e) => {
        if (!canModerate) return;
        const banBtn = e.target && e.target.closest ? e.target.closest('.wpsl-ban-btn') : null;
        const delBtn = e.target && e.target.closest ? e.target.closest('.wpsl-del-btn') : null;
        if (banBtn) {
            const name = banBtn.getAttribute('data-name') || '';
            const banned = banBtn.getAttribute('data-banned') === '1';
            const path = banned ? '/chat/mod/unban' : '/chat/mod/ban';
            try {
                const res = await fetch(WPSL_VIEW.rest + path, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'X-WP-Nonce': WPSL_VIEW.nonce || '' },
                    body: new URLSearchParams({ stream_id: String(WPSL_VIEW.stream_id), name })
                });
                await res.json();
                if (banned) {
                    bannedNames.delete(name.toLowerCase());
                    banBtn.textContent = 'Ban';
                    banBtn.setAttribute('data-banned', '0');
                } else {
                    bannedNames.add(name.toLowerCase());
                    banBtn.textContent = 'Unban';
                    banBtn.setAttribute('data-banned', '1');
                }
            } catch {}
            return;
        }
        if (delBtn) {
            const id = Number(delBtn.getAttribute('data-id') || '0');
            if (!id) return;
            try {
                const res = await fetch(WPSL_VIEW.rest + '/chat/mod/delete', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'X-WP-Nonce': WPSL_VIEW.nonce || '' },
                    body: new URLSearchParams({ stream_id: String(WPSL_VIEW.stream_id), id: String(id) })
                });
                await res.json();
                const row = delBtn.closest('.wpsl-msg');
                if (row && row.parentElement) row.parentElement.removeChild(row);
            } catch {}
        }
    });
})();





