type error = string
export type Response<T> = [T | null, error]

export function ok<T>(t: T): Response<T> {
    return [t, ""]
}

export function err<T>(e: string): Response<T> {
    return [null, e];
}

function generateClientId() {
  const bytes = new Uint8Array(8);
  crypto.getRandomValues(bytes);
  return btoa(String.fromCharCode.apply(null, bytes as any))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

let clientId = generateClientId()
sessionStorage.clientId = clientId


let auth_headers: Record<string, string> = {};
export function setAuthHeaders(headers: Record<string, string>) {
    auth_headers = { ...headers };
}

export function getAuthHeaders(): Record<string, string> {
    return { ...auth_headers }
}

export function asResponse<T>(obj: T): Promise<Response<T>> {
    return Promise.resolve([obj, ""]);
}

export async function call<T>(name: string, body: BodyInit): Promise<Response<T>> {
    let t = performance.now()
    let method = "POST";
    let url = "/rpc/" + name;
    let headers = {
        ...auth_headers,
        'X-Client-Id': clientId,
    };
    let req = new Request(url, { method, body, headers })
    let resp = await fetch(req)
    // let dur = performance.now() - t
    // console.log(name, dur.toFixed(2) + "ms")

    if (resp.status === 200) {
        let data = await resp.json()
        return [data, ""]
    } else {
        let error = await resp.text()
        return [null, error]
    }
}

// TODO test?
export function callUpload<T>(name: string, body: FormData, progressFn: (p: number) => void): Promise<Response<T>> {
    return new Promise(resolve => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', name);
        const headers = getAuthHeaders()
        for (let key in headers) {
            xhr.setRequestHeader(key, headers[key])
        }

        // progress tracking
        xhr.upload.onprogress = (e: ProgressEvent) => {
            if (e.lengthComputable) {
                progressFn(e.loaded / e.total);
            } else {
                progressFn(-1); // special value for progress unknown
            }
        };
        xhr.onloadend = () => {
            progressFn(1)
        };

        xhr.onload = () => {
            if (xhr.status === 200) {
                resolve([JSON.parse(xhr.responseText), ""]);
            } else {
                resolve([null, xhr.responseText]);
            }
        };
        xhr.send(body);
    })
}
