/**
 * folk-jitsi-room — Jitsi Meet External API wrapper with 360° Director panel.
 *
 * Loads Jitsi External API script, creates meeting in shadowRoot.
 * When director mode is active, shows HLS thumbnail strip for switching
 * the local video track to a 360° perspective view.
 */

import { startPresenceHeartbeat } from '../../../shared/collab-presence';
import type { DocumentId } from "../../../shared/local-first/document";
import { meetsSchema, meetsDocId } from "../schemas";

class FolkJitsiRoom extends HTMLElement {
	private shadow: ShadowRoot;
	private api: any = null;
	private room = "";
	private jitsiUrl = "";
	private space = "";
	private isDirector = false;
	private sessionId = "";
	// Director state
	private directorViews: Array<{ index: number; label: string; yaw: number; hls_url: string }> = [];
	private directorHlsPlayers: any[] = [];
	private directorActiveView: number | null = null;
	private directorCanvas: HTMLCanvasElement | null = null;
	private directorStream: MediaStream | null = null;
	private directorAnimFrame: number | null = null;
	private directorError = "";
	private _stopPresence: (() => void) | null = null;
	private _offlineUnsub: (() => void) | null = null;
	private _subscribedDocIds: string[] = [];

	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: "open" });
	}

	connectedCallback() {
		this.room = this.getAttribute("room") || "";
		this.jitsiUrl = this.getAttribute("jitsi-url") || "";
		this.space = this.getAttribute("space") || "";
		this.isDirector = this.getAttribute("director") === "1";
		this.sessionId = this.getAttribute("session") || "";

		this.render();
		this.loadJitsiApi();
		this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rmeets', context: this.room || 'Meeting' }));
		if (this.space && this.space !== "demo") {
			this.subscribeOffline();
		}
	}

	disconnectedCallback() {
		this._stopPresence?.();
		this._offlineUnsub?.(); this._offlineUnsub = null;
		const runtime = (window as any).__rspaceOfflineRuntime;
		if (runtime) {
			for (const id of this._subscribedDocIds) runtime.unsubscribe(id);
		}
		this._subscribedDocIds = [];
		this.dispose();
	}

	private async subscribeOffline() {
		const runtime = (window as any).__rspaceOfflineRuntime;
		if (!runtime?.isInitialized) return;
		try {
			const docId = meetsDocId(this.space) as DocumentId;
			await runtime.subscribe(docId, meetsSchema);
			this._subscribedDocIds.push(docId);
		} catch { /* runtime unavailable */ }
	}

	getApi() { return this.api; }

	executeCommand(cmd: string, ...args: any[]) {
		this.api?.executeCommand(cmd, ...args);
	}

	addJitsiListener(event: string, handler: (...args: any[]) => void) {
		this.api?.addListener(event, handler);
	}

	private render() {
		this.shadow.innerHTML = `
			<style>
				:host { display: flex; flex-direction: column; width: 100%; height: 100%; }
				.jitsi-container { width: 100%; flex: 1; min-height: 0; border-radius: 12px; overflow: hidden; background: #000; }
				.jitsi-container iframe { border: none !important; }
				.loading { display: flex; align-items: center; justify-content: center; flex: 1; min-height: 0; color: var(--rs-text-muted, #888); font-family: system-ui, sans-serif; }
				.director-strip { display: flex; gap: 6px; padding: 10px; background: var(--rs-bg-surface, #1a1a2e); border: 1px solid var(--rs-border, #333); border-radius: 0 0 12px 12px; overflow-x: auto; align-items: center; }
				.director-thumb { position: relative; width: 160px; min-width: 160px; aspect-ratio: 16/9; border-radius: 6px; overflow: hidden; cursor: pointer; border: 2px solid transparent; background: #111; }
				.director-thumb.active { border-color: #ef4444; }
				.director-thumb video { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
				.director-thumb-label { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.7); color: #fff; font-size: 0.7rem; padding: 2px 6px; text-align: center; font-family: system-ui, sans-serif; }
				.director-thumb-btn { position: absolute; top: 4px; right: 4px; background: rgba(239,68,68,0.9); color: #fff; border: none; border-radius: 4px; padding: 2px 6px; font-size: 0.65rem; cursor: pointer; font-family: system-ui, sans-serif; }
				.director-thumb-btn:hover { background: #ef4444; }
				.director-info { font-size: 0.75rem; color: var(--rs-text-muted, #888); padding: 0 8px; white-space: nowrap; font-family: system-ui, sans-serif; }
				.director-error { font-size: 0.8rem; color: #ef4444; padding: 8px; font-family: system-ui, sans-serif; }
			</style>
			<div class="jitsi-container" id="jitsi-meet" data-collab-id="session:${this.sessionId || 'lobby'}">
				<div class="loading">Loading Jitsi Meet...</div>
			</div>
			${this.isDirector && this.sessionId ? this.renderDirectorStrip() : ""}
		`;

		if (this.isDirector && this.sessionId) {
			this.bindDirectorEvents();
		}
	}

	private async loadJitsiApi() {
		if (!this.jitsiUrl) return;

		// Extract domain from URL
		const jitsiDomain = this.jitsiUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");

		try {
			// Load External API script
			if (!(window as any).JitsiMeetExternalAPI) {
				await new Promise<void>((resolve, reject) => {
					const script = document.createElement("script");
					script.src = `${this.jitsiUrl}/libs/external_api.min.js`;
					script.onload = () => resolve();
					script.onerror = () => reject(new Error("Failed to load Jitsi External API"));
					document.head.appendChild(script);
				});
			}

			const container = this.shadow.getElementById("jitsi-meet");
			if (!container) return;
			container.innerHTML = "";

			this.api = new (window as any).JitsiMeetExternalAPI(jitsiDomain, {
				roomName: this.room,
				parentNode: container,
				width: "100%",
				height: "100%",
				configOverwrite: {
					prejoinConfig: { enabled: true },
					requireDisplayName: true,
					disableDeepLinking: true,
					hideConferenceSubject: false,
					disableVirtualBackground: false,
					disableProfile: false,
					notifications: ['chat'],
					toolbarButtons: [
						"camera", "microphone", "desktop", "hangup",
						"raisehand", "tileview", "toggle-camera",
						"fullscreen", "chat", "settings",
						"participants-pane", "select-background",
						"sharedvideo", "recording",
					],
					// Hide panels that add stray close (×) buttons
					disableChat: false,
					participantsPane: { enabled: false },
				},
				interfaceConfigOverwrite: {
					SHOW_JITSI_WATERMARK: false,
					SHOW_WATERMARK_FOR_GUESTS: false,
					SHOW_BRAND_WATERMARK: false,
					CLOSE_PAGE_GUEST_HINT: false,
					SHOW_PROMOTIONAL_CLOSE_PAGE: false,
					CHAT_PANEL_POSITION: 'right',
					SETTINGS_SECTIONS: ['devices', 'language', 'moderator', 'profile', 'sounds', 'more'],
				},
			});

			// Emit custom events
			this.api.addListener("videoConferenceJoined", (data: any) => {
				this.dispatchEvent(new CustomEvent("jitsi-joined", { detail: data, bubbles: true }));
				// If director mode, load the session views
				if (this.isDirector && this.sessionId) {
					this.loadDirectorSession();
				}
			});
			this.api.addListener("videoConferenceLeft", (data: any) => {
				this.dispatchEvent(new CustomEvent("jitsi-left", { detail: data, bubbles: true }));
			});
			this.api.addListener("participantJoined", (data: any) => {
				this.dispatchEvent(new CustomEvent("jitsi-participant-joined", { detail: data, bubbles: true }));
			});
		} catch (e: any) {
			const container = this.shadow.getElementById("jitsi-meet");
			if (container) {
				container.innerHTML = `<div class="loading" style="color:#ef4444">Failed to load Jitsi: ${e.message}</div>`;
			}
		}
	}

	private dispose() {
		this.destroyDirector();
		if (this.api) {
			try { this.api.dispose(); } catch {}
			this.api = null;
		}
	}

	// ── Director Panel (Phase 4a) ──

	private renderDirectorStrip(): string {
		if (this.directorError) {
			return `<div class="director-strip"><div class="director-error">${this.directorError}</div></div>`;
		}

		if (this.directorViews.length === 0) {
			return `<div class="director-strip"><div class="director-info">360 Director — connecting to session ${this.sessionId}...</div></div>`;
		}

		const thumbs = this.directorViews.map(v => `
			<div class="director-thumb ${this.directorActiveView === v.index ? "active" : ""}" data-director-view="${v.index}">
				<video id="director-video-${v.index}" muted autoplay playsinline></video>
				<div class="director-thumb-label">${v.label} (${Math.round(v.yaw)}&deg;)</div>
				<button class="director-thumb-btn" data-director-share="${v.index}">Share</button>
			</div>
		`).join("");

		const stopBtn = this.directorActiveView !== null
			? `<button class="director-thumb-btn" data-director-share="stop" style="position:static;padding:4px 10px;font-size:0.75rem">Stop Sharing</button>`
			: "";

		return `<div class="director-strip">
			<div class="director-info">360 Director</div>
			${thumbs}
			${stopBtn}
		</div>`;
	}

	private bindDirectorEvents() {
		this.shadow.querySelectorAll("[data-director-share]").forEach(btn => {
			btn.addEventListener("click", (e) => {
				e.stopPropagation();
				const val = (btn as HTMLElement).dataset.directorShare;
				if (val === "stop") {
					this.stopDirectorShare();
				} else {
					this.shareDirectorView(parseInt(val || "0"));
				}
			});
		});
	}

	private async loadDirectorSession() {
		if (!this.sessionId) return;

		const base = window.location.pathname.replace(/\/room\/.*$/, "");
		// Find the rtube base path — go up to space level
		const spacePath = `/${this.space}`;
		const rtubeBase = `${spacePath}/rtube`;

		try {
			const resp = await fetch(`${rtubeBase}/api/live-split/status/${this.sessionId}`);
			if (!resp.ok) {
				this.directorError = "Could not connect to live-split session";
				this.renderDirectorOnly();
				return;
			}
			const data = await resp.json();
			if (data.status !== "running") {
				this.directorError = `Session status: ${data.status}`;
				this.renderDirectorOnly();
				return;
			}

			this.directorViews = data.views.map((v: any) => ({
				index: v.index,
				label: v.label,
				yaw: v.yaw,
				hls_url: v.hls_url,
			}));

			this.renderDirectorOnly();

			// Attach HLS players to director thumbnails
			setTimeout(() => this.attachDirectorHls(rtubeBase), 1000);
		} catch (e: any) {
			this.directorError = e.message || "Failed to load session";
			this.renderDirectorOnly();
		}
	}

	private renderDirectorOnly() {
		// Re-render just the director strip without touching Jitsi
		const existing = this.shadow.querySelector(".director-strip");
		if (existing) existing.remove();

		const container = this.shadow.getElementById("jitsi-meet");
		if (!container) return;

		const temp = document.createElement("div");
		temp.innerHTML = this.isDirector && this.sessionId ? this.renderDirectorStrip() : "";
		const strip = temp.firstElementChild;
		if (strip) {
			container.after(strip);
			this.bindDirectorEvents();
		}
	}

	private async attachDirectorHls(rtubeBase: string) {
		const Hls = await this.loadHlsJs();
		if (!Hls || !Hls.isSupported()) return;

		this.destroyDirectorHls();

		for (const view of this.directorViews) {
			const video = this.shadow.getElementById(`director-video-${view.index}`) as HTMLVideoElement;
			if (!video) continue;

			const hls = new Hls({
				liveSyncDurationCount: 3,
				liveMaxLatencyDurationCount: 6,
				enableWorker: true,
			});

			const hlsUrl = `${rtubeBase}/api/live-split/hls/${this.sessionId}/${view.hls_url.replace(/^.*?view_/, "view_")}`;
			hls.loadSource(hlsUrl);
			hls.attachMedia(video);
			hls.on(Hls.Events.MANIFEST_PARSED, () => video.play().catch(() => {}));
			this.directorHlsPlayers.push(hls);
		}
	}

	private async loadHlsJs(): Promise<any> {
		if ((window as any).Hls) return (window as any).Hls;
		return new Promise((resolve, reject) => {
			const script = document.createElement("script");
			script.src = "https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js";
			script.onload = () => resolve((window as any).Hls);
			script.onerror = () => reject(new Error("Failed to load HLS.js"));
			document.head.appendChild(script);
		});
	}

	private shareDirectorView(viewIndex: number) {
		const video = this.shadow.getElementById(`director-video-${viewIndex}`) as HTMLVideoElement;
		if (!video || !this.api) return;

		// Create canvas for captureStream
		if (!this.directorCanvas) {
			this.directorCanvas = document.createElement("canvas");
			this.directorCanvas.width = 1280;
			this.directorCanvas.height = 720;
		}

		this.directorActiveView = viewIndex;
		const ctx = this.directorCanvas.getContext("2d")!;

		// Stop previous animation loop
		if (this.directorAnimFrame) {
			cancelAnimationFrame(this.directorAnimFrame);
		}

		// Draw video to canvas in a loop
		const drawFrame = () => {
			if (this.directorActiveView !== viewIndex) return;
			ctx.drawImage(video, 0, 0, 1280, 720);
			this.directorAnimFrame = requestAnimationFrame(drawFrame);
		};
		drawFrame();

		// Get MediaStream from canvas
		if (!this.directorStream) {
			this.directorStream = this.directorCanvas.captureStream(30);
		}

		// Replace local video track in Jitsi
		const videoTrack = this.directorStream.getVideoTracks()[0];
		if (videoTrack) {
			// Use Jitsi's iframe API to replace the video track
			try {
				this.api.executeCommand("overwriteLocalVideoTrack", videoTrack);
			} catch {
				// Fallback: some Jitsi versions use different API
				try {
					const iframe = this.shadow.querySelector("iframe") as HTMLIFrameElement;
					if (iframe?.contentWindow) {
						iframe.contentWindow.postMessage({
							type: "replace-local-track",
							track: "video",
						}, "*");
					}
				} catch {}
			}
		}

		this.renderDirectorOnly();
	}

	private stopDirectorShare() {
		if (this.directorAnimFrame) {
			cancelAnimationFrame(this.directorAnimFrame);
			this.directorAnimFrame = null;
		}
		this.directorActiveView = null;
		this.directorStream = null;

		// Restore camera
		if (this.api) {
			try {
				this.api.executeCommand("toggleVideo");
				setTimeout(() => this.api?.executeCommand("toggleVideo"), 100);
			} catch {}
		}

		this.renderDirectorOnly();
	}

	private destroyDirectorHls() {
		for (const hls of this.directorHlsPlayers) {
			try { hls.destroy(); } catch {}
		}
		this.directorHlsPlayers = [];
	}

	private destroyDirector() {
		if (this.directorAnimFrame) {
			cancelAnimationFrame(this.directorAnimFrame);
			this.directorAnimFrame = null;
		}
		this.directorStream = null;
		this.directorCanvas = null;
		this.destroyDirectorHls();
	}

	/* ── Guided Tour ── */

	private _tour: import('../../../shared/tour-engine').TourEngine | null = null;

	private async _initTour() {
		if (this._tour) return;
		const { TourEngine } = await import('../../../shared/tour-engine');
		this._tour = new TourEngine(this.shadow as unknown as ShadowRoot, [
			{ target: '#jitsi-meet', title: 'Video Meeting', message: 'Your self-hosted Jitsi Meet room. All video and audio stays on your own infrastructure.' },
			{ target: '.jitsi-container', title: 'Full-Screen Controls', message: 'Use the Jitsi toolbar for mute, camera, screen share, chat, and more — all built in.' },
		], 'rmeets_tour_done', () => this.shadow.querySelector('.jitsi-container') as HTMLElement);
	}

	async startTour() {
		await this._initTour();
		this._tour?.start();
	}
}

customElements.define("folk-jitsi-room", FolkJitsiRoom);
