"use strict";
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const httpm = require("./HttpClient");
const util = require("./Util");
class RestClient {
    /**
     * Creates an instance of the RestClient
     * @constructor
     * @param {string} userAgent - userAgent for requests
     * @param {string} baseUrl - (Optional) If not specified, use full urls per request.  If supplied and a function passes a relative url, it will be appended to this
     * @param {ifm.IRequestHandler[]} handlers - handlers are typically auth handlers (basic, bearer, ntlm supplied)
     * @param {ifm.IRequestOptions} requestOptions - options for each http requests (http proxy setting, socket timeout)
     */
    constructor(userAgent, baseUrl, handlers, requestOptions) {
        this.client = new httpm.HttpClient(userAgent, handlers, requestOptions);
        if (baseUrl) {
            this._baseUrl = baseUrl;
        }
    }
    /**
     * Gets a resource from an endpoint
     * Be aware that not found returns a null.  Other error conditions reject the promise
     * @param {string} requestUrl - fully qualified or relative url
     * @param {IRequestOptions} requestOptions - (optional) requestOptions object
     */
    options(requestUrl, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = util.getUrl(requestUrl, this._baseUrl);
            let res = yield this.client.options(url, this._headersFromOptions(options));
            return this.processResponse(res, options);
        });
    }
    /**
     * Gets a resource from an endpoint
     * Be aware that not found returns a null.  Other error conditions reject the promise
     * @param {string} resource - fully qualified url or relative path
     * @param {IRequestOptions} requestOptions - (optional) requestOptions object
     */
    get(resource, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = util.getUrl(resource, this._baseUrl, (options || {}).queryParameters);
            let res = yield this.client.get(url, this._headersFromOptions(options));
            return this.processResponse(res, options);
        });
    }
    /**
     * Deletes a resource from an endpoint
     * Be aware that not found returns a null.  Other error conditions reject the promise
     * @param {string} resource - fully qualified or relative url
     * @param {IRequestOptions} requestOptions - (optional) requestOptions object
     */
    del(resource, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = util.getUrl(resource, this._baseUrl, (options || {}).queryParameters);
            let res = yield this.client.del(url, this._headersFromOptions(options));
            return this.processResponse(res, options);
        });
    }
    /**
     * Creates resource(s) from an endpoint
     * T type of object returned.
     * Be aware that not found returns a null.  Other error conditions reject the promise
     * @param {string} resource - fully qualified or relative url
     * @param {IRequestOptions} requestOptions - (optional) requestOptions object
     */
    create(resource, resources, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = util.getUrl(resource, this._baseUrl);
            let headers = this._headersFromOptions(options, true);
            let data = JSON.stringify(resources, null, 2);
            let res = yield this.client.post(url, data, headers);
            return this.processResponse(res, options);
        });
    }
    /**
     * Updates resource(s) from an endpoint
     * T type of object returned.
     * Be aware that not found returns a null.  Other error conditions reject the promise
     * @param {string} resource - fully qualified or relative url
     * @param {IRequestOptions} requestOptions - (optional) requestOptions object
     */
    update(resource, resources, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = util.getUrl(resource, this._baseUrl);
            let headers = this._headersFromOptions(options, true);
            let data = JSON.stringify(resources, null, 2);
            let res = yield this.client.patch(url, data, headers);
            return this.processResponse(res, options);
        });
    }
    /**
     * Replaces resource(s) from an endpoint
     * T type of object returned.
     * Be aware that not found returns a null.  Other error conditions reject the promise
     * @param {string} resource - fully qualified or relative url
     * @param {IRequestOptions} requestOptions - (optional) requestOptions object
     */
    replace(resource, resources, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = util.getUrl(resource, this._baseUrl);
            let headers = this._headersFromOptions(options, true);
            let data = JSON.stringify(resources, null, 2);
            let res = yield this.client.put(url, data, headers);
            return this.processResponse(res, options);
        });
    }
    uploadStream(verb, requestUrl, stream, options) {
        return __awaiter(this, void 0, void 0, function* () {
            let url = util.getUrl(requestUrl, this._baseUrl);
            let headers = this._headersFromOptions(options, true);
            let res = yield this.client.sendStream(verb, url, stream, headers);
            return this.processResponse(res, options);
        });
    }
    _headersFromOptions(options, contentType) {
        options = options || {};
        let headers = options.additionalHeaders || {};
        headers["Accept"] = options.acceptHeader || "application/json";
        if (contentType) {
            let found = false;
            for (let header in headers) {
                if (header.toLowerCase() == "content-type") {
                    found = true;
                }
            }
            if (!found) {
                headers["Content-Type"] = 'application/json; charset=utf-8';
            }
        }
        return headers;
    }
    static dateTimeDeserializer(key, value) {
        if (typeof value === 'string') {
            let a = new Date(value);
            if (!isNaN(a.valueOf())) {
                return a;
            }
        }
        return value;
    }
    processResponse(res, options) {
        return __awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
                const statusCode = res.message.statusCode;
                const response = {
                    statusCode: statusCode,
                    result: null,
                    headers: {}
                };
                // not found leads to null obj returned
                if (statusCode == httpm.HttpCodes.NotFound) {
                    resolve(response);
                }
                let obj;
                let contents;
                // get the result from the body
                try {
                    contents = yield res.readBody();
                    if (contents && contents.length > 0) {
                        if (options && options.deserializeDates) {
                            obj = JSON.parse(contents, RestClient.dateTimeDeserializer);
                        }
                        else {
                            obj = JSON.parse(contents);
                        }
                        if (options && options.responseProcessor) {
                            response.result = options.responseProcessor(obj);
                        }
                        else {
                            response.result = obj;
                        }
                    }
                    response.headers = res.message.headers;
                }
                catch (err) {
                    // Invalid resource (contents not json);  leaving result obj null
                }
                // note that 3xx redirects are handled by the http layer.
                if (statusCode > 299) {
                    let msg;
                    // if exception/error in body, attempt to get better error
                    if (obj && obj.message) {
                        msg = obj.message;
                    }
                    else if (contents && contents.length > 0) {
                        // it may be the case that the exception is in the body message as string
                        msg = contents;
                    }
                    else {
                        msg = "Failed request: (" + statusCode + ")";
                    }
                    let err = new Error(msg);
                    // attach statusCode and body obj (if available) to the error object
                    err['statusCode'] = statusCode;
                    if (response.result) {
                        err['result'] = response.result;
                    }
                    if (response.headers) {
                        err['responseHeaders'] = response.headers;
                    }
                    reject(err);
                }
                else {
                    resolve(response);
                }
            }));
        });
    }
}
exports.RestClient = RestClient;