/**
 * Base model class
 * 
 * @author Nicolas Stein
 * @author Michael Fallise
 * @version 1.2
 * @category Core
 * @subcategory Application
 */
class Model {

    /**
     * 
     * @param {object} response 
     * @returns {object}
     */
    _processResponse(response, resolve, reject){   
        if(response.ok){ // All ok
            if( (response.headers.get('Content-Type')) && (response.headers.get('Content-Type').toLocaleLowerCase().indexOf('application/json')>-1) ) {
                resolve(response.json());
            } else {
                resolve(
                    response.text().then(rawResponse => {
                        if(rawResponse != '') {
                            console.warn('Server did not declare response as Json...trying to parse it anyway', rawResponse)
                            return(JSON.parse(rawResponse));
                        } else if(response.status==204) return(true); // Normal to have no content, so make sure other layers see it as success
                        else return('');
                    })
                )
            }
        } else { // server error 
            // server error with json error details
            if( (response.headers.get('Content-Type')) && (response.headers.get('Content-Type').toLocaleLowerCase().indexOf('application/json')>-1) ){
                    response.json().then(
                    cleanJson => { // json error details is clean
                        let error = {
                            'code': response.status,
                            'displayMessage': (cleanJson.error.displayMessage || ' !!No debugMessage from server!!'),
                            'debugMessage': response.statusText+'\n'+(cleanJson.error.debugMessage || ' !!No debugMessage from server!!'),
                        }
                        this.onRequestError(error);
                        reject(error);
                    },
                    text => { 
                        let error = { // json error details is garbage
                            'code': response.status,
                            'displayMessage': 'Could not request to server!?',
                            'debugMessage': 'Additionally, bad Json returned :', text,
                        }
                        this.onRequestError(error);
                        reject(error);
                    }
                )
            } else { // server error with text return
                response.text().then(
                    text => { 
                        let error = {
                            'code': response.status,
                            'displayMessage': 'Could not request to server!?',
                            'debugMessage': `Server responded: ${text}`,                            
                        }
                        this.onRequestError(error);
                        reject(error);
                    },
                )
            }
        }
    }
    
    /**
     * Generic API service call
     * @param {string} uri 
     * @param {string} method 
     * @param {object} payload 
     * @returns {object}
     */
    request(uri, method, payload) {
        // Fetch chooses its own resolve/reject (see https://developer.mozilla.org/en-US/docs/Web/API/fetch)
        // Errors like 404 are not rejected. and we can't change the -already done- resolve/reject choice.
        // therefore we return our own promise.
        if((!method) || (!uri)) {
            return(
                new Promise((resolve, reject) => reject(
                    {   sucess :false, 
                        payload: null,
                        error: {    displayError: 'Server Error (no endpoint)', 
                                    debugError: 'Missing endpoint' },
                    }) 
            ))
        }

        return ( 
            new Promise((resolve, reject) => {
                let body = null;
                if(method.toLowerCase()!='get') body =  payload ? JSON.stringify(payload): null;
                else if(payload) uri += '?'+Object.entries(payload).map(kv => kv.map(encodeURIComponent).join("=")).join("&");
                fetch(uri, {
                    method: method,
                    headers: { 'Accept': 'application/json', 'Content-Type':'application/json'},
                    body: body,
                    credentials: 'include', //By nike: mandatory for MT to get session !!!
                })
                .then( response => {              
                    this._processResponse(response, resolve, reject);
                })
                .catch(debugMessage => { // Other errors like metwork, protocol, CORs errors, or 200 but bad JSON
                    let error = {
                        'displayMessage': 'Could not request to server!?',
                        'debugMessage': debugMessage,
                        'code': '4XX' // non http but >299
                    }
                    this.onRequestError(error);
                    reject(error);
                })

            })
        );
    }

    /**
     * Request error handler
     * @param {object} error 
     */
    onRequestError(error) { console.error('Ajax request error:', error); }
}

app.registerClass('Model', Model)
