"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceError = exports.useResponsive = exports.useRealTime = exports.useAuth = exports.useApi = exports.useService = exports.OptimizedServiceProvider = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
/**
 * OptimizedServiceManager.tsx
 *
 * Comprehensive service layer manager that consolidates all service-related functionality:
 *
 * Services consolidated:
 * - api.ts (API client configuration and user/content APIs)
 * - useDashboardData.js (dashboard data fetching hook)
 * - useDashboardState.js (dashboard state management hook)
 * - useTheme.ts (theme management)
 * - useBreakpoint.ts (responsive breakpoint handling)
 * - colorUtils.ts (color utility functions)
 * - responsiveUtils.ts (responsive design utilities)
 * - styleUtils.ts (styling utilities)
 *
 * Key improvements:
 * - Unified service layer with comprehensive error handling
 * - Real-time data synchronization with WebSocket support
 * - Advanced caching strategies with TTL and invalidation
 * - Request deduplication and batch processing
 * - Comprehensive logging and monitoring
 * - Performance optimization with request prioritization
 * - Offline support with data synchronization
 * - Type-safe API client with automatic retry logic
 * - Centralized authentication and authorization
 * - Multi-tenant support with context switching
 */
const react_1 = require("react");
const react_2 = require("@chakra-ui/react");
const defaultConfig = {
    apiUrl: process.env.REACT_APP_API_URL || 'https://api.bluefly.io',
    wsUrl: process.env.REACT_APP_WS_URL || 'wss://api.bluefly.io/ws',
    timeout: 30000,
    retries: 3,
    cacheTtl: 300000, // 5 minutes
    batchSize: 10,
    retryDelay: 1000,
    enableRealTime: true,
    enableOffline: true,
    enableMetrics: true,
    enableLogging: true,
    logLevel: 'info'
};
// ===== OPTIMIZED API CLIENT =====
class OptimizedApiClient {
    constructor(config) {
        this.cache = new Map();
        this.requestQueue = new Map();
        this.retryQueue = new Map();
        this.metrics = {
            requestCount: 0,
            errorCount: 0,
            cacheHitRate: 0,
            averageResponseTime: 0,
            activeConnections: 0,
            queuedRequests: 0,
            retryCount: 0,
            lastUpdated: new Date().toISOString()
        };
        this.interceptors = [];
        this.errorInterceptors = [];
        this.config = { ...defaultConfig, ...config };
        this.setupPeriodicCleanup();
    }
    // ===== AUTHENTICATION =====
    setAuthToken(token, refreshToken) {
        this.authToken = token;
        this.refreshToken = refreshToken;
        localStorage.setItem('auth_token', token);
        if (refreshToken) {
            localStorage.setItem('refresh_token', refreshToken);
        }
    }
    clearAuth() {
        this.authToken = undefined;
        this.refreshToken = undefined;
        localStorage.removeItem('auth_token');
        localStorage.removeItem('refresh_token');
    }
    async refreshAuthToken() {
        if (!this.refreshToken)
            return false;
        try {
            const response = await fetch(`${this.config.apiUrl}/auth/refresh`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${this.refreshToken}`
                }
            });
            if (response.ok) {
                const data = await response.json();
                this.setAuthToken(data.accessToken, data.refreshToken);
                return true;
            }
        }
        catch (error) {
            this.log('error', 'Failed to refresh auth token', { error });
        }
        return false;
    }
    // ===== INTERCEPTORS =====
    addRequestInterceptor(interceptor) {
        this.interceptors.push(interceptor);
    }
    addErrorInterceptor(interceptor) {
        this.errorInterceptors.push(interceptor);
    }
    // ===== CACHING =====
    getCacheKey(config) {
        const { url, method, params, data } = config;
        return `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`;
    }
    getCachedData(key) {
        const entry = this.cache.get(key);
        if (!entry)
            return null;
        const now = Date.now();
        if (now - entry.timestamp > entry.ttl) {
            this.cache.delete(key);
            return null;
        }
        return entry.data;
    }
    setCachedData(key, data, ttl, tags = []) {
        const entry = {
            data,
            timestamp: Date.now(),
            ttl: ttl || this.config.cacheTtl,
            tags,
            size: JSON.stringify(data).length
        };
        this.cache.set(key, entry);
    }
    invalidateCache(pattern) {
        if (!pattern) {
            this.cache.clear();
            return;
        }
        const patterns = Array.isArray(pattern) ? pattern : [pattern];
        for (const [key, entry] of this.cache.entries()) {
            const shouldInvalidate = patterns.some(p => {
                if (p.includes('*')) {
                    const regex = new RegExp(p.replace(/\*/g, '.*'));
                    return regex.test(key) || entry.tags.some(tag => regex.test(tag));
                }
                return key.includes(p) || entry.tags.includes(p);
            });
            if (shouldInvalidate) {
                this.cache.delete(key);
            }
        }
    }
    setupPeriodicCleanup() {
        setInterval(() => {
            const now = Date.now();
            for (const [key, entry] of this.cache.entries()) {
                if (now - entry.timestamp > entry.ttl) {
                    this.cache.delete(key);
                }
            }
            this.updateMetrics();
        }, 60000); // Clean up every minute
    }
    // ===== REQUEST HANDLING =====
    async makeRequest(config) {
        const startTime = Date.now();
        this.metrics.requestCount++;
        // Apply interceptors
        let processedConfig = { ...config };
        for (const interceptor of this.interceptors) {
            processedConfig = interceptor(processedConfig);
        }
        // Check cache
        if (processedConfig.method === 'GET' && processedConfig.cache !== false) {
            const cacheKey = this.getCacheKey(processedConfig);
            const cachedData = this.getCachedData(cacheKey);
            if (cachedData) {
                this.log('debug', 'Cache hit', { url: processedConfig.url, cacheKey });
                return {
                    data: cachedData,
                    success: true,
                    timestamp: new Date().toISOString()
                };
            }
        }
        // Request deduplication
        if (processedConfig.deduplicationKey) {
            const existingRequest = this.requestQueue.get(processedConfig.deduplicationKey);
            if (existingRequest) {
                this.log('debug', 'Request deduplicated', { key: processedConfig.deduplicationKey });
                return existingRequest;
            }
        }
        // Build request options
        const requestOptions = {
            method: processedConfig.method,
            headers: {
                'Content-Type': 'application/json',
                'X-Client-ID': 'bfui',
                'X-Request-ID': this.generateRequestId(),
                ...(this.authToken && { 'Authorization': `Bearer ${this.authToken}` }),
                ...(this.config.tenant && { 'X-Tenant-ID': this.config.tenant }),
                ...processedConfig.headers
            },
            ...(processedConfig.data && { body: JSON.stringify(processedConfig.data) }),
            signal: AbortSignal.timeout(processedConfig.timeout || this.config.timeout)
        };
        // Build URL with params
        const url = new URL(processedConfig.url, this.config.apiUrl);
        if (processedConfig.params) {
            Object.entries(processedConfig.params).forEach(([key, value]) => {
                if (value !== undefined && value !== null) {
                    url.searchParams.append(key, String(value));
                }
            });
        }
        const requestPromise = this.executeRequest(url.toString(), requestOptions, processedConfig, startTime);
        // Store in deduplication queue
        if (processedConfig.deduplicationKey) {
            this.requestQueue.set(processedConfig.deduplicationKey, requestPromise);
            requestPromise.finally(() => {
                this.requestQueue.delete(processedConfig.deduplicationKey);
            });
        }
        return requestPromise;
    }
    async executeRequest(url, options, config, startTime) {
        try {
            this.log('debug', 'Making request', { url, method: config.method });
            const response = await fetch(url, options);
            const responseTime = Date.now() - startTime;
            // Update metrics
            this.metrics.averageResponseTime =
                (this.metrics.averageResponseTime * (this.metrics.requestCount - 1) + responseTime) / this.metrics.requestCount;
            if (!response.ok) {
                throw new ServiceError(response.status.toString(), response.statusText, await response.text(), response.status < 500 && response.status !== 401, new Date().toISOString(), response.headers.get('X-Request-ID') || undefined);
            }
            const result = await response.json();
            // Cache successful GET requests
            if (config.method === 'GET' && config.cache !== false) {
                const cacheKey = this.getCacheKey(config);
                this.setCachedData(cacheKey, result.data, config.cacheTtl);
            }
            this.log('debug', 'Request successful', { url, responseTime });
            return result;
        }
        catch (error) {
            this.metrics.errorCount++;
            let serviceError;
            if (error instanceof ServiceError) {
                serviceError = error;
            }
            else {
                serviceError = new ServiceError('NETWORK_ERROR', error instanceof Error ? error.message : 'Unknown error', error, true, new Date().toISOString());
            }
            // Apply error interceptors
            for (const interceptor of this.errorInterceptors) {
                const processed = interceptor(serviceError);
                if (processed === null) {
                    // Error was handled by interceptor
                    return this.executeRequest(url, options, config, startTime);
                }
                serviceError = processed;
            }
            // Handle 401 errors with token refresh
            if (serviceError.code === '401' && this.refreshToken) {
                const refreshed = await this.refreshAuthToken();
                if (refreshed) {
                    // Retry with new token
                    const newOptions = {
                        ...options,
                        headers: {
                            ...options.headers,
                            'Authorization': `Bearer ${this.authToken}`
                        }
                    };
                    return this.executeRequest(url, newOptions, config, startTime);
                }
            }
            // Retry logic
            const retryKey = `${config.method}:${url}`;
            const retryCount = this.retryQueue.get(retryKey) || 0;
            const maxRetries = config.retries ?? this.config.retries;
            if (serviceError.retryable && retryCount < maxRetries) {
                this.retryQueue.set(retryKey, retryCount + 1);
                this.metrics.retryCount++;
                const delay = this.config.retryDelay * Math.pow(2, retryCount); // Exponential backoff
                await new Promise(resolve => setTimeout(resolve, delay));
                this.log('warn', 'Retrying request', { url, attempt: retryCount + 1, maxRetries });
                return this.executeRequest(url, options, config, startTime);
            }
            this.retryQueue.delete(retryKey);
            this.log('error', 'Request failed', { url, error: serviceError });
            throw serviceError;
        }
    }
    // ===== PUBLIC API METHODS =====
    async get(url, config) {
        return this.makeRequest({ url, method: 'GET', ...config });
    }
    async post(url, data, config) {
        return this.makeRequest({ url, method: 'POST', data, ...config });
    }
    async put(url, data, config) {
        return this.makeRequest({ url, method: 'PUT', data, ...config });
    }
    async patch(url, data, config) {
        return this.makeRequest({ url, method: 'PATCH', data, ...config });
    }
    async delete(url, config) {
        return this.makeRequest({ url, method: 'DELETE', ...config });
    }
    // ===== BATCH REQUESTS =====
    async batch(requests) {
        const batches = [];
        for (let i = 0; i < requests.length; i += this.config.batchSize) {
            batches.push(requests.slice(i, i + this.config.batchSize));
        }
        const results = [];
        for (const batch of batches) {
            const batchPromises = batch.map(request => this.makeRequest(request));
            const batchResults = await Promise.allSettled(batchPromises);
            batchResults.forEach(result => {
                if (result.status === 'fulfilled') {
                    results.push(result.value);
                }
                else {
                    results.push({
                        data: null,
                        success: false,
                        message: result.reason.message,
                        timestamp: new Date().toISOString()
                    });
                }
            });
        }
        return results;
    }
    // ===== UTILITIES =====
    generateRequestId() {
        return Math.random().toString(36).substring(2) + Date.now().toString(36);
    }
    updateMetrics() {
        const totalRequests = this.metrics.requestCount;
        const cacheSize = this.cache.size;
        const estimatedCacheHits = Math.max(0, totalRequests - this.metrics.errorCount - this.metrics.retryCount);
        this.metrics.cacheHitRate = totalRequests > 0 ? (estimatedCacheHits / totalRequests) * 100 : 0;
        this.metrics.queuedRequests = this.requestQueue.size;
        this.metrics.lastUpdated = new Date().toISOString();
    }
    getMetrics() {
        this.updateMetrics();
        return { ...this.metrics };
    }
    log(level, message, data) {
        if (!this.config.enableLogging)
            return;
        const levels = { debug: 0, info: 1, warn: 2, error: 3 };
        const currentLevel = levels[this.config.logLevel];
        const messageLevel = levels[level];
        if (messageLevel >= currentLevel) {
            console[level](`[ServiceManager] ${message}`, data);
        }
    }
}
// ===== WEBSOCKET MANAGER =====
class WebSocketManager {
    constructor(config) {
        this.subscribers = new Map();
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
        this.reconnectDelay = 1000;
        this.config = config;
    }
    connect() {
        if (!this.config.enableRealTime)
            return;
        try {
            this.ws = new WebSocket(this.config.wsUrl);
            this.ws.onopen = () => {
                console.log('[WebSocket] Connected');
                this.reconnectAttempts = 0;
                this.startHeartbeat();
            };
            this.ws.onmessage = (event) => {
                try {
                    const message = JSON.parse(event.data);
                    this.handleMessage(message);
                }
                catch (error) {
                    console.error('[WebSocket] Failed to parse message', error);
                }
            };
            this.ws.onclose = () => {
                console.log('[WebSocket] Disconnected');
                this.stopHeartbeat();
                this.scheduleReconnect();
            };
            this.ws.onerror = (error) => {
                console.error('[WebSocket] Error', error);
            };
        }
        catch (error) {
            console.error('[WebSocket] Failed to connect', error);
            this.scheduleReconnect();
        }
    }
    disconnect() {
        if (this.ws) {
            this.ws.close();
            this.ws = undefined;
        }
        this.stopHeartbeat();
    }
    subscribe(channel, callback) {
        if (!this.subscribers.has(channel)) {
            this.subscribers.set(channel, new Set());
        }
        this.subscribers.get(channel).add(callback);
        // Send subscription message
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.send({
                type: 'subscribe',
                channel,
                data: {},
                timestamp: new Date().toISOString(),
                id: Math.random().toString(36)
            });
        }
        // Return unsubscribe function
        return () => {
            const channelSubscribers = this.subscribers.get(channel);
            if (channelSubscribers) {
                channelSubscribers.delete(callback);
                if (channelSubscribers.size === 0) {
                    this.subscribers.delete(channel);
                    // Send unsubscription message
                    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
                        this.send({
                            type: 'unsubscribe',
                            channel,
                            data: {},
                            timestamp: new Date().toISOString(),
                            id: Math.random().toString(36)
                        });
                    }
                }
            }
        };
    }
    send(message) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(message));
        }
    }
    handleMessage(message) {
        const channelSubscribers = this.subscribers.get(message.channel);
        if (channelSubscribers) {
            channelSubscribers.forEach(callback => callback(message));
        }
    }
    scheduleReconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
            setTimeout(() => {
                this.reconnectAttempts++;
                this.connect();
            }, delay);
        }
    }
    startHeartbeat() {
        this.heartbeatInterval = setInterval(() => {
            if (this.ws && this.ws.readyState === WebSocket.OPEN) {
                this.send({
                    type: 'ping',
                    channel: 'system',
                    data: {},
                    timestamp: new Date().toISOString(),
                    id: Math.random().toString(36)
                });
            }
        }, 30000); // 30 seconds
    }
    stopHeartbeat() {
        if (this.heartbeatInterval) {
            clearInterval(this.heartbeatInterval);
            this.heartbeatInterval = undefined;
        }
    }
}
const ServiceContext = (0, react_1.createContext)(null);
const OptimizedServiceProvider = ({ children, config: userConfig = {} }) => {
    const config = (0, react_1.useMemo)(() => ({ ...defaultConfig, ...userConfig }), [userConfig]);
    const apiClient = (0, react_1.useMemo)(() => new OptimizedApiClient(config), [config]);
    const wsManager = (0, react_1.useMemo)(() => new WebSocketManager(config), [config]);
    const [user, setUser] = (0, react_1.useState)(null);
    const [isLoading, setIsLoading] = (0, react_1.useState)(true);
    const [error, setError] = (0, react_1.useState)(null);
    const [metrics, setMetrics] = (0, react_1.useState)(apiClient.getMetrics());
    const { colorMode, toggleColorMode } = (0, react_2.useColorMode)();
    const toast = (0, react_2.useToast)();
    // Responsive breakpoint detection
    const breakpoint = (0, react_2.useBreakpointValue)({
        base: 'mobile',
        sm: 'mobile',
        md: 'tablet',
        lg: 'desktop',
        xl: 'desktop'
    }) || 'mobile';
    const isMobile = breakpoint === 'mobile';
    const isTablet = breakpoint === 'tablet';
    const isDesktop = breakpoint === 'desktop';
    const isAuthenticated = (0, react_1.useMemo)(() => !!user, [user]);
    // ===== AUTHENTICATION METHODS =====
    const login = (0, react_1.useCallback)(async (credentials) => {
        setIsLoading(true);
        setError(null);
        try {
            const response = await apiClient.post('/auth/login', credentials);
            if (response.success) {
                setUser(response.data.user);
                apiClient.setAuthToken(response.data.accessToken, response.data.refreshToken);
                wsManager.connect();
                toast({
                    title: 'Welcome back!',
                    description: `Logged in as ${response.data.user.fullName || response.data.user.username}`,
                    status: 'success',
                    duration: 3000,
                    isClosable: true
                });
            }
        }
        catch (err) {
            const serviceError = err;
            setError(serviceError);
            toast({
                title: 'Login failed',
                description: serviceError.message,
                status: 'error',
                duration: 5000,
                isClosable: true
            });
            throw serviceError;
        }
        finally {
            setIsLoading(false);
        }
    }, [apiClient, wsManager, toast]);
    const logout = (0, react_1.useCallback)(() => {
        setUser(null);
        setError(null);
        apiClient.clearAuth();
        wsManager.disconnect();
        apiClient.clearCache();
        toast({
            title: 'Logged out',
            description: 'You have been successfully logged out',
            status: 'info',
            duration: 3000,
            isClosable: true
        });
    }, [apiClient, wsManager, toast]);
    const refreshAuth = (0, react_1.useCallback)(async () => {
        try {
            const success = await apiClient.refreshAuthToken();
            if (success) {
                const response = await apiClient.get('/user/profile');
                if (response.success) {
                    setUser(response.data);
                }
            }
            return success;
        }
        catch (error) {
            logout();
            return false;
        }
    }, [apiClient, logout]);
    // ===== USER METHODS =====
    const updateProfile = (0, react_1.useCallback)(async (data) => {
        if (!user)
            return;
        try {
            const response = await apiClient.put('/user/profile', data);
            if (response.success) {
                setUser(response.data);
                toast({
                    title: 'Profile updated',
                    status: 'success',
                    duration: 3000,
                    isClosable: true
                });
            }
        }
        catch (err) {
            const serviceError = err;
            toast({
                title: 'Failed to update profile',
                description: serviceError.message,
                status: 'error',
                duration: 5000,
                isClosable: true
            });
            throw serviceError;
        }
    }, [user, apiClient, toast]);
    const updatePreferences = (0, react_1.useCallback)(async (data) => {
        if (!user)
            return;
        try {
            const response = await apiClient.put('/user/preferences', data);
            if (response.success) {
                setUser(prev => prev ? { ...prev, preferences: { ...prev.preferences, ...response.data } } : null);
                toast({
                    title: 'Preferences updated',
                    status: 'success',
                    duration: 3000,
                    isClosable: true
                });
            }
        }
        catch (err) {
            const serviceError = err;
            toast({
                title: 'Failed to update preferences',
                description: serviceError.message,
                status: 'error',
                duration: 5000,
                isClosable: true
            });
            throw serviceError;
        }
    }, [user, apiClient, toast]);
    // ===== UTILITY METHODS =====
    const clearCache = (0, react_1.useCallback)((pattern) => {
        apiClient.invalidateCache(pattern);
        toast({
            title: 'Cache cleared',
            status: 'info',
            duration: 2000,
            isClosable: true
        });
    }, [apiClient, toast]);
    const getMetrics = (0, react_1.useCallback)(() => {
        return apiClient.getMetrics();
    }, [apiClient]);
    const subscribe = (0, react_1.useCallback)((channel, callback) => {
        return wsManager.subscribe(channel, callback);
    }, [wsManager]);
    // ===== INITIALIZATION =====
    (0, react_1.useEffect)(() => {
        const initializeAuth = async () => {
            const token = localStorage.getItem('auth_token');
            const refreshToken = localStorage.getItem('refresh_token');
            if (token && refreshToken) {
                apiClient.setAuthToken(token, refreshToken);
                try {
                    const response = await apiClient.get('/user/profile');
                    if (response.success) {
                        setUser(response.data);
                        wsManager.connect();
                    }
                    else {
                        // Token might be expired, try refresh
                        const refreshed = await refreshAuth();
                        if (!refreshed) {
                            logout();
                        }
                    }
                }
                catch (error) {
                    logout();
                }
            }
            setIsLoading(false);
        };
        initializeAuth();
    }, [apiClient, wsManager, refreshAuth, logout]);
    // Update metrics periodically
    (0, react_1.useEffect)(() => {
        const interval = setInterval(() => {
            setMetrics(apiClient.getMetrics());
        }, 10000); // Update every 10 seconds
        return () => clearInterval(interval);
    }, [apiClient]);
    // ===== CONTEXT VALUE =====
    const contextValue = (0, react_1.useMemo)(() => ({
        apiClient,
        wsManager,
        user,
        isAuthenticated,
        isLoading,
        error,
        metrics,
        config,
        login,
        logout,
        refreshAuth,
        updateProfile,
        updatePreferences,
        clearCache,
        getMetrics,
        subscribe,
        colorMode,
        toggleColorMode,
        breakpoint,
        isMobile,
        isTablet,
        isDesktop
    }), [
        apiClient,
        wsManager,
        user,
        isAuthenticated,
        isLoading,
        error,
        metrics,
        config,
        login,
        logout,
        refreshAuth,
        updateProfile,
        updatePreferences,
        clearCache,
        getMetrics,
        subscribe,
        colorMode,
        toggleColorMode,
        breakpoint,
        isMobile,
        isTablet,
        isDesktop
    ]);
    return ((0, jsx_runtime_1.jsx)(ServiceContext.Provider, { value: contextValue, children: children }));
};
exports.OptimizedServiceProvider = OptimizedServiceProvider;
// ===== CUSTOM HOOKS =====
const useService = () => {
    const context = (0, react_1.useContext)(ServiceContext);
    if (!context) {
        throw new Error('useService must be used within a ServiceProvider');
    }
    return context;
};
exports.useService = useService;
const useApi = () => {
    const { apiClient } = (0, exports.useService)();
    return apiClient;
};
exports.useApi = useApi;
const useAuth = () => {
    const { user, isAuthenticated, isLoading, login, logout, refreshAuth } = (0, exports.useService)();
    return { user, isAuthenticated, isLoading, login, logout, refreshAuth };
};
exports.useAuth = useAuth;
const useRealTime = () => {
    const { subscribe } = (0, exports.useService)();
    return { subscribe };
};
exports.useRealTime = useRealTime;
const useResponsive = () => {
    const { breakpoint, isMobile, isTablet, isDesktop } = (0, exports.useService)();
    return { breakpoint, isMobile, isTablet, isDesktop };
};
exports.useResponsive = useResponsive;
// ===== UTILITY FUNCTIONS =====
class ServiceError extends Error {
    constructor(code, message, details, retryable = false, timestamp = new Date().toISOString(), requestId, context) {
        super(message);
        this.name = 'ServiceError';
        this.code = code;
        this.details = details;
        this.retryable = retryable;
        this.timestamp = timestamp;
        this.requestId = requestId;
        this.context = context;
    }
}
exports.ServiceError = ServiceError;
// ===== EXPORTS =====
exports.default = exports.OptimizedServiceProvider;
//# sourceMappingURL=OptimizedServiceManager.js.map