import { apiBaseURL } from "./config"
import * as sentry from "./sentry"
import * as SSR from "../api/hooks/lib/ssr.js"
import config from "../api/hooks/config"

// [MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com/)
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?: APIParams
        body?: any
        noToken?: boolean
        signal?: AbortSignal
        _groupBy?: string
        showErrors?: boolean
    }
): Promise<{ data: T; count: number }> => {
    if (typeof window === "undefined") {
        // ssr
        // @ts-ignore
        return context.ssrFetch(endpoint, options)
    }

    // try ssr cache
    const key = SSR.obj2str({ endpoint, options })
    // @ts-ignore
    if (window.__SSR_CACHE__) {
        // @ts-ignore
        const ssrVal = window.__SSR_CACHE__[key]
        console.log("SSR:", key, ssrVal)
        if (ssrVal) {
            return ssrVal
        }
    }

    let method = "GET"
    if (options?.method) method = options.method.toUpperCase()

    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?._groupBy) query += "&_groupBy=" + options._groupBy

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

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

    // send jwt
    if (!options?.noToken) {
        // try {
        //     headers["Authorization"] =
        //         "Bearer " + (await getLogin()).tokenString
        // } catch (e) { }
    }

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

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

    const span = sentry.currentTransaction()?.startChild({
        op: "fetch",
        description: method + " " + url,
        data: Object.assign({}, options, { url }),
    })
    const trace_id = span?.toTraceparent()
    if (trace_id) {
        headers["sentry-trace"] = trace_id
    }

    try {
        const response = await _fetch(url, {
            method,
            mode: "cors",
            headers,
            body: options?.body ? JSON.stringify(options.body) : null,
            signal: options?.signal,
        })

        span?.finish()

        // @ts-ignore
        let data = (await response?.json()) || null

        if (response?.status < 200 || response?.status >= 400) throw { response, data }

        // @ts-ignore
        return { data, count: response?.headers?.get("x-results-count") || 0 }
    } catch (e) {
        if (options?.showErrors && !(e instanceof DOMException)) {
            // newNotification({
            //     class: "error",
            //     html: "Es ist ein Fehler aufgetreten! Bitte laden Sie die Seite neu und versuchen es später nocheinmal.",
            //     timeout: 6000,
            // })
        }
        throw e
    }
}

export const sendEmail = async (type: string = "contactForm", data: any, noToken?: boolean) => {
    await api("contact_form?type=" + type, {
        method: "post",
        body: data,
        noToken,
        showErrors: true,
        headers: {
            token: config.publicToken,
        },
    })
}

export const getContent = async (apiParams: APIParams): Promise<Content[]> => {
    const c = await api<Content[]>("content", {
        // limit: 1,
        // sort: "-priority",
        ...apiParams,
    })

    if (c?.data?.length) {
        return c.data
    }

    return null
}

export const getGeneralInformation = async (): Promise<GeneralInfo[]> => {
    try {
        let response = await api<GeneralInfo[]>("general", {
            method: "get",
            offset: 0,
            limit: 1,
            filter: {
                public: true,
            },
        })
        return response.data
    } catch (e) {
        return null
    }
}

export const getNavigations = async (l: Locale): Promise<Navigation[]> => {
    try {
        let response = await api<Navigation[]>("navigation", {
            method: "get",
            offset: 0,
            filter: {
                // locale: l.key,
            },
        })
        return response.data
    } catch (e) {
        return null
    }
}

// export const getGalleries = async (galIds: number[], params?: APIParams): Promise<Gallery[]> => {
//     try {
//         let response = await api<Gallery[]>("galleries", {
//             method: "get",
//             offset: 0,
//             params: {
//                 sort: "title",
//                 ...params,
//             },
//             filter: {
//                 _id: { $in: galIds },
//             },
//         })

//         return response.data
//     } catch (e) {
//         return null
//     }
// }
