import {
    GET,
    POST,
    PUT,
    DELETE
} from './ApiRequest';

export default class Api {
    debug = false;

    api_url = '';

    request = {
        type: '',
        option: {},
        data: {},
        url: ''
    };

    /**
     * @type {Object}
     */
    static error_handler = {};

    /**
     *
     * @param {String} api_url absolute address to the api
     * @param {String} type One of the constants appearing at the top of this file, e.g. 'PUT'
     */
    constructor(api_url, type) {
        this.api_url = api_url;
        this.request.type = type;
    }

    /**
     * @param {{method: string, data: {}, headers:{}, endpoint: string}} info generated from this.provideData
     * @return {Promise<Response>}
     */
    fetch(info) {
        return fetch(...(this.prepareFetch(info)))
            .then(Api.prepareResponse)
            .then(Api.generateResult)
            .then(result => Api.parseResult(result, this.request.type, info.data));
    }

    /**
     * @param {String} endpoint Name of the endpoint to fetch, e.g. 'posts'
     * @param {Object} data
     * @returns {Object} { url, options } The HTTP request parameters
     */
    provideData(endpoint, data) {
        switch(this.request.type) {
            case GET:
                return {
                    endpoint: endpoint,
                    method: 'GET'
                };
            case PUT:
                return {
                    endpoint: endpoint,
                    data: JSON.stringify(data),
                    method: 'PUT'
                };
            case POST:
                return {
                    endpoint: endpoint,
                    data: JSON.stringify(data),
                    method: 'POST',
                    headers: {
                        "Content-Type": "application/json; charset=utf-8",
                    }
                };
            case 'POST_legacy':
                let form_data = new FormData();
                for(const key in data) {
                    if(data.hasOwnProperty(key)) {
                        form_data.append(key, data[key]);
                    }
                }
                return {
                    endpoint: endpoint,
                    data: form_data,
                    method: 'POST',
                    headers: {}
                };
            case DELETE:
                return {
                    endpoint: endpoint,
                    method: 'DELETE'
                };
            default:
                throw new Error('Unsupported data provided, unkown connection type: ' + this.request.type);
        }
    }

    /**
     * Provides the option to fetch
     * @param {{method: string, data: {}, headers:{}, endpoint: string}} info generated from this.provideData
     * @returns {Array} with index 0 the url and index 1 the option data
     */
    prepareFetch(info) {
        let option = {};
        if(info.method) {
            option.method = info.method;
        }

        if(info.data) {
            option.body = info.data;
        }

        option.headers = new Headers();

        if(info.headers) {
            for(let header in info.headers) {
                if(info.headers.hasOwnProperty(header)) {
                    option.headers.append(header, info.headers[header]);
                }
            }
        }

        let request = [
            this.api_url + '/' + info.endpoint,
            option
        ];

        if(this.debug) {
            console.log('Api Request:');
            console.log(request);
        }

        return request;
    }

    static prepareResponse(response) {
        return response.text().then((text) => {
            return {
                status: response.status,
                statusText: response.statusText,
                headers: response.headers,
                body: text
            };
        });
    }

    static generateResult(response) {
        let json = JSON.parse('{}');
        try {
            if('string' === typeof response.body && 0 < response.body.length) {
                json = JSON.parse(response.body);
            } else {
                console.error(response.body);
                throw new Error('Api: response body is not JSON');
            }
        } catch(e) {
            // not json, will use empty json object as result
            console.error(e.message);
        }

        return {
            status: response.status,
            headers: response.headers,
            json: json
        };
    }

    /**
     * @param {{status: number, headers: Headers, json: Object}} result
     * @param {String} type
     * @param {Object} data the data that has been send from the client
     * @returns {Object}
     */
    static parseResult(result, type, data) {
        let res = {};
        res.status = result.status;
        res.headers = result.headers;
        res.data = result.json;

        switch(type) {
            case GET:
                //res.total = parseInt(result.headers.get('content-range').split('/').pop(), 10);
                break;
            case POST:
                //res.data = {...data, id: result.json.id};
                break;
            default:
                break;
        }

        if(res.status < 200 || res.status >= 300) {
            return Promise.reject(res);
        }

        return res;
    }
}