import axios from 'axios';

const api = axios.create({
    baseURL: process.env.NEXT_PUBLIC_API_URL || '',
    validateStatus: (status) => status >= 200 && status < 300,
    withCredentials: true,
});

// Response interceptor: on a 401, first try to silently renew the 30-min
// access token from the (still valid, 7-day) refresh cookie and retry the
// original request — so an actively-used session is NOT bounced to /login every
// 30 minutes. Only when the refresh itself fails (refresh token expired/revoked
// = genuinely inactive) do we redirect. Applies to all roles.
//
// Single-flight: the backend ROTATES the refresh token (revokes it on use), so
// concurrent 401s must share ONE refresh call — otherwise the 2nd+ would send
// the already-revoked cookie and force a spurious logout.
// Role-gated requests. A read-only user loading Assets or Vulnerabilities
// fired /auth/users on every page, which the backend correctly refused — a
// steady stream of 403s in the log for a call whose result that role can never
// use (the assignment dropdowns it feeds are hidden for readonly anyway).
//
// The role is picked up from whatever /auth/me response goes past, so no page
// has to be rearranged and no extra round-trip is added. Until the first
// /auth/me lands the role is unknown and requests behave exactly as before.
let cachedRole: string | null = null;
// /api/v1/settings/<key> is admin-only for every key, and the Settings page
// requests a dozen of them on open — one 403 each. The list endpoint and the
// public bits are elsewhere, so the whole prefix can go.
const READONLY_DENIED = ['/auth/users', '/api/v1/settings/'];

api.interceptors.request.use((config) => {
    const url = config.url || '';
    if (cachedRole === 'readonly' && READONLY_DENIED.some(p => url.includes(p))) {
        // Reject before sending. Every caller of these already handles a
        // failure with an empty-list fallback, which is the correct result
        // here — this role has no users list.
        return Promise.reject(Object.assign(new Error('skipped: read-only role'),
                                            { _roleSkipped: true, config }));
    }
    return config;
});

let refreshInFlight: Promise<unknown> | null = null;
api.interceptors.response.use(
    (response) => {
        if ((response.config?.url || '').includes('/auth/me')) {
            cachedRole = (response.data as { role?: string })?.role ?? cachedRole;
        }
        return response;
    },
    async (error) => {
        const original = error.config || {};
        const url: string = original.url || '';
        // Endpoints that must NOT trigger a refresh-retry (would loop / are the
        // auth handshake itself).
        const isAuthEndpoint =
            url.includes('/auth/login') ||
            url.includes('/auth/refresh') ||
            url.includes('/auth/mfa/');
        if (error.response?.status === 401 && !isAuthEndpoint && !original._retried) {
            original._retried = true;
            try {
                refreshInFlight = refreshInFlight || api.post('/auth/refresh');
                await refreshInFlight;             // cookie sent via withCredentials
                return api(original);              // replay the original request
            } catch {
                // fall through to the redirect below
            } finally {
                refreshInFlight = null;
            }
        }
        if (error.response?.status === 401) {
            // /auth/me is a soft check (no redirect — Navigation handles it);
            // everything else bounces to login once refresh is exhausted.
            const isSoftCheck = url.includes('/auth/me') || isAuthEndpoint;
            const path = typeof window !== 'undefined' ? window.location.pathname : '';
            const onAuthPage = path === '/login' || path.startsWith('/mfa-setup');
            if (!isSoftCheck && !onAuthPage && typeof window !== 'undefined') {
                window.location.href = '/login';
            }
        }
        return Promise.reject(error);
    }
);

export default api;
