import { SessionInfo } from '@core/types'
import { AsyncLocalStorage } from 'node:async_hooks'

/**
 * Session Service
 *
 * This service manages user session information throughout the application using AsyncLocalStorage.
 * It provides a thread-local storage mechanism for session data that is accessible across the request
 * lifecycle without passing context through function parameters.
 *
 * Usage:
 * - Authentication interceptors call `set()` to establish session context
 * - Application code calls `get()` to access the current session information
 */
export class SessionStore {
	private static readonly storage = new AsyncLocalStorage<SessionInfo>()

	/**
	 * Sets the current session information.
	 *
	 * @param user - The session information to set
	 */
	static set(user: SessionInfo) {
		return this.storage.enterWith(user)
	}

	/**
	 * Retrieves the current session information
	 *
	 * @returns The current session information, or undefined if no session is found
	 */
	static get(): SessionInfo | undefined {
		return this.storage.getStore()
	}

	/**
	 * Retrieves userId from the current session.
	 *
	 * @returns The userId, or undefined if no session is found
	 */
	static get userId(): string | undefined {
		return this.storage.getStore()?.id
	}

	/** Sets tenantId for the current session */
	static set tenantId(tenantId: string) {
		const currentSession = this.get()
		if (currentSession) {
			this.storage.enterWith({ ...currentSession, tenantId })
		} else {
			this.storage.enterWith({ tenantId } as SessionInfo)
		}
	}

	/**
	 * Retrieves tenantId from the current session.
	 *
	 * @returns The tenantId, or undefined if no session is active
	 */
	static get tenantId(): string | undefined {
		return this.storage.getStore()?.tenantId
	}

	/**
	 * Runs the given function with the provided session as guaranteed context.
	 *
	 * Unlike the `enterWith`-based setters, `AsyncLocalStorage.run` reliably
	 * scopes the session for everything awaited inside `fn` — including code in
	 * the CALLER's async frame that an `enterWith` from a nested awaited call
	 * would not reach. Use this when the session is derived mid-request (e.g.
	 * resolving the tenant from a reservation's management token).
	 */
	static runWith<T>(session: SessionInfo, fn: () => Promise<T>): Promise<T> {
		return this.storage.run(session, fn)
	}
}
