"use client";

import {
	createContext,
	type ReactNode,
	useContext,
	useEffect,
	useState,
} from "react";
import { SFWorkspaceStore } from "./sf-workspace-store";

/**
 * ProjectStoreManager maintains a Map<string, SFWorkspaceStore> of per-project
 * stores with SSE lifecycle management. Only the active project's store keeps its
 * SSE connection open — background stores are disconnected to save resources.
 *
 * Exposes a useSyncExternalStore-compatible interface for React components to
 * reactively read the active project path.
 */
export class ProjectStoreManager {
	private stores = new Map<string, SFWorkspaceStore>();
	private activeProjectCwd: string | null = null;
	private listeners = new Set<() => void>();

	// ─── useSyncExternalStore interface ──────────────────────────────────────

	subscribe = (listener: () => void): (() => void) => {
		this.listeners.add(listener);
		return () => this.listeners.delete(listener);
	};

	getSnapshot = (): string | null => this.activeProjectCwd;

	// ─── Public API ──────────────────────────────────────────────────────────

	getActiveStore(): SFWorkspaceStore | null {
		if (!this.activeProjectCwd) return null;
		return this.stores.get(this.activeProjectCwd) ?? null;
	}

	getActiveProjectCwd(): string | null {
		return this.activeProjectCwd;
	}

	/**
	 * Switch to the given project. Disconnects SSE on the previous active store,
	 * creates a new store if needed (lazily), reconnects SSE on re-activated stores.
	 */
	switchProject(projectCwd: string): SFWorkspaceStore {
		// Disconnect SSE on current active store
		if (this.activeProjectCwd && this.activeProjectCwd !== projectCwd) {
			const prev = this.stores.get(this.activeProjectCwd);
			if (prev) prev.disconnectSSE();
		}

		// Get or create store for new project
		let store = this.stores.get(projectCwd);
		if (!store) {
			store = new SFWorkspaceStore(projectCwd);
			this.stores.set(projectCwd, store);
			store.start();
		} else {
			// Reconnect SSE on re-activated store
			store.reconnectSSE();
		}

		this.activeProjectCwd = projectCwd;
		this.notify();
		return store;
	}

	/** Dispose all stores and clear manager state. */
	disposeAll(): void {
		for (const store of this.stores.values()) {
			store.dispose();
		}
		this.stores.clear();
		this.activeProjectCwd = null;
		this.notify();
	}

	/** Close a single project's store and switch to another if it was active. */
	closeProject(projectCwd: string): void {
		const store = this.stores.get(projectCwd);
		if (!store) return;

		store.dispose();
		this.stores.delete(projectCwd);

		// If we closed the active project, switch to another or clear
		if (this.activeProjectCwd === projectCwd) {
			const remaining = Array.from(this.stores.keys());
			if (remaining.length > 0) {
				// Switch to the first remaining project
				const next = this.stores.get(remaining[0])!;
				this.activeProjectCwd = remaining[0];
				next.reconnectSSE();
			} else {
				this.activeProjectCwd = null;
			}
		}

		this.notify();
	}

	/** Number of active project stores. */
	getProjectCount(): number {
		return this.stores.size;
	}

	/** Get all active project paths. */
	getActiveProjectPaths(): string[] {
		return Array.from(this.stores.keys());
	}

	private notify(): void {
		for (const listener of this.listeners) listener();
	}
}

// ─── React Context + Provider + Hook ──────────────────────────────────────

export const ProjectStoreManagerContext =
	createContext<ProjectStoreManager | null>(null);

export function ProjectStoreManagerProvider({
	children,
}: {
	children: ReactNode;
}) {
	const [manager] = useState(() => new ProjectStoreManager());

	useEffect(() => {
		return () => manager.disposeAll();
	}, [manager]);

	return (
		<ProjectStoreManagerContext.Provider value={manager}>
			{children}
		</ProjectStoreManagerContext.Provider>
	);
}

export function useProjectStoreManager(): ProjectStoreManager {
	const mgr = useContext(ProjectStoreManagerContext);
	if (!mgr)
		throw new Error(
			"useProjectStoreManager must be used within ProjectStoreManagerProvider",
		);
	return mgr;
}
