/**
 * Simplyin HyvaPayment - Session Storage Utility
 * Manages client-side session data for authentication state persistence
 * 
 * Requirements: 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7
 * 
 * Usage:
 * // Store auth token
 * SessionStorage.setAuthToken('token123');
 * 
 * // Retrieve auth token
 * const token = SessionStorage.getAuthToken();
 * 
 * // Store user data
 * SessionStorage.setUserData({ addresses: [], paymentMethods: [] });
 * 
 * // Clear all Simplyin data
 * SessionStorage.clearAll();
 */

'use strict';

/**
 * SessionStorage Utility
 * Provides session storage management with expiration support
 * Uses 'simplyin_' prefix for all keys to avoid conflicts
 * 
 * Validates: Requirements 14.1-14.7
 */
const SessionStorage = {
    // ==========================================
    // Constants
    // ==========================================
    
    /**
     * Prefix for all session storage keys
     * @type {string}
     */
    PREFIX: 'simplyin_',
    
    /**
     * Default expiration time in milliseconds (1 hour)
     * @type {number}
     */
    EXPIRATION_TIME: 60 * 60 * 1000,
    
    /**
     * Storage keys
     * @type {Object}
     */
    KEYS: {
        AUTH_TOKEN: 'auth_token',
        USER_DATA: 'user_data',
        NEW_USER_FLAG: 'new_user_flag',
        PENDING_EMAIL: 'pending_email',
        NOTIFICATION_TOKEN_ID: 'notification_token_id',
        PAYMENT_STATE: 'payment_state',
        THREE_DS_DATA: '3ds_data',
        EXPIRATION: 'expiration'
    },
    
    // ==========================================
    // Core Storage Methods
    // ==========================================
    
    /**
     * Set an item in session storage with the Simplyin prefix
     * Automatically stores expiration timestamp
     * 
     * @param {string} key - The storage key (without prefix)
     * @param {*} value - The value to store (will be JSON stringified)
     * @returns {boolean} True if storage was successful
     */
    setItem(key, value) {
        try {
            const prefixedKey = this.PREFIX + key;
            const storageValue = {
                value: value,
                timestamp: Date.now()
            };
            
            sessionStorage.setItem(prefixedKey, JSON.stringify(storageValue));
            
            // Update expiration timestamp
            this._updateExpiration();
            
            return true;
        } catch (error) {
            console.error('[Simplyin SessionStorage] Error setting item:', key, error);
            return false;
        }
    },
    
    /**
     * Get an item from session storage
     * Returns null if item doesn't exist or is expired
     * 
     * @param {string} key - The storage key (without prefix)
     * @returns {*} The stored value or null if not found/expired
     */
    getItem(key) {
        try {
            // Check if session is expired first
            if (this.isExpired()) {
                this.clearAll();
                return null;
            }
            
            const prefixedKey = this.PREFIX + key;
            const storedData = sessionStorage.getItem(prefixedKey);
            
            if (!storedData) {
                return null;
            }
            
            const parsed = JSON.parse(storedData);
            return parsed.value;
        } catch (error) {
            console.error('[Simplyin SessionStorage] Error getting item:', key, error);
            return null;
        }
    },

    /**
     * Remove an item from session storage
     * 
     * @param {string} key - The storage key (without prefix)
     * @returns {boolean} True if removal was successful
     */
    removeItem(key) {
        try {
            const prefixedKey = this.PREFIX + key;
            sessionStorage.removeItem(prefixedKey);
            return true;
        } catch (error) {
            console.error('[Simplyin SessionStorage] Error removing item:', key, error);
            return false;
        }
    },
    
    /**
     * Clear all Simplyin-related data from session storage
     * Validates: Requirement 14.7 (clear sensitive data on logout/expiration)
     * 
     * @returns {boolean} True if clearing was successful
     */
    clearAll() {
        try {
            const keysToRemove = [];
            
            // Find all keys with our prefix
            for (let i = 0; i < sessionStorage.length; i++) {
                const key = sessionStorage.key(i);
                if (key && key.startsWith(this.PREFIX)) {
                    keysToRemove.push(key);
                }
            }
            
            // Remove all found keys
            keysToRemove.forEach(key => {
                sessionStorage.removeItem(key);
            });
            
            return true;
        } catch (error) {
            console.error('[Simplyin SessionStorage] Error clearing all items:', error);
            return false;
        }
    },
    
    /**
     * Check if the session has expired
     * Validates: Requirement 14.7 (handle expiration)
     * 
     * @returns {boolean} True if session is expired
     */
    isExpired() {
        try {
            const prefixedKey = this.PREFIX + this.KEYS.EXPIRATION;
            const expirationData = sessionStorage.getItem(prefixedKey);
            
            if (!expirationData) {
                return false; // No expiration set, not expired
            }
            
            const parsed = JSON.parse(expirationData);
            const expirationTime = parsed.value;
            
            return Date.now() > expirationTime;
        } catch (error) {
            console.error('[Simplyin SessionStorage] Error checking expiration:', error);
            return false;
        }
    },
    
    // ==========================================
    // Auth Token Methods
    // Validates: Requirement 14.1
    // ==========================================
    
    /**
     * Get the stored authentication token
     * Validates: Requirement 14.1 (store auth token in session storage)
     * 
     * @returns {string|null} The auth token or null if not found
     */
    getAuthToken() {
        return this.getItem(this.KEYS.AUTH_TOKEN);
    },
    
    /**
     * Set the authentication token
     * Validates: Requirement 14.1 (store auth token in session storage)
     * 
     * @param {string} token - The authentication token
     * @returns {boolean} True if storage was successful
     */
    setAuthToken(token) {
        return this.setItem(this.KEYS.AUTH_TOKEN, token);
    },
    
    /**
     * Remove the authentication token
     * 
     * @returns {boolean} True if removal was successful
     */
    removeAuthToken() {
        return this.removeItem(this.KEYS.AUTH_TOKEN);
    },
    
    // ==========================================
    // User Data Methods
    // Validates: Requirement 14.2
    // ==========================================
    
    /**
     * Get the stored user data (addresses, payment methods)
     * Validates: Requirement 14.2 (store user data including addresses and payment methods)
     * 
     * @returns {Object|null} The user data object or null if not found
     */
    getUserData() {
        return this.getItem(this.KEYS.USER_DATA);
    },
    
    /**
     * Set the user data
     * Validates: Requirement 14.2 (store user data including addresses and payment methods)
     * 
     * @param {Object} data - The user data object
     * @param {Array} data.addresses - User's saved addresses
     * @param {Array} data.paymentMethods - User's saved payment methods
     * @returns {boolean} True if storage was successful
     */
    setUserData(data) {
        return this.setItem(this.KEYS.USER_DATA, data);
    },
    
    /**
     * Remove the user data
     * 
     * @returns {boolean} True if removal was successful
     */
    removeUserData() {
        return this.removeItem(this.KEYS.USER_DATA);
    },
    
    // ==========================================
    // New User Flag Methods
    // Validates: Requirement 14.3
    // ==========================================
    
    /**
     * Get the new user flag
     * Validates: Requirement 14.3 (store new user flag for account creation flow)
     * 
     * @returns {boolean|null} The new user flag or null if not found
     */
    getNewUserFlag() {
        return this.getItem(this.KEYS.NEW_USER_FLAG);
    },
    
    /**
     * Set the new user flag
     * Validates: Requirement 14.3 (store new user flag for account creation flow)
     * 
     * @param {boolean} isNewUser - Whether the user is new
     * @returns {boolean} True if storage was successful
     */
    setNewUserFlag(isNewUser) {
        return this.setItem(this.KEYS.NEW_USER_FLAG, isNewUser);
    },
    
    /**
     * Remove the new user flag
     * 
     * @returns {boolean} True if removal was successful
     */
    removeNewUserFlag() {
        return this.removeItem(this.KEYS.NEW_USER_FLAG);
    },

    // ==========================================
    // Pending Email Methods
    // Validates: Requirement 14.4
    // ==========================================
    
    /**
     * Get the pending email for push notification recovery
     * Validates: Requirement 14.4 (store pending email for push notification recovery)
     * 
     * @returns {string|null} The pending email or null if not found
     */
    getPendingEmail() {
        return this.getItem(this.KEYS.PENDING_EMAIL);
    },
    
    /**
     * Set the pending email for push notification recovery
     * Validates: Requirement 14.4 (store pending email for push notification recovery)
     * 
     * @param {string} email - The pending email address
     * @returns {boolean} True if storage was successful
     */
    setPendingEmail(email) {
        return this.setItem(this.KEYS.PENDING_EMAIL, email);
    },
    
    /**
     * Remove the pending email
     * 
     * @returns {boolean} True if removal was successful
     */
    removePendingEmail() {
        return this.removeItem(this.KEYS.PENDING_EMAIL);
    },
    
    // ==========================================
    // Notification Token ID Methods
    // Validates: Requirement 14.5
    // ==========================================
    
    /**
     * Get the notification token ID for push notification polling
     * Validates: Requirement 14.5 (store notification token ID for push notification polling)
     * 
     * @returns {string|null} The notification token ID or null if not found
     */
    getNotificationTokenId() {
        return this.getItem(this.KEYS.NOTIFICATION_TOKEN_ID);
    },
    
    /**
     * Set the notification token ID for push notification polling
     * Validates: Requirement 14.5 (store notification token ID for push notification polling)
     * 
     * @param {string} tokenId - The notification token ID
     * @returns {boolean} True if storage was successful
     */
    setNotificationTokenId(tokenId) {
        return this.setItem(this.KEYS.NOTIFICATION_TOKEN_ID, tokenId);
    },
    
    /**
     * Remove the notification token ID
     * 
     * @returns {boolean} True if removal was successful
     */
    removeNotificationTokenId() {
        return this.removeItem(this.KEYS.NOTIFICATION_TOKEN_ID);
    },
    
    // ==========================================
    // Payment State Methods
    // ==========================================
    
    /**
     * Get the current payment state
     * Used for tracking payment progress and recovery
     * 
     * @returns {Object|null} The payment state object or null if not found
     */
    getPaymentState() {
        return this.getItem(this.KEYS.PAYMENT_STATE);
    },
    
    /**
     * Set the payment state
     * Used for tracking payment progress and recovery
     * 
     * @param {Object} state - The payment state object
     * @param {string} state.paymentId - The payment ID
     * @param {string} state.orderId - The order ID
     * @param {string} state.magentoQuoteId - The Magento quote ID
     * @param {string} state.paymentMethod - The payment method used
     * @param {string} state.paymentStatus - Current payment status
     * @param {string} [state.redirectUrl] - Optional redirect URL for 3DS
     * @param {number} state.lastUpdated - Timestamp of last update
     * @returns {boolean} True if storage was successful
     */
    setPaymentState(state) {
        // Ensure lastUpdated is set
        const stateWithTimestamp = {
            ...state,
            lastUpdated: Date.now()
        };
        return this.setItem(this.KEYS.PAYMENT_STATE, stateWithTimestamp);
    },
    
    /**
     * Remove the payment state
     * 
     * @returns {boolean} True if removal was successful
     */
    removePaymentState() {
        return this.removeItem(this.KEYS.PAYMENT_STATE);
    },
    
    // ==========================================
    // 3D Secure Data Methods
    // ==========================================
    
    /**
     * Get the 3D Secure data
     * Used for handling 3DS redirects and recovery
     * 
     * @returns {Object|null} The 3DS data object or null if not found
     */
    get3DSData() {
        return this.getItem(this.KEYS.THREE_DS_DATA);
    },
    
    /**
     * Set the 3D Secure data
     * Used for handling 3DS redirects and recovery
     * 
     * @param {Object} data - The 3DS data object
     * @param {string} data.paymentId - The payment ID
     * @param {string} data.redirectUrl - The 3DS redirect URL
     * @param {boolean} data.allowIframe - Whether iframe is allowed
     * @param {string} data.returnUrl - The return URL after 3DS
     * @returns {boolean} True if storage was successful
     */
    set3DSData(data) {
        return this.setItem(this.KEYS.THREE_DS_DATA, data);
    },
    
    /**
     * Remove the 3D Secure data
     * 
     * @returns {boolean} True if removal was successful
     */
    remove3DSData() {
        return this.removeItem(this.KEYS.THREE_DS_DATA);
    },
    
    // ==========================================
    // Convenience Methods
    // ==========================================
    
    /**
     * Clear all payment-related data
     * Useful after payment completion or cancellation
     * 
     * @returns {boolean} True if clearing was successful
     */
    clearPaymentData() {
        const paymentCleared = this.removePaymentState();
        const threeDSCleared = this.remove3DSData();
        return paymentCleared && threeDSCleared;
    },
    
    /**
     * Check if user is authenticated
     * Validates: Requirement 14.6 (restore authentication state from storage)
     * 
     * @returns {boolean} True if auth token exists and session is not expired
     */
    isAuthenticated() {
        if (this.isExpired()) {
            this.clearAll();
            return false;
        }
        
        const token = this.getAuthToken();
        return token !== null && token !== '';
    },
    
    /**
     * Restore authentication state from storage
     * Validates: Requirement 14.6 (restore authentication state when checkout page loads)
     * 
     * @returns {Object} Object containing authentication state
     */
    restoreAuthState() {
        if (this.isExpired()) {
            this.clearAll();
            return {
                isAuthenticated: false,
                authToken: null,
                userData: null,
                isNewUser: false,
                pendingEmail: null,
                notificationTokenId: null
            };
        }
        
        return {
            isAuthenticated: this.isAuthenticated(),
            authToken: this.getAuthToken(),
            userData: this.getUserData(),
            isNewUser: this.getNewUserFlag() || false,
            pendingEmail: this.getPendingEmail(),
            notificationTokenId: this.getNotificationTokenId()
        };
    },
    
    /**
     * Store complete authentication state
     * Convenience method to store all auth-related data at once
     * 
     * @param {Object} authState - The authentication state
     * @param {string} authState.authToken - The auth token
     * @param {Object} authState.userData - The user data
     * @param {boolean} [authState.isNewUser] - Whether user is new
     * @returns {boolean} True if all storage operations were successful
     */
    storeAuthState(authState) {
        let success = true;
        
        if (authState.authToken) {
            success = success && this.setAuthToken(authState.authToken);
        }
        
        if (authState.userData) {
            success = success && this.setUserData(authState.userData);
        }
        
        if (typeof authState.isNewUser === 'boolean') {
            success = success && this.setNewUserFlag(authState.isNewUser);
        }
        
        return success;
    },
    
    /**
     * Logout - clear all sensitive data
     * Validates: Requirement 14.7 (clear sensitive data when user logs out)
     * 
     * @returns {boolean} True if clearing was successful
     */
    logout() {
        return this.clearAll();
    },
    
    // ==========================================
    // Private Helper Methods
    // ==========================================
    
    /**
     * Update the expiration timestamp
     * Called whenever data is stored to extend the session
     * 
     * @private
     */
    _updateExpiration() {
        try {
            const expirationTime = Date.now() + this.EXPIRATION_TIME;
            const prefixedKey = this.PREFIX + this.KEYS.EXPIRATION;
            const storageValue = {
                value: expirationTime,
                timestamp: Date.now()
            };
            
            sessionStorage.setItem(prefixedKey, JSON.stringify(storageValue));
        } catch (error) {
            console.error('[Simplyin SessionStorage] Error updating expiration:', error);
        }
    },
    
    /**
     * Get all stored keys (for debugging)
     * 
     * @returns {string[]} Array of stored keys (without prefix)
     */
    getAllKeys() {
        const keys = [];
        
        for (let i = 0; i < sessionStorage.length; i++) {
            const key = sessionStorage.key(i);
            if (key && key.startsWith(this.PREFIX)) {
                keys.push(key.substring(this.PREFIX.length));
            }
        }
        
        return keys;
    },
    
    /**
     * Get storage info (for debugging)
     * 
     * @returns {Object} Object containing storage statistics
     */
    getStorageInfo() {
        const keys = this.getAllKeys();
        let totalSize = 0;
        
        keys.forEach(key => {
            const prefixedKey = this.PREFIX + key;
            const value = sessionStorage.getItem(prefixedKey);
            if (value) {
                totalSize += value.length;
            }
        });
        
        return {
            keyCount: keys.length,
            keys: keys,
            totalSize: totalSize,
            isExpired: this.isExpired(),
            isAuthenticated: this.isAuthenticated()
        };
    }
};

// Export for use in templates
if (typeof window !== 'undefined') {
    window.SessionStorage = SessionStorage;
}

// Export for module systems (testing)
if (typeof module !== 'undefined' && module.exports) {
    module.exports = { SessionStorage };
}
