var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
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());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import { md5 } from '../../md5/md5';
import { ApiError } from '../utils';
var ApiService = /** @class */ (function () {
    function ApiService(app, instanceData) {
        if (instanceData === void 0) { instanceData = {}; }
        this.app = app;
        // dafault instance data
        var apiKey = this.app.options.apiKey;
        this.baseEndpoint = '';
        this.predefinedQuery = !!apiKey ? { apiKey: apiKey } : {};
        this.predefinedBody = {};
        this.beforeRequestHooks = [];
        // set custom data
        this.setData(instanceData);
    }
    ApiService.prototype.extend = function () {
        return new ApiService(this.app, {
            endpoint: this.baseEndpoint,
            query: this.predefinedQuery,
            body: this.predefinedBody,
            beforeHooks: this.beforeRequestHooks,
        });
    };
    ApiService.prototype.setData = function (data) {
        var endpoint = data.endpoint, query = data.query, body = data.body, beforeHooks = data.beforeHooks;
        if (!!endpoint) {
            this.baseEndpoint = endpoint;
        }
        if (!!query) {
            this.predefinedQuery = query;
        }
        if (!!body) {
            this.predefinedBody = body;
        }
        if (!!beforeHooks) {
            this.beforeRequestHooks = beforeHooks;
        }
        return this;
    };
    ApiService.prototype.setEndpoint = function (endpoint) {
        this.baseEndpoint = endpoint;
        return this;
    };
    ApiService.prototype.addQuery = function (query) {
        this.predefinedQuery = __assign({}, this.predefinedQuery, query);
        return this;
    };
    ApiService.prototype.addBody = function (body) {
        this.predefinedBody = __assign({}, this.predefinedBody, body);
        return this;
    };
    ApiService.prototype.addBeforeHooks = function (hooks) {
        if (hooks instanceof Function) {
            hooks = [hooks];
        }
        this.beforeRequestHooks = this.beforeRequestHooks.concat(hooks);
        return this;
    };
    ApiService.prototype.buildEndpoint = function (endpoint) {
        if (endpoint === void 0) { endpoint = ''; }
        endpoint = '/' + this.baseEndpoint + '/' + endpoint; // add base
        endpoint = endpoint.replace(/\/+$/, ''); // remove trailing slash
        endpoint = endpoint.replace(/\/+/g, '/'); // remove repeated slashs
        return !!endpoint ? endpoint : '/';
    };
    ApiService.prototype.buildQuery = function (query) {
        if (query === void 0) { query = {}; }
        query = __assign({}, this.predefinedQuery, query);
        // make the string
        var queryStr = '';
        for (var _i = 0, _a = Object.keys(query); _i < _a.length; _i++) {
            var key = _a[_i];
            queryStr = queryStr + '&' + key + '=' + query[key];
        }
        return queryStr.replace(/^&/, '');
    };
    ApiService.prototype.buildBody = function (body) {
        if (body === void 0) { body = {}; }
        return __assign({}, this.predefinedBody, body);
    };
    ApiService.prototype.buildUrl = function (endpoint, query) {
        if (endpoint === void 0) { endpoint = ''; }
        if (query === void 0) { query = ''; }
        var url = this.app.options.backendUrl;
        url += !!endpoint ? ('?e=' + endpoint) : (!!query ? '?' : '');
        return (!!query ? (url + '&' + query) : url).replace('?&', '?');
    };
    ApiService.prototype.runHooks = function (hook, data) {
        return __awaiter(this, void 0, void 0, function () {
            var hooks, i;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        hooks = hook === 'before' ? this.beforeRequestHooks : [];
                        i = 0;
                        _a.label = 1;
                    case 1:
                        if (!(i < hooks.length)) return [3 /*break*/, 4];
                        return [4 /*yield*/, hooks[i](data)];
                    case 2:
                        data = _a.sent();
                        _a.label = 3;
                    case 3:
                        i++;
                        return [3 /*break*/, 1];
                    case 4: return [2 /*return*/, data];
                }
            });
        });
    };
    ApiService.prototype.fetch = function (input, init) {
        return __awaiter(this, void 0, void 0, function () {
            var response, result;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, fetch(input, init)];
                    case 1:
                        response = _a.sent();
                        if (!response.ok) {
                            throw new Error('API fetch failed.');
                        }
                        return [4 /*yield*/, response.json()];
                    case 2:
                        result = _a.sent();
                        if (result.error) {
                            throw new ApiError(result);
                        }
                        return [2 /*return*/, result.data];
                }
            });
        });
    };
    ApiService.prototype.request = function (inputs) {
        if (inputs === void 0) { inputs = {}; }
        return __awaiter(this, void 0, void 0, function () {
            var _a, method, _b, endpoint, _c, query, _d, body, _e, cacheTime;
            return __generator(this, function (_f) {
                _a = inputs.method, method = _a === void 0 ? 'get' : _a, _b = inputs.endpoint, endpoint = _b === void 0 ? '/' : _b, _c = inputs.query, query = _c === void 0 ? {} : _c, _d = inputs.body, body = _d === void 0 ? {} : _d, _e = inputs.cacheTime, cacheTime = _e === void 0 ? 0 : _e;
                if (method.toLowerCase() === 'get') {
                    return [2 /*return*/, this.get(endpoint, query, cacheTime)];
                }
                else if (method.toLowerCase() === 'post') {
                    return [2 /*return*/, this.post(endpoint, query, body)];
                }
                else {
                    return [2 /*return*/, this.post(endpoint, __assign({ method: method }, query), body)];
                }
                return [2 /*return*/];
            });
        });
    };
    ApiService.prototype.get = function (endpoint, query, cacheTime) {
        if (query === void 0) { query = {}; }
        if (cacheTime === void 0) { cacheTime = 0; }
        return __awaiter(this, void 0, void 0, function () {
            var originalUrl, beforeHookResult, url;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        originalUrl = this.buildUrl(this.buildEndpoint(endpoint), this.buildQuery(query));
                        return [4 /*yield*/, this.runHooks('before', {
                                endpoint: endpoint, query: query, body: {},
                            })];
                    case 1:
                        beforeHookResult = _a.sent();
                        endpoint = beforeHookResult.endpoint;
                        query = beforeHookResult.query;
                        url = this.buildUrl(this.buildEndpoint(endpoint), this.buildQuery(query));
                        return [4 /*yield*/, this.app.Cache.getRefresh('api_' + md5(originalUrl), cacheTime, function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0: return [4 /*yield*/, this.fetch(url, { method: 'GET' })];
                                    case 1: return [2 /*return*/, _a.sent()];
                                }
                            }); }); })];
                    case 2: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    ApiService.prototype.post = function (endpoint, query, body) {
        if (query === void 0) { query = {}; }
        if (body === void 0) { body = {}; }
        return __awaiter(this, void 0, void 0, function () {
            var beforeHookResult, url;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.runHooks('before', {
                            endpoint: endpoint, query: query, body: body,
                        })];
                    case 1:
                        beforeHookResult = _a.sent();
                        endpoint = beforeHookResult.endpoint;
                        query = beforeHookResult.query;
                        body = beforeHookResult.body;
                        url = this.buildUrl(this.buildEndpoint(endpoint), this.buildQuery(query));
                        return [4 /*yield*/, this.fetch(url, {
                                method: 'POST',
                                body: JSON.stringify(this.buildBody(body)),
                                headers: {
                                    'Content-Type': 'application/x-www-form-urlencoded',
                                },
                            })];
                    case 2: 
                    // send request
                    return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    ApiService.prototype.put = function (endpoint, query, body) {
        if (query === void 0) { query = {}; }
        if (body === void 0) { body = {}; }
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.post(endpoint, __assign({}, query, { method: 'PUT' }), body)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    ApiService.prototype.patch = function (endpoint, query, body) {
        if (query === void 0) { query = {}; }
        if (body === void 0) { body = {}; }
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.post(endpoint, __assign({}, query, { method: 'PATCH' }), body)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    ApiService.prototype.delete = function (endpoint, query, body) {
        if (query === void 0) { query = {}; }
        if (body === void 0) { body = {}; }
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.post(endpoint, __assign({}, query, { method: 'DELETE' }), body)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    return ApiService;
}());
export { ApiService };
