const AUTH_TOKEN_KEY = 'auth_token';
const REFRESH_TOKEN_KEY = 'refresh_token';
const REFRESH_ENDPOINT = '/api/token/refresh';
const AUTH_CHECK_ENDPOINT = '/api/me';

let refreshRequestInFlight = null;
let authCheckRequestInFlight = null;

export function getAuthToken() {
    return String(window.localStorage.getItem(AUTH_TOKEN_KEY) || '').trim();
}

export function getRefreshToken() {
    return String(window.localStorage.getItem(REFRESH_TOKEN_KEY) || '').trim();
}

export function storeTokens(accessToken, refreshToken) {
    const normalizedAccess = String(accessToken || '').trim();
    const normalizedRefresh = String(refreshToken || '').trim();

    if (normalizedAccess !== '') {
        window.localStorage.setItem(AUTH_TOKEN_KEY, normalizedAccess);
    }

    if (normalizedRefresh !== '') {
        window.localStorage.setItem(REFRESH_TOKEN_KEY, normalizedRefresh);
    }
}

export function clearTokens() {
    window.localStorage.removeItem(AUTH_TOKEN_KEY);
    window.localStorage.removeItem(REFRESH_TOKEN_KEY);
}

export function redirectToLogin() {
    if (window.location.pathname === '/login') {
        return;
    }

    const next = `${window.location.pathname}${window.location.search}`;
    window.location.href = `/login?next=${encodeURIComponent(next)}`;
}

export async function isAuthenticated() {
    const currentUser = await fetchCurrentUser();

    return currentUser !== null;
}

export async function fetchCurrentUser() {
    if (authCheckRequestInFlight instanceof Promise) {
        return authCheckRequestInFlight;
    }

    authCheckRequestInFlight = (async () => {
        const token = getAuthToken();
        if (token === '') {
            return null;
        }

        let response = await performRequest(AUTH_CHECK_ENDPOINT, 'GET', undefined, token);
        let payload = await response.json().catch(() => ({}));

        if (isExpiredJwtError(response, payload)) {
            try {
                await refreshAccessToken();
            } catch {
                return null;
            }

            response = await performRequest(AUTH_CHECK_ENDPOINT, 'GET', undefined, getAuthToken());
            payload = await response.json().catch(() => ({}));
        }

        if (!response.ok) {
            if (response.status === 401) {
                clearTokens();
            }

            return null;
        }

        const user = payload?.user;
        if (user === null || typeof user !== 'object') {
            return null;
        }

        return user;
    })();

    try {
        return await authCheckRequestInFlight;
    } catch {
        return null;
    } finally {
        authCheckRequestInFlight = null;
    }
}

export async function requireAuth() {
    const authenticated = await isAuthenticated();
    if (!authenticated) {
        redirectToLogin();
        return false;
    }

    return true;
}

function isExpiredJwtError(response, payload) {
    if (response.status !== 401) {
        return false;
    }

    const message = String(payload?.message || '').toLowerCase();

    return message.includes('expired jwt token');
}

async function performRequest(endpoint, method, body, token) {
    const hasBody = body !== undefined;
    const headers = {
        'Accept': 'application/json'
    };

    if (token !== '') {
        headers['Authorization'] = `Bearer ${token}`;
    }

    if (hasBody) {
        headers['Content-Type'] = 'application/json';
    }

    return fetch(endpoint, {
        method,
        headers,
        body: hasBody ? JSON.stringify(body) : undefined
    });
}

async function refreshAccessToken() {
    if (refreshRequestInFlight instanceof Promise) {
        return refreshRequestInFlight;
    }

    refreshRequestInFlight = (async () => {
        const currentRefreshToken = getRefreshToken();
        if (currentRefreshToken === '') {
            clearTokens();
            redirectToLogin();
            throw new Error('Bitte erneut einloggen.');
        }

        const response = await fetch(REFRESH_ENDPOINT, {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ refresh_token: currentRefreshToken })
        });

        const payload = await response.json().catch(() => ({}));
        if (!response.ok) {
            clearTokens();
            redirectToLogin();
            throw new Error('Bitte erneut einloggen.');
        }

        const token = String(payload?.token || '').trim();
        const newRefreshToken = String(payload?.refresh_token || '').trim();
        storeTokens(token, newRefreshToken);

        return token;
    })();

    try {
        return await refreshRequestInFlight;
    } finally {
        refreshRequestInFlight = null;
    }
}

async function requestJson(endpoint, options = {}) {
    const method = options.method || 'GET';
    const body = Object.prototype.hasOwnProperty.call(options, 'body') ? options.body : undefined;
    let response = await performRequest(endpoint, method, body, getAuthToken());
    let payload = await response.json().catch(() => ({}));

    if (isExpiredJwtError(response, payload)) {
        await refreshAccessToken();
        response = await performRequest(endpoint, method, body, getAuthToken());
        payload = await response.json().catch(() => ({}));
    }

    if (!response.ok) {
        if (response.status === 401) {
            clearTokens();
            redirectToLogin();
        }

        const message = String(payload?.message || `API request failed (${response.status}).`);
        throw new Error(message);
    }

    return payload;
}

export async function logoutRequest() {
    const refreshToken = getRefreshToken();

    try {
        await fetch('/api/logout', {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ refresh_token: refreshToken })
        });
    } catch {
        // ignore errors during logout
    }

    clearTokens();
}

export async function fetchGames() {
    return requestJson('/api/games');
}

export async function fetchModes() {
    return requestJson('/api/modes');
}

export async function createGameRequest(payload) {
    return requestJson('/api/games', {
        method: 'POST',
        body: payload
    });
}

export async function fetchGameState(gameId) {
    return requestJson(`/api/games/${encodeURIComponent(gameId)}`);
}

export async function fetchGameStatistics(gameId) {
    return requestJson(`/api/games/${encodeURIComponent(gameId)}/statistics`);
}

export async function startGameRequest(gameId) {
    return requestJson(`/api/games/${encodeURIComponent(gameId)}/start`, {
        method: 'POST'
    });
}

export async function commitThrowRequest(gameId, payload) {
    return requestJson(`/api/games/${encodeURIComponent(gameId)}/throws`, {
        method: 'POST',
        body: payload
    });
}

export async function undoThrowRequest(gameId) {
    return requestJson(`/api/games/${encodeURIComponent(gameId)}/undo`, {
        method: 'POST'
    });
}

export async function finishGameRequest(gameId) {
    return requestJson(`/api/games/${encodeURIComponent(gameId)}/finish`, {
        method: 'POST'
    });
}
