import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';

// Retry configuration
const MAX_RETRIES = 3;
const RETRY_DELAY_BASE = 1000; // 1 second

// Request queue for offline mode
let isRefreshing = false;
let failedQueue: Array<{
    resolve: (value?: unknown) => void;
    reject: (reason?: unknown) => void;
}> = [];

const processQueue = (error: unknown = null) => {
    failedQueue.forEach(prom => {
        if (error) {
            prom.reject(error);
        } else {
            prom.resolve();
        }
    });
    failedQueue = [];
};

const api = axios.create({
    baseURL: import.meta.env.VITE_API_URL || '/api',
    headers: {
        'Content-Type': 'application/json',
    },
    timeout: 30000, // 30 second timeout
});

// Request interceptor - add auth token
api.interceptors.request.use((config) => {
    const token = localStorage.getItem('token');
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
});

// Response interceptor - handle errors and retries
api.interceptors.response.use(
    (response) => response,
    async (error: AxiosError) => {
        const originalRequest = error.config as InternalAxiosRequestConfig & { _retryCount?: number };

        if (!originalRequest) {
            return Promise.reject(error);
        }

        // Handle 401 Unauthorized - token expired
        if (error.response?.status === 401) {
            // If already refreshing, queue this request
            if (isRefreshing) {
                return new Promise((resolve, reject) => {
                    failedQueue.push({ resolve, reject });
                }).then(() => api(originalRequest));
            }

            // Try to refresh token
            isRefreshing = true;

            try {
                const refreshToken = localStorage.getItem('refreshToken');
                if (refreshToken) {
                    const { data } = await axios.post(
                        `${import.meta.env.VITE_API_URL || '/api'}/auth/refresh`,
                        { refreshToken }
                    );

                    localStorage.setItem('token', data.accessToken);
                    if (data.refreshToken) {
                        localStorage.setItem('refreshToken', data.refreshToken);
                    }

                    processQueue();
                    return api(originalRequest);
                }
            } catch (refreshError) {
                processQueue(refreshError);
                // Clear tokens and redirect to login
                localStorage.removeItem('token');
                localStorage.removeItem('refreshToken');
                localStorage.removeItem('user');
                window.location.href = '/login';
                return Promise.reject(refreshError);
            } finally {
                isRefreshing = false;
            }
        }

        // Retry logic for network errors and 5xx errors
        const shouldRetry = (
            !error.response || // Network error
            (error.response.status >= 500 && error.response.status < 600) // Server error
        );

        if (shouldRetry) {
            originalRequest._retryCount = originalRequest._retryCount || 0;

            if (originalRequest._retryCount < MAX_RETRIES) {
                originalRequest._retryCount++;

                // Exponential backoff: 1s, 2s, 4s
                const delay = RETRY_DELAY_BASE * Math.pow(2, originalRequest._retryCount - 1);

                await new Promise(resolve => setTimeout(resolve, delay));

                console.log(`Retrying request (${originalRequest._retryCount}/${MAX_RETRIES}):`, originalRequest.url);
                return api(originalRequest);
            }
        }

        // Handle rate limiting (429)
        if (error.response?.status === 429) {
            const retryAfter = error.response.headers['retry-after'];
            const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1000 : 60000;

            // Dispatch event so UI can show a message
            window.dispatchEvent(new CustomEvent('api:rate-limited', {
                detail: { waitTime, url: originalRequest.url }
            }));
        }

        return Promise.reject(error);
    }
);

export default api;

