/**
 * Thread-Safe State Management Utility
 * Provides thread-safe operations for shared state in autonomous systems
 */

import { EventEmitter } from 'events';

/**
 * Thread-safe Map implementation with locking mechanism
 */
export class ThreadSafeMap<K, V> extends EventEmitter {
	private map = new Map<K, V>();
	private locks = new Set<string>();
	private readonly lockTimeout: number;

	constructor(lockTimeout: number = 5000) {
		super();
		this.lockTimeout = lockTimeout;
	}

	/**
	 * Acquire a lock for a specific key
	 */
	private async acquireLock(key: K): Promise<string> {
		const lockId = `${String(key)}_${Date.now()}_${Math.random()}`;
		const keyStr = String(key);
		
		// Wait for existing lock to be released
		while (this.locks.has(keyStr)) {
			await new Promise(resolve => setTimeout(resolve, 10));
		}
		
		this.locks.add(keyStr);
		
		// Set timeout to prevent deadlocks
		setTimeout(() => {
			if (this.locks.has(keyStr)) {
				console.warn(`Lock timeout for key ${keyStr}, forcibly releasing`);
				this.locks.delete(keyStr);
			}
		}, this.lockTimeout);
		
		return lockId;
	}

	/**
	 * Release a lock for a specific key
	 */
	private releaseLock(key: K): void {
		this.locks.delete(String(key));
	}

	/**
	 * Safely set a value with locking
	 */
	public async safeSet(key: K, value: V): Promise<void> {
		await this.acquireLock(key);
		try {
			const oldValue = this.map.get(key);
			this.map.set(key, value);
			this.emit('change', { key, oldValue, newValue: value, operation: 'set' });
		} finally {
			this.releaseLock(key);
		}
	}

	/**
	 * Safely get a value
	 */
	public safeGet(key: K): V | undefined {
		return this.map.get(key);
	}

	/**
	 * Safely delete a value with locking
	 */
	public async safeDelete(key: K): Promise<boolean> {
		await this.acquireLock(key);
		try {
			const oldValue = this.map.get(key);
			const deleted = this.map.delete(key);
			if (deleted) {
				this.emit('change', { key, oldValue, newValue: undefined, operation: 'delete' });
			}
			return deleted;
		} finally {
			this.releaseLock(key);
		}
	}

	/**
	 * Safely check if key exists
	 */
	public safeHas(key: K): boolean {
		return this.map.has(key);
	}

	/**
	 * Safely get all keys
	 */
	public safeKeys(): K[] {
		return Array.from(this.map.keys());
	}

	/**
	 * Safely get all values
	 */
	public safeValues(): V[] {
		return Array.from(this.map.values());
	}

	/**
	 * Safely get all entries
	 */
	public safeEntries(): [K, V][] {
		return Array.from(this.map.entries());
	}

	/**
	 * Safely get size
	 */
	public safeSize(): number {
		return this.map.size;
	}

	/**
	 * Safely clear all entries
	 */
	public async safeClear(): Promise<void> {
		// Lock all existing keys
		const keys = this.safeKeys();
		for (const key of keys) {
			await this.acquireLock(key);
		}
		
		try {
			this.map.clear();
			this.emit('change', { operation: 'clear' });
		} finally {
			// Release all locks
			for (const key of keys) {
				this.releaseLock(key);
			}
		}
	}

	/**
	 * Safely update a value with a function
	 */
	public async safeUpdate(key: K, updateFn: (currentValue: V | undefined) => V): Promise<V> {
		await this.acquireLock(key);
		try {
			const oldValue = this.map.get(key);
			const newValue = updateFn(oldValue);
			this.map.set(key, newValue);
			this.emit('change', { key, oldValue, newValue, operation: 'update' });
			return newValue;
		} finally {
			this.releaseLock(key);
		}
	}

	/**
	 * Safely compute a value if absent
	 */
	public async safeComputeIfAbsent(key: K, computeFn: () => Promise<V> | V): Promise<V> {
		await this.acquireLock(key);
		try {
			if (this.map.has(key)) {
				return this.map.get(key)!;
			}
			
			const value = await Promise.resolve(computeFn());
			this.map.set(key, value);
			this.emit('change', { key, oldValue: undefined, newValue: value, operation: 'compute' });
			return value;
		} finally {
			this.releaseLock(key);
		}
	}
}

/**
 * Thread-safe Set implementation
 */
export class ThreadSafeSet<T> extends EventEmitter {
	private set = new Set<T>();
	private locks = new Set<string>();
	private readonly lockTimeout: number;

	constructor(lockTimeout: number = 5000) {
		super();
		this.lockTimeout = lockTimeout;
	}

	/**
	 * Acquire a lock for set operations
	 */
	private async acquireLock(): Promise<void> {
		const lockId = `set_${Date.now()}_${Math.random()}`;
		
		// Wait for existing lock to be released
		while (this.locks.size > 0) {
			await new Promise(resolve => setTimeout(resolve, 10));
		}
		
		this.locks.add(lockId);
		
		// Set timeout to prevent deadlocks
		setTimeout(() => {
			if (this.locks.has(lockId)) {
				console.warn(`Set lock timeout, forcibly releasing`);
				this.locks.delete(lockId);
			}
		}, this.lockTimeout);
	}

	/**
	 * Release the set lock
	 */
	private releaseLock(): void {
		this.locks.clear();
	}

	/**
	 * Safely add a value
	 */
	public async safeAdd(value: T): Promise<boolean> {
		await this.acquireLock();
		try {
			const hadValue = this.set.has(value);
			this.set.add(value);
			if (!hadValue) {
				this.emit('change', { value, operation: 'add' });
			}
			return !hadValue;
		} finally {
			this.releaseLock();
		}
	}

	/**
	 * Safely delete a value
	 */
	public async safeDelete(value: T): Promise<boolean> {
		await this.acquireLock();
		try {
			const deleted = this.set.delete(value);
			if (deleted) {
				this.emit('change', { value, operation: 'delete' });
			}
			return deleted;
		} finally {
			this.releaseLock();
		}
	}

	/**
	 * Safely check if value exists
	 */
	public safeHas(value: T): boolean {
		return this.set.has(value);
	}

	/**
	 * Safely get all values
	 */
	public safeValues(): T[] {
		return Array.from(this.set.values());
	}

	/**
	 * Safely get size
	 */
	public safeSize(): number {
		return this.set.size;
	}

	/**
	 * Safely clear all values
	 */
	public async safeClear(): Promise<void> {
		await this.acquireLock();
		try {
			this.set.clear();
			this.emit('change', { operation: 'clear' });
		} finally {
			this.releaseLock();
		}
	}
}

/**
 * Thread-safe Counter with atomic operations
 */
export class ThreadSafeCounter extends EventEmitter {
	private value: number = 0;
	private lock: boolean = false;

	/**
	 * Atomically increment the counter
	 */
	public async increment(amount: number = 1): Promise<number> {
		while (this.lock) {
			await new Promise(resolve => setTimeout(resolve, 1));
		}
		
		this.lock = true;
		try {
			const oldValue = this.value;
			this.value += amount;
			this.emit('change', { oldValue, newValue: this.value, operation: 'increment' });
			return this.value;
		} finally {
			this.lock = false;
		}
	}

	/**
	 * Atomically decrement the counter
	 */
	public async decrement(amount: number = 1): Promise<number> {
		return this.increment(-amount);
	}

	/**
	 * Get current value
	 */
	public getValue(): number {
		return this.value;
	}

	/**
	 * Atomically set the value
	 */
	public async setValue(newValue: number): Promise<number> {
		while (this.lock) {
			await new Promise(resolve => setTimeout(resolve, 1));
		}
		
		this.lock = true;
		try {
			const oldValue = this.value;
			this.value = newValue;
			this.emit('change', { oldValue, newValue, operation: 'set' });
			return this.value;
		} finally {
			this.lock = false;
		}
	}

	/**
	 * Atomically compare and swap
	 */
	public async compareAndSwap(expected: number, newValue: number): Promise<boolean> {
		while (this.lock) {
			await new Promise(resolve => setTimeout(resolve, 1));
		}
		
		this.lock = true;
		try {
			if (this.value === expected) {
				const oldValue = this.value;
				this.value = newValue;
				this.emit('change', { oldValue, newValue, operation: 'compareAndSwap' });
				return true;
			}
			return false;
		} finally {
			this.lock = false;
		}
	}
}

/**
 * Thread-safe Queue implementation
 */
export class ThreadSafeQueue<T> extends EventEmitter {
	private queue: T[] = [];
	private lock: boolean = false;

	/**
	 * Acquire queue lock
	 */
	private async acquireLock(): Promise<void> {
		while (this.lock) {
			await new Promise(resolve => setTimeout(resolve, 1));
		}
		this.lock = true;
	}

	/**
	 * Release queue lock
	 */
	private releaseLock(): void {
		this.lock = false;
	}

	/**
	 * Safely enqueue an item
	 */
	public async enqueue(item: T): Promise<void> {
		await this.acquireLock();
		try {
			this.queue.push(item);
			this.emit('enqueue', item);
		} finally {
			this.releaseLock();
		}
	}

	/**
	 * Safely dequeue an item
	 */
	public async dequeue(): Promise<T | undefined> {
		await this.acquireLock();
		try {
			const item = this.queue.shift();
			if (item !== undefined) {
				this.emit('dequeue', item);
			}
			return item;
		} finally {
			this.releaseLock();
		}
	}

	/**
	 * Safely peek at the front item
	 */
	public peek(): T | undefined {
		return this.queue[0];
	}

	/**
	 * Get queue size
	 */
	public size(): number {
		return this.queue.length;
	}

	/**
	 * Check if queue is empty
	 */
	public isEmpty(): boolean {
		return this.queue.length === 0;
	}

	/**
	 * Safely clear the queue
	 */
	public async clear(): Promise<void> {
		await this.acquireLock();
		try {
			this.queue = [];
			this.emit('clear');
		} finally {
			this.releaseLock();
		}
	}
}

/**
 * State Management Registry for centralized state tracking
 */
export class StateManager {
	private static instance: StateManager;
	private maps = new Map<string, ThreadSafeMap<any, any>>();
	private sets = new Map<string, ThreadSafeSet<any>>();
	private counters = new Map<string, ThreadSafeCounter>();
	private queues = new Map<string, ThreadSafeQueue<any>>();

	private constructor() {}

	public static getInstance(): StateManager {
		if (!StateManager.instance) {
			StateManager.instance = new StateManager();
		}
		return StateManager.instance;
	}

	/**
	 * Get or create a thread-safe map
	 */
	public getMap<K, V>(name: string): ThreadSafeMap<K, V> {
		if (!this.maps.has(name)) {
			this.maps.set(name, new ThreadSafeMap<K, V>());
		}
		return this.maps.get(name)!;
	}

	/**
	 * Get or create a thread-safe set
	 */
	public getSet<T>(name: string): ThreadSafeSet<T> {
		if (!this.sets.has(name)) {
			this.sets.set(name, new ThreadSafeSet<T>());
		}
		return this.sets.get(name)!;
	}

	/**
	 * Get or create a thread-safe counter
	 */
	public getCounter(name: string): ThreadSafeCounter {
		if (!this.counters.has(name)) {
			this.counters.set(name, new ThreadSafeCounter());
		}
		return this.counters.get(name)!;
	}

	/**
	 * Get or create a thread-safe queue
	 */
	public getQueue<T>(name: string): ThreadSafeQueue<T> {
		if (!this.queues.has(name)) {
			this.queues.set(name, new ThreadSafeQueue<T>());
		}
		return this.queues.get(name)!;
	}

	/**
	 * Remove a managed state
	 */
	public removeState(name: string): boolean {
		const removed = this.maps.delete(name) || 
						this.sets.delete(name) || 
						this.counters.delete(name) || 
						this.queues.delete(name);
		return removed;
	}

	/**
	 * Get statistics about managed state
	 */
	public getStats(): {
		maps: number;
		sets: number;
		counters: number;
		queues: number;
		total: number;
	} {
		return {
			maps: this.maps.size,
			sets: this.sets.size,
			counters: this.counters.size,
			queues: this.queues.size,
			total: this.maps.size + this.sets.size + this.counters.size + this.queues.size
		};
	}

	/**
	 * Clear all managed state
	 */
	public async clearAll(): Promise<void> {
		const promises: Promise<void>[] = [];
		
		for (const map of this.maps.values()) {
			promises.push(map.safeClear());
		}
		
		for (const set of this.sets.values()) {
			promises.push(set.safeClear());
		}
		
		for (const queue of this.queues.values()) {
			promises.push(queue.clear());
		}
		
		await Promise.all(promises);
		
		this.maps.clear();
		this.sets.clear();
		this.counters.clear();
		this.queues.clear();
	}
}

// Global state manager instance
export const stateManager = StateManager.getInstance();