import { CacheService } from './cacheService';

/**
 * Session storage service
 * Provides Redis-backed session management for non-authenticated sessions
 * (e.g., guest carts, temporary form data)
 */

export interface SessionData {
	[key: string]: unknown;
	expiresAt?: number;
}

export class SessionStorageService {
	private cache: CacheService;
	private static instance: SessionStorageService | null = null;
	private readonly DEFAULT_TTL = 3600; // 1 hour default

	private constructor(cache?: CacheService) {
		this.cache = cache || CacheService.getInstance();
	}

	static getInstance(cache?: CacheService): SessionStorageService {
		if (!SessionStorageService.instance) {
			SessionStorageService.instance = new SessionStorageService(cache);
		}
		return SessionStorageService.instance;
	}

	/**
	 * Reset the singleton instance - for testing only
	 */
	static resetInstance(): void {
		SessionStorageService.instance = null;
	}

	/**
	 * Create a new session
	 */
	async createSession(sessionId: string, data: SessionData, ttlSeconds = this.DEFAULT_TTL): Promise<void> {
		const sessionData = {
			...data,
			createdAt: Date.now(),
			expiresAt: Date.now() + ttlSeconds * 1000,
		};
		await this.cache.set(`session:${sessionId}`, sessionData, ttlSeconds);
	}

	/**
	 * Get session data
	 */
	async getSession(sessionId: string): Promise<SessionData | null> {
		return this.cache.get<SessionData>(`session:${sessionId}`);
	}

	/**
	 * Update session data
	 */
	async updateSession(sessionId: string, data: Partial<SessionData>): Promise<void> {
		const existing = await this.getSession(sessionId);
		if (!existing) {
			throw new Error('Session not found');
		}

		const updated = {
			...existing,
			...data,
			updatedAt: Date.now(),
		};

		// Get remaining TTL and preserve it (don't extend)
		const remainingTtl = await this.cache.ttl(`session:${sessionId}`);
		// If TTL is -1 (no expiry) or -2 (key doesn't exist), use default
		const ttlToUse = remainingTtl > 0 ? remainingTtl : this.DEFAULT_TTL;
		await this.cache.set(`session:${sessionId}`, updated, ttlToUse);
	}

	/**
	 * Delete session
	 */
	async deleteSession(sessionId: string): Promise<void> {
		await this.cache.delete(`session:${sessionId}`);
	}

	/**
	 * Check if session exists and is valid
	 */
	async isValidSession(sessionId: string): Promise<boolean> {
		const session = await this.getSession(sessionId);
		if (!session) return false;

		// Check expiration
		if (session.expiresAt && session.expiresAt < Date.now()) {
			await this.deleteSession(sessionId);
			return false;
		}

		return true;
	}

	/**
	 * Extend session expiration
	 */
	async extendSession(sessionId: string, additionalSeconds = this.DEFAULT_TTL): Promise<void> {
		const exists = await this.cache.exists(`session:${sessionId}`);
		if (!exists) {
			throw new Error('Session not found');
		}

		await this.cache.expire(`session:${sessionId}`, additionalSeconds);
	}

	/**
	 * Get all active sessions (for admin/monitoring)
	 * Uses SCAN to avoid blocking Redis
	 */
	async getActiveSessionCount(): Promise<number> {
		return this.cache.countPattern('session:*');
	}
}
