// API Client with automatic token handling and caching
class APIClient {
    constructor() {
        this.apiKey = null;
        this.apiKeyExpiresAt = 0;
        this.apiKeyPromise = null;
        this.baseURL = '';
        this.cache = new Map();
        this.pendingRequests = new Map();
        this.CACHE_DURATION = 5 * 60 * 1000; // 5 minutes client-side cache for general requests
        this.SEARCH_CACHE_DURATION = 15 * 60 * 1000; // 15 minutes for search results (longer because less frequently changing)
    }

    // Initialize the API client by fetching the API key
    async init(forceRefresh = false) {
        if (!forceRefresh && this.apiKey && this.apiKeyExpiresAt && Date.now() < this.apiKeyExpiresAt) {
            return;
        }

        if (this.apiKeyPromise) {
            return this.apiKeyPromise;
        }
        
        this.apiKeyPromise = (async () => {
            const response = await fetch('/api-key', {
                method: 'GET',
                cache: 'no-cache',
                headers: {
                    'Accept': 'application/json'
                }
            });

            if (!response.ok) {
                throw new Error(`Failed to fetch API key: ${response.status} ${response.statusText}`);
            }

            const data = await response.json();
            this.apiKey = data.apiKey;
            const expiresAt = Date.parse(data.expires);
            this.apiKeyExpiresAt = Number.isFinite(expiresAt) ? expiresAt : Date.now() + 24 * 60 * 60 * 1000;
            return this.apiKey;
        })();

        try {
            return await this.apiKeyPromise;
        } finally {
            this.apiKeyPromise = null;
        }
    }

    async ensureValidApiKey() {
        if (!this.apiKey || !this.apiKeyExpiresAt || Date.now() >= this.apiKeyExpiresAt) {
            await this.init(true);
        }
    }

    // Generate signature for request (matches server-side algorithm)
    generateSignature(method, path, queryString, timestamp, apiSecret) {
        const data = `${method}${path}${queryString}`;
        // Match server-side simple hashing algorithm exactly
        let hash = 0;
        const combined = `${data}-${timestamp}-${apiSecret}`;
        for (let i = 0; i < combined.length; i++) {
            const char = combined.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash; // Convert to 32bit integer
        }
        return Math.abs(hash).toString(16);
    }

    // Make authenticated API requests with caching and in-flight dedup
    async request(endpoint, options = {}) {
        const method = options.method || 'GET';
        const authEnabled = options.auth !== false;
        const cacheKey = `${method}:${endpoint}`;
        const useCache = options.cache !== false && method === 'GET'; // Only cache GET requests

        // Determine cache duration based on endpoint type
        const isSearchRequest = endpoint.includes('/api/search/');
        const cacheDuration = isSearchRequest ? this.SEARCH_CACHE_DURATION : this.CACHE_DURATION;

        // Check cache for GET requests
        if (useCache && this.cache.has(cacheKey)) {
            const cached = this.cache.get(cacheKey);
            const age = Date.now() - cached.timestamp;

            if (age < cacheDuration) {
                console.log(`[API Client] Cache hit for ${endpoint} (${Math.floor(age / 1000)}s old)`);
                return cached.data;
            } else {
                console.log(`[API Client] Cache expired for ${endpoint}`);
                this.cache.delete(cacheKey);
            }
        }

        // In-flight dedup: if an identical GET is already in flight, share its promise
        if (useCache && this.pendingRequests.has(cacheKey)) {
            console.log(`[API Client] In-flight dedup hit for ${endpoint}`);
            return this.pendingRequests.get(cacheKey);
        }

        const promise = this._performRequest(endpoint, options, { method, authEnabled, cacheKey, useCache });

        if (useCache) {
            this.pendingRequests.set(cacheKey, promise);
            promise.finally(() => {
                if (this.pendingRequests.get(cacheKey) === promise) {
                    this.pendingRequests.delete(cacheKey);
                }
            });
        }

        return promise;
    }

    async _performRequest(endpoint, options, ctx) {
        const { method, authEnabled, cacheKey, useCache } = ctx;

        console.log('[API Client] request called for:', endpoint);

        // Auto-initialize if needed
        if (authEnabled) {
            await this.ensureValidApiKey();
        }

        if (authEnabled && !this.apiKey) {
            throw new Error('Failed to get API key after initialization');
        }

        const url = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
        const timestamp = Date.now().toString();

        const headers = {
            'Content-Type': 'application/json',
            ...options.headers
        };
        if (authEnabled) {
            headers['X-API-Key'] = this.apiKey;
            headers['X-Request-Timestamp'] = timestamp;
        }

        const finalOptions = {
            ...options,
            method,
            headers
        };

        console.log('[API Client] Making request to:', url);

        let response = await fetch(url, finalOptions);
        console.log('[API Client] Response status:', response.status);

        if (authEnabled && (response.status === 401 || response.status === 403)) {
            console.warn(`[API Client] Auth failed for ${endpoint}; refreshing API key and retrying once`);
            this.apiKey = null;
            this.apiKeyExpiresAt = 0;
            await this.init(true);
            finalOptions.headers['X-API-Key'] = this.apiKey;
            finalOptions.headers['X-Request-Timestamp'] = Date.now().toString();
            response = await fetch(url, finalOptions);
            console.log('[API Client] Retry response status:', response.status);
        }

        if (!response.ok) {
            const errorText = await response.text();
            console.error('[API Client] Request failed:', response.status, errorText);
            throw new Error(`API request failed: ${response.status} - ${errorText}`);
        }

        const data = await response.json();

        // Cache successful GET responses
        if (useCache) {
            this.cache.set(cacheKey, {
                data: data,
                timestamp: Date.now()
            });
            console.log(`[API Client] Cached response for ${endpoint}`);
        }

        return data;
    }
    
    // Clear cache manually if needed
    clearCache() {
        this.cache.clear();
        this.pendingRequests.clear();
        console.log('[API Client] Cache cleared');
    }
    
    // Get cache stats
    getCacheStats() {
        const now = Date.now();
        const stats = {
            total: this.cache.size,
            fresh: 0,
            stale: 0,
            searchEntries: 0
        };
        
        for (const [key, value] of this.cache.entries()) {
            const age = now - value.timestamp;
            const isSearch = key.includes('/api/search/');
            const maxAge = isSearch ? this.SEARCH_CACHE_DURATION : this.CACHE_DURATION;
            
            if (isSearch) stats.searchEntries++;
            
            if (age < maxAge) {
                stats.fresh++;
            } else {
                stats.stale++;
            }
        }
        
        return stats;
    }

    // Convenience methods
    async searchPlayers(nickname) {
        return this.request(`/api/search/${encodeURIComponent(nickname)}`);
    }

    async getPlayer(uid) {
        return this.request(`/api/player/${uid}`);
    }

    async getPlayerGames(uid) {
        return this.request(`/api/player/${uid}/games`);
    }

    async getStats() {
        return this.request('/api/stats', { auth: false });
    }

    // Leaderboard methods with stale-while-revalidate option
    async getPlayerLeaderboard(useStaleWhileRevalidate = true) {
        return this.requestWithSWR('/api/leaderboard/players', useStaleWhileRevalidate);
    }

    async getVehicleLeaderboard(vehicle = null, useStaleWhileRevalidate = true) {
        const endpoint = vehicle
            ? `/api/leaderboard/vehicles?vehicle=${encodeURIComponent(vehicle)}`
            : '/api/leaderboard/vehicles';
        return this.requestWithSWR(endpoint, useStaleWhileRevalidate);
    }
    
    async getSquadronLeaderboard(useStaleWhileRevalidate = true) {
        return this.requestWithSWR('/api/leaderboard/squadrons', useStaleWhileRevalidate);
    }
    
    async getSquadronDetails(squadronName, startDate, endDate) {
        let endpoint = `/api/squadrons/${encodeURIComponent(squadronName)}`;
        if (startDate || endDate) {
            const params = new URLSearchParams();
            if (startDate) params.append('start_date', startDate.toISOString());
            if (endDate) params.append('end_date', endDate.toISOString());
            endpoint += '?' + params.toString();
        }
        return this.request(endpoint);
    }

    async getSquadronGames(squadronName, startDate, endDate) {
        let endpoint = `/api/squadrons/${encodeURIComponent(squadronName)}/games`;
        if (startDate || endDate) {
            const params = new URLSearchParams();
            if (startDate) params.append('start_date', startDate.toISOString());
            if (endDate) params.append('end_date', endDate.toISOString());
            endpoint += '?' + params.toString();
        }
        return this.request(endpoint);
    }

    async getLeaderboardStats(useStaleWhileRevalidate = true) {
        return this.requestWithSWR('/api/leaderboard/stats', useStaleWhileRevalidate);
    }
    
    // Stale-While-Revalidate: Return cached data immediately, fetch fresh data in background
    async requestWithSWR(endpoint, useSWR = true) {
        const cacheKey = `GET:${endpoint}`;
        
        if (useSWR && this.cache.has(cacheKey)) {
            const cached = this.cache.get(cacheKey);
            const age = Date.now() - cached.timestamp;
            
            // If data is stale (older than cache duration) but not ancient (< 10 minutes)
            if (age >= this.CACHE_DURATION && age < 10 * 60 * 1000) {
                console.log(`[API Client] SWR: Returning stale data and revalidating ${endpoint}`);
                
                // Fetch fresh data in background (don't await)
                this.request(endpoint).catch(err => {
                    console.error('[API Client] SWR background fetch failed:', err);
                });
                
                // Return stale data immediately
                return cached.data;
            }
        }
        
        // Normal request (will use cache if fresh)
        return this.request(endpoint);
    }

    async getVehicleIcons() {
        return this.request('/api/vehicle-icons');
    }

    async getMatch(sessionId) {
        return this.request(`/api/match/${sessionId}`);
    }

    async getMatchReplay(sessionId) {
        return this.request(`/api/match/${sessionId}/replay`);
    }

    async getReplayCanvas(sessionId) {
        return this.request(`/api/match/${sessionId}/replay-canvas`);
    }

    async searchGames(params = {}) {
        const queryParams = new URLSearchParams();
        if (params.player) queryParams.append('player', params.player);
        if (params.map) queryParams.append('map', params.map);
        if (params.squadron) queryParams.append('squadron', params.squadron);
        if (params.time_from) queryParams.append('time_from', params.time_from);
        if (params.time_to) queryParams.append('time_to', params.time_to);
        if (params.limit) queryParams.append('limit', params.limit);
        return this.request(`/api/games/search?${queryParams}`);
    }

    async getMaps() {
        return this.request('/api/maps');
    }

}

// Global API client instance
window.apiClient = new APIClient();

// Pre-initialize when DOM is ready (but don't block)
document.addEventListener('DOMContentLoaded', () => {
    // Initialize in background without blocking
    window.apiClient.init().catch(() => {
        // Silently fail, will retry on first request
    });
});

// Quick init function that doesn't throw
window.ensureAPIClient = async () => {
    try {
        if (!window.apiClient) {
            console.error('[API Client] window.apiClient is not defined');
            return false;
        }
        if (!window.apiClient.apiKey) {
            await window.apiClient.init();
        }
        return true;
    } catch (error) {
        console.error('[API Client] Error in ensureAPIClient:', error);
        return false;
    }
};
