declare global {
    interface Window {
        env?: {
            API_URL?: string;
        };
    }
}

const getApiUrl = () => {
    // 1. Runtime config (Docker)
    if (window.env?.API_URL) {
        return `${window.env.API_URL}/api/v1`;
    }
    // 2. LocalStorage override
    const stored = localStorage.getItem("k_notes_api_url");
    if (stored) {
        return `${stored}/api/v1`;
    }
    // 3. Default fallback
    return "http://localhost:3000/api/v1";
};

export const getBaseUrl = () => {
    if (window.env?.API_URL) {
        return window.env.API_URL;
    }
    const stored = localStorage.getItem("k_notes_api_url");
    return stored ? stored : "http://localhost:3000";
}

export class ApiError extends Error {
    public status: number;

    constructor(status: number, message: string) {
        super(message);
        this.status = status;
        this.name = "ApiError";
    }
}

async function fetchWithAuth(endpoint: string, options: RequestInit = {}) {
    const url = `${getApiUrl()}${endpoint}`;

    const headers = {
        "Content-Type": "application/json",
        ...options.headers,
    };

    const config: RequestInit = {
        ...options,
        headers,
        credentials: "include", // Important for cookies!
        // signal: controller.signal, // Removing signal, using race instead
    };

    try {
        const fetchPromise = fetch(url, config);
        const timeoutPromise = new Promise((_, reject) =>
            setTimeout(() => reject(new TypeError("Network request timed out")), 3000)
        );

        const response = (await Promise.race([fetchPromise, timeoutPromise])) as Response;
        // clearTimeout(timeoutId); // Not needed with race logic here (though leaking timer? No, race settles.)


        if (!response.ok) {
            // Try to parse error message
            let errorMessage = "An error occurred";
            try {
                const errorData = await response.json();
                errorMessage = errorData.error?.message || errorData.message || errorMessage;
            } catch {
                // failed to parse json
            }

            throw new ApiError(response.status, errorMessage);
        }

        // For 204 No Content or empty responses
        if (response.status === 204) {
            return null;
        }

        // Try to parse JSON
        try {
            return await response.json();
        } catch {
            return null;
        }
    } catch (error) {

        throw error;
    }
}



export const api = {
    get: (endpoint: string) => fetchWithAuth(endpoint, { method: "GET" }),
    post: (endpoint: string, body: any) =>
        fetchWithAuth(endpoint, {
            method: "POST",
            body: JSON.stringify(body),
        }),
    patch: (endpoint: string, body: any) =>
        fetchWithAuth(endpoint, {
            method: "PATCH",
            body: JSON.stringify(body),
        }),
    delete: (endpoint: string) => fetchWithAuth(endpoint, { method: "DELETE" }),
    exportData: async () => {
        const response = await fetch(`${getApiUrl()}/export`, {
            credentials: "include",
        });
        if (!response.ok) throw new ApiError(response.status, "Failed to export data");
        return response.blob();
    },
    importData: (data: any) => api.post("/import", data),
};
