class Network {

    /**
     * Load envelope json data 
     * @param {any} envelopeKey
     */
    async getEnvelope(envelopeKey) {
        return this.getRequest(`/api/envelope/${envelopeKey}`)
            .then(this.wrapJsonResponse.bind(this))
    }

    /**
     * Save signature data to server
     * @param {any} envelopeKey
     * @param {any} documentId
     * @param {any} json
     */
    async postEnvelope(envelopeKey, documentId, json) {
        return this.postRequest(`/api/envelope/${envelopeKey}?index=${documentId}`, json)
            .then(this.wrapJsonResponse.bind(this))
    }

    /**
     * Load document binary data
     * @param {any} envelopeKey
     * @param {any} documentId
     */
    async getDocument(envelopeKey, documentId) {
        return this.getRequest(`/api/document/${envelopeKey}?index=${documentId}`)
            .then(this.wrapBinaryResponse.bind(this))
    }

    /**
     * Tell the server that document has been seen
     * @param {any} envelopeKey
     */
    async openDocument(envelopeKey) {
        return this.postRequest(`/api/document/${envelopeKey}`, {})
            .then(this.wrapJsonResponse.bind(this))
    }

    /**
    * Add CSRF Token to request headers
    * @param {any} options
    * @returns
    */
    withCSRFToken(options) {
        const token = getCSRFToken
        let headers = options.headers

        options.headers = {
            ...headers,
            ...token
        }

        return options
    }

    /**
     * Fetches CSRF Token from page
     * @returns
     */
    getCSRFToken() {
        const token = document.getElementsByName('__RequestVerificationToken')[0].value
        return { 'X-XSRF-TOKEN': token }
    }

    /**
     * Creates a GET HTTP request to `url`
     * @param {any} url
     */
    getRequest(url, body) {
        const token = this.getCSRFToken()
        const options = {
            credentials: 'include',
            method: 'GET',
            headers: {
                ...token
            }
        }

        if (body !== undefined) {
            options.body = JSON.stringify(body);
        }

        return fetch(url, options)
    }

    /**
     * Creates a POST HTTP request for url
     * @param {any} url
     * @param {any} json
     * @returns
     */
    postRequest(url, json) {
        const token = this.getCSRFToken()
        const options = {
            credentials: 'include',
            method: 'POST',
            headers: {
                ...token,
                'Content-Type': 'application/json; charset=utf-8'
            },
            body: JSON.stringify(json)
        }

        return fetch(url, options)
    }

    /**
     * Reads and wraps a json response
     * @param {any} response
     * @returns
     */
    async wrapJsonResponse(response) {
        return await this.wrapResponse(
            response,
            async (res) => await res.json())
    }

    /**
     * Reads and wraps a binary response
     * @param {any} response
     * @returns
     */
    async wrapBinaryResponse(response) {
        return await this.wrapResponse(
            response,
            async (res) => await res.arrayBuffer())
    }

    /**
     * Wraps a fetch response depending on status code
     * @param {any} response
     * @param {any} responseHandler
     * @returns
     */
    async wrapResponse(response, responseHandler) {
        let wrappedResponse

        if (response.status === 200) {
            const data = await responseHandler(response)
            wrappedResponse = new WrappedResponse(data, null)
        } else if (response.status === 403) {
            const error = await response.json()
            wrappedResponse = new WrappedResponse(null, error)
        } else {
            wrappedResponse = new WrappedResponse(null, null)
        }

        return wrappedResponse
    }
}

class WrappedResponse {
    constructor(data, error) {
        this.data = data
        this.error = error
        this.fatal = (data === null && error === null)
    }
}

async function setLangAsync(language, flagCode) {
    document.getElementById('selectedFlag').className = 'fi ' + flagCode + ' me-2';

    await fetch(`/lang/${language}`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        }
    })
}

async function setLanguage(language) {

    const hasLang = await fetch('/lang', {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
        }
    })
    .then(res => res.json())
    .then(langs => langs.includes(language))
    .catch(err => false);

    if(hasLang)
        return await fetch(`/lang/${language}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' }
        })
        .then(response => {
            if (response.redirected)
                window.location.href = response.url;
            else if (!response.ok)
                return Promise.reject('Failed to set language');
        });
}