import { apiBaseURL } from "./config"

const _f = function (url, options): Promise<Response> {
    if (typeof XMLHttpRequest === "undefined") {
        return Promise.resolve(null)
    }

    options = options || {}
    return new Promise((resolve, reject) => {
        const request = new XMLHttpRequest()
        const keys = []
        const all = []
        const headers = {}

        const response = (): Response => ({
            ok: ((request.status / 100) | 0) == 2, // 200-299
            statusText: request.statusText,
            status: request.status,
            url: request.responseURL,
            text: () => Promise.resolve(request.responseText),
            json: () => Promise.resolve(request.responseText).then(JSON.parse),
            blob: () => Promise.resolve(new Blob([request.response])),
            clone: response,
            headers: {
                // @ts-ignore
                keys: () => keys,
                // @ts-ignore
                entries: () => all,
                get: (n) => headers[n.toLowerCase()],
                has: (n) => n.toLowerCase() in headers,
            },
        })

        request.open(options.method || "get", url, true)

        request.onload = () => {
            request
                .getAllResponseHeaders()
                // @ts-ignore
                .replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm, (m, key, value) => {
                    keys.push((key = key.toLowerCase()))
                    all.push([key, value])
                    headers[key] = headers[key] ? `${headers[key]},${value}` : value
                })
            resolve(response())
        }

        request.onerror = reject
        request.withCredentials = options.credentials == "include"
        for (const i in options.headers) {
            request.setRequestHeader(i, options.headers[i])
        }
        request.send(options.body || null)
    })
}

const _fetch = typeof fetch === "undefined" ? (typeof window === "undefined" ? _f : window.fetch || _f) : fetch

export const api = async <T>(
    endpoint: string,
    options?: {
        method?: string
        filter?: any
        sort?: string
        limit?: number
        offset?: number
        projection?: string
        headers?: {
            [key: string]: string
        }
        params?: {
            [key: string]: string
        }
    },
    body?: any
): Promise<{ data: T; count: number } | any> => {
    if (typeof window === "undefined") {
        // ssr
        // @ts-ignore
        return context.ssrFetch(endpoint, options)
    }

    let method = options?.method || "GET"

    let query = "&count=1"
    if (options?.filter) query += "&filter=" + encodeURIComponent(JSON.stringify(options.filter))
    if (options?.sort) query += "&sort=" + options.sort + "&sort=_id"
    if (options?.limit) query += "&limit=" + options.limit
    if (options?.offset) query += "&offset=" + options.offset
    if (options?.projection) query += "&projection=" + options.projection

    if (options?.params) {
        Object.keys(options.params).forEach((p) => {
            query += "&" + p + "=" + encodeURIComponent(options.params[p])
        })
    }

    let headers: any = {
        "Content-Type": "application/json",
    }

    if (options?.headers) headers = { ...headers, ...options.headers }

    let url = apiBaseURL + endpoint + (query ? "?" + query : "")

    const requestOptions: any = {
        method,
        mode: "cors",
        headers,
    }

    if (method === "POST" || method === "PUT") {
        requestOptions.body = JSON.stringify(body)
    }

    let response = await _fetch(url, requestOptions)
    if (response.status == 409 || response.status == 401) return response
    let data = (await response?.json()) || null
    // @ts-ignore
    return { data }
}
