import Vue from 'vue';
import axios from 'axios';
import {errorBox} from './utils';

const codeMessage = {
    200: 'ok.',
    401: 'no permission.',
    404: 'not found.',
};

const showMessage = (res) => {
    const response = res.response;
    const errorText = codeMessage[response.status] || response.statusText;
    let url = response.config.url.replace('//', '_');
    url = url.substr(url.indexOf('/'));
    errorBox(`${response.status}: ${errorText} (${url})`);
};

function decodeData(bytes, encode) {
    const bufferTypes = new Uint8Array(bytes);
    try {
        if (encode !== '') {
            return new TextDecoder(encode).decode(bufferTypes);
        } else {
            return new TextDecoder().decode(bufferTypes);
        }
    } catch (e) {
        console.error('TextDecoder faile.', encode, bytes, e);
        throw e;
    }
}

function arrayBufferFromBlob(bytesBlob) {
    return new Promise(function (resolve, reject) {
        const reader = new FileReader();
        reader.readAsArrayBuffer(bytesBlob);
        reader.onload = function () {
            resolve(this.result);
        };
    });
}

/**
 * Requests a URL, returning a promise.
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [option] The options we want to pass to "fetch"
 * @return {object}           An object containing either "data" or "err"
 */
export default function request(
    apiURL,
    options,
    successCallback,
    errorCallback
) {
    const defaultOptions = {
        loading: true,
        direct: false,
        credentials: 'include',
    };
    const newOptions = {
        ...defaultOptions,
        ...options,
        url: apiURL,
    };
    newOptions.headers = {
        Accept: 'application/json',
        ...newOptions.headers,
    };
    //
    if (newOptions.method === 'GET') {
        newOptions.params = newOptions.data;
    }
    if (newOptions.method === 'POST' || newOptions.method === 'PUT') {
        if (!(newOptions.data instanceof FormData)) {
            newOptions.headers = {
                'Content-Type': 'application/json;',
                ...newOptions.headers,
            };
            newOptions.data = JSON.stringify(newOptions.data);
        }
    }
    //
    errorCallback =
        errorCallback === null || errorCallback === undefined
            ? (errorMessage) => {
                showMessage(errorMessage);
            }
            : errorCallback;
    successCallback =
        successCallback === null || successCallback === undefined
            ? () => {
            }
            : successCallback;
    //
    let finallyCallback = () => {
        /**/
    };
    if (newOptions.loading) {
        const loading = Vue.prototype.$loading({
            lock: true,
            text: 'Loading',
            spinner: 'el-icon-loading',
            background: 'rgba(0, 0, 0, 0.5)',
        });
        finallyCallback = () => {
            loading.close();
        };
    }
    return axios.request({
        ...newOptions,
        withCredentials: true,
        responseType: 'blob',
    }).then(async (response) => {
        let contentType = '';
        let contentEncode = '';
        for (const key in response.headers) {
            const keyLC = key.toLowerCase();
            if (keyLC === 'x-interfaceui-contexttype') {
                contentType = response.headers[key];
                contentType = contentType.toLowerCase();
                break;
            } else if (keyLC === 'content-type') {
                // "application/json;charset=UTF-8"
                const contentTypeHeader = response.headers[key];
                const splitContent = contentTypeHeader.split(';');
                for (const splitItem in splitContent) {
                    const splitItemContent = splitContent[splitItem];
                    if (splitItemContent.toLowerCase().startsWith('charset=')) {
                        contentEncode = splitItemContent.substring('charset='.length, splitItemContent.length);
                    }
                }
            }
        }
        if (contentType === undefined || contentType == null || contentType === '') {
            if (response.data.type === 'application/json') {
                contentType = 'json';
            }
        }
        //
        if (contentType === 'json') {
            // json
            arrayBufferFromBlob(response.data).then((arrayBuffer) => {
                response.dataTypeMode = 'json';
                response.data = JSON.parse(decodeData(arrayBuffer, contentEncode));
                successCallback(response);
            });
        } else if (contentType === 'text') {
            // text
            arrayBufferFromBlob(response.data).then((arrayBuffer) => {
                response.dataTypeMode = 'text';
                response.data = decodeData(arrayBuffer, contentEncode);
                successCallback(response);
            });
        } else {
            // bytes
            arrayBufferFromBlob(response.data).then((arrayBuffer) => {
                const bufferTypes = new Uint8Array(arrayBuffer);
                let tempString = '';
                let n = 0;
                for (let i = 0; i < bufferTypes.length; ++i) {
                    let hexDat = bufferTypes[i].toString(16).toUpperCase();
                    if (hexDat.length === 1) {
                        hexDat = '0' + hexDat;
                    }
                    if (n < 15) {
                        n++;
                        tempString = tempString + hexDat + ' ';
                    } else {
                        n = 0;
                        tempString = tempString + hexDat + '\n';
                    }
                }
                response.dataTypeMode = 'bytes';
                response.data = tempString.trim();
                successCallback(response);
            });
        }
    }
    ).catch(errorCallback).finally(finallyCallback);
}
