"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceName = exports.KodoHttpClient = void 0;
const async_lock_1 = __importDefault(require("async-lock"));
const url_1 = require("url");
const region_1 = require("./region");
const region_service_1 = require("./region_service");
const kodo_auth_1 = require("./kodo-auth");
const zlib_1 = __importDefault(require("zlib"));
const uplog_1 = require("./uplog");
const http_client_1 = require("./http-client");
class KodoHttpClient {
    constructor(sharedOptions) {
        this.sharedOptions = sharedOptions;
        this.regionsCache = {};
        this.regionsCacheLock = new async_lock_1.default();
        this.regionService = new region_service_1.RegionService(sharedOptions);
        this.uplogBuffer = new uplog_1.UplogBuffer({
            appName: sharedOptions.appName, appVersion: sharedOptions.appVersion,
            bufferSize: sharedOptions.uplogBufferSize,
            onBufferFull: (buffer) => {
                return this.sendUplog(buffer);
            }
        });
        this.httpClient = new http_client_1.HttpClient(sharedOptions, this.uplogBuffer);
    }
    call(options) {
        return new Promise((resolve, reject) => {
            this.getServiceUrls(options.serviceName, options.bucketName, options.s3RegionId, options.stats).then((urls) => {
                this.callUrls(urls, {
                    method: options.method,
                    path: options.path,
                    query: options.query,
                    data: options.data,
                    dataType: options.dataType,
                    form: options.form,
                    contentType: options.contentType,
                    headers: options.headers,
                    uploadProgress: options.uploadProgress,
                    uploadThrottle: options.uploadThrottle,
                    stats: options.stats,
                }).then(resolve, reject);
            }).catch(reject);
        });
    }
    callUrls(urls, options) {
        return this.httpClient.call(urls, options);
    }
    clearCache() {
        Object.keys(this.regionsCache).forEach((key) => { delete this.regionsCache[key]; });
        this.regionService.clearCache();
    }
    getServiceUrls(serviceName, bucketName, s3RegionId, stats) {
        return new Promise((resolve, reject) => {
            let key;
            if (s3RegionId) {
                key = `${this.sharedOptions.ucUrl}/${s3RegionId}`;
            }
            else {
                key = `${this.sharedOptions.ucUrl}/${this.sharedOptions.accessKey}/${bucketName}`;
            }
            if (this.regionsCache[key]) {
                resolve(this.getUrlsFromRegion(serviceName, this.regionsCache[key]));
                return;
            }
            this.regionsCacheLock.acquire(key, () => {
                if (this.regionsCache[key]) {
                    return Promise.resolve(this.regionsCache[key]);
                }
                else if (bucketName) {
                    return region_1.Region.query({
                        bucketName,
                        accessKey: this.sharedOptions.accessKey,
                        ucUrl: this.sharedOptions.ucUrl,
                        timeout: this.sharedOptions.timeout,
                        retry: this.sharedOptions.retry,
                        retryDelay: this.sharedOptions.retryDelay,
                        appName: this.sharedOptions.appName,
                        appVersion: this.sharedOptions.appVersion,
                        uplogBufferSize: this.sharedOptions.uplogBufferSize,
                        requestCallback: this.sharedOptions.requestCallback,
                        responseCallback: this.sharedOptions.responseCallback,
                        stats: stats,
                    });
                }
                else {
                    return new Promise((resolve, reject) => {
                        this.regionService.getAllRegions({
                            timeout: this.sharedOptions.timeout,
                            retry: this.sharedOptions.retry,
                            retryDelay: this.sharedOptions.retryDelay,
                            stats: stats,
                        }).then((regions) => {
                            if (regions.length == 0) {
                                reject(Error('regions is empty'));
                                return;
                            }
                            if (s3RegionId) {
                                const region = regions.find((region) => region.s3Id === s3RegionId);
                                if (!region) {
                                    reject(new Error(`Cannot find region of ${s3RegionId}`));
                                    return;
                                }
                                resolve(region);
                            }
                            else {
                                resolve(regions[0]);
                            }
                        }).catch(reject);
                    });
                }
            }).then((region) => {
                this.regionsCache[key] = region;
                resolve(this.getUrlsFromRegion(serviceName, region));
            }).catch(reject);
        });
    }
    log(entry) {
        return this.uplogBuffer.log(entry);
    }
    sendUplog(logBuffer) {
        return new Promise((resolve, reject) => {
            const query = new url_1.URLSearchParams();
            query.set("compressed", "gzip");
            const token = kodo_auth_1.makeUploadToken(this.sharedOptions.accessKey, this.sharedOptions.secretKey, kodo_auth_1.newUploadPolicy("testbucket"));
            let headers = { 'authorization': `UpToken ${token}` };
            if (KodoHttpClient.logClientId) {
                headers['X-Log-Client-Id'] = KodoHttpClient.logClientId;
            }
            zlib_1.default.gzip(logBuffer, { level: zlib_1.default.constants.Z_BEST_COMPRESSION }, (err, compressedLog) => {
                if (err) {
                    reject(err);
                    return;
                }
                this.call({
                    method: 'POST',
                    serviceName: ServiceName.Uplog,
                    path: 'log/4',
                    query: query,
                    headers: headers,
                    data: compressedLog,
                }).then((response) => {
                    if (!KodoHttpClient.logClientId && response.headers['X-Log-Client-Id']) {
                        KodoHttpClient.logClientId = response.headers['X-Log-Client-Id'].toString();
                    }
                    resolve();
                }).catch(reject);
            });
        });
    }
    getUrlsFromRegion(serviceName, region) {
        switch (serviceName) {
            case ServiceName.Up:
                return [...region.upUrls];
            case ServiceName.Uc:
                return [...region.ucUrls];
            case ServiceName.Rs:
                return [...region.rsUrls];
            case ServiceName.Rsf:
                return [...region.rsfUrls];
            case ServiceName.Api:
                return [...region.apiUrls];
            case ServiceName.S3:
                return [...region.s3Urls];
            case ServiceName.Qcdn:
                return ['https://api.qiniu.com'];
            case ServiceName.Portal:
                return ['https://portal.qiniu.com'];
            case ServiceName.Uplog:
                return ['http://uplog.qbox.me'];
        }
    }
}
exports.KodoHttpClient = KodoHttpClient;
KodoHttpClient.logClientId = undefined;
var ServiceName;
(function (ServiceName) {
    ServiceName[ServiceName["Up"] = 0] = "Up";
    ServiceName[ServiceName["Uc"] = 1] = "Uc";
    ServiceName[ServiceName["Rs"] = 2] = "Rs";
    ServiceName[ServiceName["Rsf"] = 3] = "Rsf";
    ServiceName[ServiceName["Api"] = 4] = "Api";
    ServiceName[ServiceName["S3"] = 5] = "S3";
    ServiceName[ServiceName["Qcdn"] = 6] = "Qcdn";
    ServiceName[ServiceName["Portal"] = 7] = "Portal";
    ServiceName[ServiceName["Uplog"] = 8] = "Uplog";
})(ServiceName = exports.ServiceName || (exports.ServiceName = {}));
//# sourceMappingURL=kodo-http-client.js.map