/**
 * Resolve API base for sibling (/website + /api) or subpath (/prefix/ + /prefix/api) layouts.
 * Override per deploy: <script>window.QDB_API_BASE = '/nat-as-server/api';</script>
 */
function resolveApiBase() {
    if (typeof window !== 'undefined' && window.QDB_API_BASE) {
        return String(window.QDB_API_BASE).replace(/\/$/, '');
    }
    const fromRelative = new URL('../api', window.location.href);
    const relPath = fromRelative.pathname.replace(/\/$/, '');
    const pageDir = window.location.pathname.replace(/\/[^/]*$/, '/') || '/';
    const mount = pageDir.split('/').filter(Boolean)[0];
    // Subpath deploy: /prefix/ is aliased to website/, so ../api wrongly becomes /api
    if (mount && mount !== 'api' && mount !== 'website' && relPath === '/api') {
        return `/${mount}/api`;
    }
    return relPath;
}

const API_BASE = resolveApiBase();

/** Build a URL under the API base (path may include query string). */
export function apiUrl(endpoint) {
    const clean = String(endpoint).replace(/^\//, '').replace(/\.php(\?|$)/, '$1');
    return `${API_BASE}/${clean}`;
}

function getToken() {
    return localStorage.getItem('qdb_token');
}

const WEBSITE_ROLES = ['admin', 'supervisor'];

let sessionCheckPromise = null;
let sessionCheckToken = null;

/** Clear cached session validation (after logout or token change). */
export function invalidateSessionCheck() {
    sessionCheckPromise = null;
    sessionCheckToken = null;
}

/** Clear stored session and open the login page. */
export function redirectToLogin() {
    invalidateSessionCheck();
    localStorage.removeItem('qdb_token');
    localStorage.removeItem('qdb_user');
    localStorage.removeItem('qdb_role');
    window.location.hash = '#/login';
}

/** @returns {boolean} true if the response means the session is no longer valid */
function isSessionInvalidResponse(res, json) {
    if (res.status === 401) return true;
    const errMsg = json.error?.message || (typeof json.error === 'string' ? json.error : '');
    const errCode = json.error?.code || '';
    return res.status === 401
        || res.status === 403 && (
            errCode === 'UNAUTHORIZED'
            || errCode === 'PASSWORD_CHANGE_REQUIRED'
            || errCode === 'FORBIDDEN'
            || errMsg === 'Invalid or expired token'
            || errMsg === 'Missing Bearer token'
            || errMsg === 'Password change required before access'
        );
}

/**
 * Verify the stored token is still valid on the server. Redirects to login when not.
 * @returns {Promise<boolean>}
 */
export async function ensureValidSession() {
    const token = getToken();
    if (!token) {
        redirectToLogin();
        return false;
    }

    if (sessionCheckPromise && sessionCheckToken === token) {
        return sessionCheckPromise;
    }

    sessionCheckToken = token;
    sessionCheckPromise = (async () => {
        try {
            const data = await apiGet('session');
            if (!data.success || !data.valid) {
                redirectToLogin();
                return false;
            }
            if (data.mustChangePassword) {
                redirectToLogin();
                return false;
            }
            if (!WEBSITE_ROLES.includes(data.role)) {
                redirectToLogin();
                return false;
            }
            if (data.user) localStorage.setItem('qdb_user', data.user);
            if (data.role) localStorage.setItem('qdb_role', data.role);
            return true;
        } catch (e) {
            if (e.message !== 'Session expired') {
                redirectToLogin();
            }
            return false;
        }
    })();

    return sessionCheckPromise;
}

async function handleAuthResponse(res) {
    if (res.status !== 401 && res.status !== 403) return;
    const json = await res.json().catch(() => ({}));
    if (isSessionInvalidResponse(res, json)) {
        redirectToLogin();
        throw new Error('Session expired');
    }
    const errMsg = json.error?.message || json.error || '';
    throw new Error(errMsg || 'Permission denied');
}

async function apiFetch(endpoint, options = {}) {
    const token = getToken();
    const headers = options.headers || {};
    headers['X-QDB-Client'] = 'web';
    if (token) headers['Authorization'] = `Bearer ${token}`;
    if (!(options.body instanceof FormData)) {
        headers['Content-Type'] = 'application/json; charset=UTF-8';
    }

    // Strip .php suffix for clean URLs
    const cleanEndpoint = endpoint.replace(/\.php(\?|$)/, '$1');
    const res = await fetch(apiUrl(cleanEndpoint), { ...options, headers });

    if (res.status === 401 || res.status === 403) {
        await handleAuthResponse(res);
    }

    return res;
}

/**
 * Unwrap the unified response envelope:
 *   {"ok": true, "data": ...} -> returns { success: true, ...data }
 *   {"ok": false, "error": ...} -> returns { success: false, error: message }
 * Also supports legacy format for backward compat.
 */
function unwrap(json) {
    if (typeof json.ok === 'boolean') {
        if (json.ok) {
            const d = json.data || {};
            return { success: true, ...(Array.isArray(d) ? { items: d } : d) };
        }
        const err = json.error;
        const msg = typeof err === 'string' ? err : (err?.message || 'Unknown error');
        return { success: false, error: msg, errorCode: typeof err === 'object' ? err?.code : null };
    }
    return json;
}

export async function apiGet(endpoint) {
    const res = await apiFetch(endpoint);
    return unwrap(await res.json());
}

export async function apiPost(endpoint, body) {
    const res = await apiFetch(endpoint, {
        method: 'POST',
        body: JSON.stringify(body),
    });
    return unwrap(await res.json());
}

export async function apiPut(endpoint, body) {
    const res = await apiFetch(endpoint, {
        method: 'PUT',
        body: JSON.stringify(body),
    });
    return unwrap(await res.json());
}

export async function apiPatch(endpoint, body) {
    const res = await apiFetch(endpoint, {
        method: 'PATCH',
        body: JSON.stringify(body),
    });
    return unwrap(await res.json());
}

export async function apiDelete(endpoint, body) {
    const res = await apiFetch(endpoint, {
        method: 'DELETE',
        body: JSON.stringify(body),
    });
    return unwrap(await res.json());
}

/** Authenticated file/download fetch (marks request as website for activity logging). */
export async function apiDownloadFetch(url, options = {}) {
    const token = getToken();
    const headers = { ...(options.headers || {}), 'X-QDB-Client': 'web' };
    if (token) headers['Authorization'] = `Bearer ${token}`;
    const res = await fetch(url, { ...options, headers });
    const ct = res.headers.get('Content-Type') || '';
    if (ct.includes('application/json')) {
        await handleAuthResponse(res);
    } else if (res.status === 401 || res.status === 403) {
        redirectToLogin();
        throw new Error('Session expired');
    }
    return res;
}
