/**
 * Reactive State Management System für Module-Kommunikation
 * Ermöglicht type-safe state sharing zwischen Modulen
 */
import { Logger } from './logger.js';

/**
 * @typedef {Object} StateChangeEvent
 * @property {string} key - State key that changed
 * @property {*} value - New value
 * @property {*} oldValue - Previous value
 * @property {string} source - Module that triggered the change
 */

/**
 * @typedef {Object} StateSubscription
 * @property {string} id - Unique subscription ID
 * @property {Function} callback - Callback function
 * @property {string} subscriber - Module name that subscribed
 */

export class StateManager {
    constructor() {
        /** @type {Map<string, any>} */
        this.state = new Map();
        
        /** @type {Map<string, StateSubscription[]>} */
        this.subscribers = new Map();
        
        /** @type {Map<string, string>} */
        this.stateOwners = new Map();
        
        /** @type {Map<string, any>} */
        this.defaultValues = new Map();
        
        /** @type {string} */
        this.currentModule = 'unknown';
        
        this.subscriptionCounter = 0;
    }

    /**
     * Set the current module context for state operations
     * @param {string} moduleName - Name of the module
     */
    setContext(moduleName) {
        this.currentModule = moduleName;
    }

    /**
     * Register a state key with default value and owner
     * @param {string} key - State key
     * @param {*} defaultValue - Default value
     * @param {string} [owner] - Module that owns this state
     */
    register(key, defaultValue, owner = this.currentModule) {
        if (this.state.has(key)) {
            Logger.warn(`[StateManager] State key '${key}' already registered by ${this.stateOwners.get(key)}`);
            return;
        }

        this.state.set(key, defaultValue);
        this.defaultValues.set(key, defaultValue);
        this.stateOwners.set(key, owner);
        this.subscribers.set(key, []);

        Logger.info(`[StateManager] Registered '${key}' (owner: ${owner})`);
    }

    /**
     * Get current value of state key
     * @param {string} key - State key
     * @returns {*} Current value or undefined
     */
    get(key) {
        if (!this.state.has(key)) {
            Logger.warn(`[StateManager] Unknown state key: '${key}'`);
            return undefined;
        }
        return this.state.get(key);
    }

    /**
     * Set value for state key (with ownership check)
     * @param {string} key - State key
     * @param {*} value - New value
     * @param {boolean} [force=false] - Force set even if not owner
     * @returns {boolean} Success status
     */
    set(key, value, force = false) {
        if (!this.state.has(key)) {
            Logger.warn(`[StateManager] Cannot set unknown state key: '${key}'`);
            return false;
        }

        const owner = this.stateOwners.get(key);
        if (!force && owner !== this.currentModule) {
            Logger.warn(`[StateManager] Module '${this.currentModule}' cannot modify '${key}' (owned by ${owner})`);
            return false;
        }

        const oldValue = this.state.get(key);
        if (oldValue === value) {
            return true; // No change
        }

        this.state.set(key, value);
        this.notifySubscribers(key, value, oldValue);

        Logger.info(`[StateManager] Updated '${key}' by ${this.currentModule}`);
        return true;
    }

    /**
     * Subscribe to state changes
     * @param {string} key - State key to watch
     * @param {Function} callback - Callback function (value, oldValue, key) => void
     * @param {string} [subscriber] - Subscriber module name
     * @returns {string} Subscription ID for unsubscribing
     */
    subscribe(key, callback, subscriber = this.currentModule) {
        if (!this.state.has(key)) {
            Logger.warn(`[StateManager] Cannot subscribe to unknown state key: '${key}'`);
            return null;
        }

        const subscriptionId = `${subscriber}_${++this.subscriptionCounter}`;
        const subscription = {
            id: subscriptionId,
            callback,
            subscriber
        };

        if (!this.subscribers.has(key)) {
            this.subscribers.set(key, []);
        }

        this.subscribers.get(key).push(subscription);
        Logger.info(`[StateManager] Subscribed '${subscriber}' to '${key}'`);

        return subscriptionId;
    }

    /**
     * Unsubscribe from state changes
     * @param {string} subscriptionId - Subscription ID from subscribe()
     */
    unsubscribe(subscriptionId) {
        for (const [key, subscriptions] of this.subscribers.entries()) {
            const index = subscriptions.findIndex(sub => sub.id === subscriptionId);
            if (index !== -1) {
                const subscription = subscriptions[index];
                subscriptions.splice(index, 1);
                Logger.info(`[StateManager] Unsubscribed '${subscription.subscriber}' from '${key}'`);
                return;
            }
        }
        Logger.warn(`[StateManager] Subscription ID not found: ${subscriptionId}`);
    }

    /**
     * Notify all subscribers of a state change
     * @private
     * @param {string} key - State key
     * @param {*} value - New value
     * @param {*} oldValue - Previous value
     */
    notifySubscribers(key, value, oldValue) {
        const subscriptions = this.subscribers.get(key) || [];
        
        subscriptions.forEach(subscription => {
            try {
                subscription.callback(value, oldValue, key);
            } catch (error) {
                Logger.error(`[StateManager] Error in subscriber '${subscription.subscriber}' for '${key}':`, error);
            }
        });
    }

    /**
     * Reset state key to default value
     * @param {string} key - State key to reset
     * @returns {boolean} Success status
     */
    reset(key) {
        if (!this.state.has(key)) {
            Logger.warn(`[StateManager] Cannot reset unknown state key: '${key}'`);
            return false;
        }

        const defaultValue = this.defaultValues.get(key);
        return this.set(key, defaultValue, true);
    }

    /**
     * Clear all subscriptions for a module (useful for cleanup)
     * @param {string} moduleName - Module name
     */
    clearModuleSubscriptions(moduleName) {
        let cleared = 0;
        
        for (const [key, subscriptions] of this.subscribers.entries()) {
            const filtered = subscriptions.filter(sub => sub.subscriber !== moduleName);
            cleared += subscriptions.length - filtered.length;
            this.subscribers.set(key, filtered);
        }

        if (cleared > 0) {
            Logger.info(`[StateManager] Cleared ${cleared} subscriptions for module '${moduleName}'`);
        }
    }

    /**
     * Get current state snapshot (for debugging)
     * @returns {Object} Current state and metadata
     */
    getSnapshot() {
        const snapshot = {
            state: Object.fromEntries(this.state),
            owners: Object.fromEntries(this.stateOwners),
            subscriptions: {}
        };

        for (const [key, subs] of this.subscribers.entries()) {
            snapshot.subscriptions[key] = subs.map(sub => ({
                id: sub.id,
                subscriber: sub.subscriber
            }));
        }

        return snapshot;
    }

    /**
     * Reset entire state manager (useful for testing)
     */
    resetAll() {
        this.state.clear();
        this.subscribers.clear();
        this.stateOwners.clear();
        this.defaultValues.clear();
        this.subscriptionCounter = 0;
        Logger.info('[StateManager] Reset complete');
    }

    /**
     * Create a scoped state manager for a specific module
     * @param {string} moduleName - Module name
     * @returns {Object} Scoped state interface
     */
    createScope(moduleName) {
        return {
            register: (key, defaultValue) => {
                this.setContext(moduleName);
                return this.register(key, defaultValue, moduleName);
            },
            
            get: (key) => this.get(key),
            
            set: (key, value) => {
                this.setContext(moduleName);
                return this.set(key, value);
            },
            
            subscribe: (key, callback) => {
                this.setContext(moduleName);
                return this.subscribe(key, callback, moduleName);
            },
            
            unsubscribe: (subscriptionId) => this.unsubscribe(subscriptionId),
            
            reset: (key) => this.reset(key),
            
            cleanup: () => this.clearModuleSubscriptions(moduleName)
        };
    }
}

// Global instance
export const stateManager = new StateManager();

// Debug access
if (typeof window !== 'undefined') {
    window.stateManager = stateManager;
    window.stateSnapshot = () => stateManager.getSnapshot();
}