"use strict";
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 events_1 = require("events");
const superagent_1 = require("superagent");
const request = require("superagent");
const response_error_1 = require("./response-error");
const search_error_1 = require("./search-error");
const handleError = (err, req) => {
    if (err instanceof response_error_1.ResponseError) {
        throw new search_error_1.SearchError(req.method, req.url, err.statusCode, err.message);
    }
    else if (err instanceof search_error_1.SearchError) {
        throw err;
    }
    else if (err.response) {
        const response = err.response;
        const body = response.body;
        let message;
        if (body && body.error && body.error.message) {
            message = body.error.message;
        }
        else if (body && body.Message) {
            message = body.Message;
        }
        else if (typeof body === 'string') {
            message = body;
        }
        else if (typeof body === 'object') {
            message = JSON.stringify(body);
        }
        else if (body) {
            message = body.toString();
        }
        else {
            message = 'Unknown search request error';
        }
        throw new search_error_1.SearchError(req.method, req.url, response.status, message);
    }
    else {
        throw new search_error_1.SearchError(req.method, req.url, -1, err.message || 'Unknown Error', err);
    }
};
const handleResponse = (resp, timer) => {
    return {
        result: resp.body,
        properties: {
            requestId: resp.header['request-id'],
            elapsedTime: parseInt(resp.header['elapsed-time'], 10),
            clientRequestId: resp.header['client-request-id'],
            eTag: resp.header.etag,
            location: resp.header.location,
        },
        statusCode: resp.status,
        timer,
    };
};
const handlePromise = (req, timer) => __awaiter(this, void 0, void 0, function* () {
    try {
        const resp = yield req;
        return handleResponse(resp, timer);
    }
    catch (err) {
        return handleError(err, req);
    }
});
const handleCallback = (req, callback, timer) => {
    req.end((err, resp) => {
        let error;
        let searchResp;
        if (err) {
            try {
                handleError(err, req);
            }
            catch (err) {
                error = err;
            }
        }
        else {
            searchResp = handleResponse(resp, timer);
        }
        callback(error, searchResp);
    });
};
superagent_1.serialize['application/json'] = (obj) => {
    return Buffer.isBuffer(obj) ? obj : JSON.stringify(obj);
};
/** Internal class to handle search HTTP requests/responses */
class SearchRequester {
    constructor(service, adminKey, defaultVersion) {
        this.service = service;
        this.defaultVersion = defaultVersion;
        this.events = new events_1.EventEmitter();
        this.adminKeys = Array.isArray(adminKey) ? adminKey : [adminKey];
    }
    get endpoint() { return `https://${this.service}.search.windows.net`; }
    request(req, optionsOrCallback, callback) {
        const [options, cb] = this.getParams(optionsOrCallback, callback);
        const headers = Object.assign({
            'api-key': options && options.key ? options.key : this.adminKeys[0],
            'if-match': options && options.ifMatch ? options.ifMatch : null,
            'if-none-match': options && options.ifNoneMatch ? options.ifNoneMatch : null,
            'client-request-id': options && options.clientRequestId ? options.clientRequestId : null,
            'return-client-request-id': options && options.returnClientRequestId ? 'True' : null,
        }, req.headers);
        const query = Object.assign({
            'api-version': options && options.version ? options.version : this.defaultVersion,
        }, req.query);
        const timer = { start: new Date(), response: process.hrtime(), end: process.hrtime() };
        const requestValue = request(req.method, this.endpoint + req.path)
            .set(headers)
            .query(query)
            .send(req.body)
            .retry(options ? options.retry : 0)
            .timeout(options ? options.timeout : null)
            .parse(req.parser)
            .on('response', (resp) => {
            timer.response = process.hrtime(timer.response);
            this.events.emit('response', resp);
        })
            .on('error', (err) => {
            timer.end = process.hrtime(timer.end);
            if (this.events.listenerCount('error')) {
                this.events.emit('error', err);
            }
        })
            .on('end', (err) => {
            timer.end = process.hrtime(timer.end);
        });
        this.events.emit('request', { request: req, options });
        if (cb) {
            handleCallback(requestValue, cb, timer);
        }
        else {
            return handlePromise(requestValue, timer);
        }
    }
    getParams(optionsOrCallback, callback) {
        if (typeof optionsOrCallback === 'function') {
            return [{}, optionsOrCallback];
        }
        else {
            return [optionsOrCallback || {}, callback];
        }
    }
}
exports.SearchRequester = SearchRequester;
//# sourceMappingURL=search-requester.js.map