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) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(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 (g && (g = 0, op[0] && (_ = 0)), _) 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 };
    }
};
var AliTrackLogger = /** @class */ (function () {
    function AliTrackLogger() {
        var _this = this;
        this.logList = [];
        this.checkTimer = null;
        this.retryTimes = 0;
        this.enable = false;
        this.db = null;
        this.useIndexedDB = false;
        this.DB_NAME = "__slide_logs_report__";
        this.STORE_NAME = "logs";
        this.BATCH_SIZE = 1000;
        this.uploadLoggerGlobalEvent = function (event) {
            if (event.data.type === "@slide/_upload_log_") {
                _this.start();
            }
        };
        this.config = {
            checkTime: 5000,
            buffer: 300,
        };
        window.addEventListener("message", this.uploadLoggerGlobalEvent);
        this.initIndexedDB();
    }
    AliTrackLogger.prototype.initIndexedDB = function () {
        return __awaiter(this, void 0, void 0, function () {
            var request_1;
            var _this = this;
            return __generator(this, function (_a) {
                if (!('indexedDB' in window)) {
                    this.useIndexedDB = false;
                    this.start();
                    return [2 /*return*/];
                }
                try {
                    request_1 = indexedDB.open(this.DB_NAME, 1);
                    request_1.onerror = function () {
                        _this.useIndexedDB = false;
                    };
                    request_1.onsuccess = function () {
                        _this.start();
                        _this.db = request_1.result;
                        _this.useIndexedDB = true;
                    };
                    request_1.onupgradeneeded = function (event) {
                        var db = event.target.result;
                        if (!db.objectStoreNames.contains(_this.STORE_NAME)) {
                            var store = db.createObjectStore(_this.STORE_NAME, { keyPath: 'id', autoIncrement: true });
                            store.createIndex('timestamp', 'timestamp');
                        }
                    };
                }
                catch (error) {
                    this.useIndexedDB = false;
                }
                return [2 /*return*/];
            });
        });
    };
    AliTrackLogger.prototype.storeLogInIndexedDB = function (log) {
        return __awaiter(this, void 0, void 0, function () {
            var error_1;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!this.useIndexedDB)
                            return [2 /*return*/];
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 4, , 5]);
                        if (!(!this.db || !this.isDatabaseHealthy())) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.recreateDatabase()];
                    case 2:
                        _a.sent();
                        if (!this.db || !this.useIndexedDB) {
                            throw new Error('Database not available after recreation attempt');
                        }
                        _a.label = 3;
                    case 3: return [2 /*return*/, new Promise(function (resolve, reject) {
                            var transaction = _this.db.transaction([_this.STORE_NAME], 'readwrite');
                            var store = transaction.objectStore(_this.STORE_NAME);
                            var request = store.add(log);
                            transaction.onerror = function () {
                                // If transaction fails due to database being deleted, try to recreate
                                _this.recreateDatabase().then(function () {
                                    if (!_this.db || !_this.useIndexedDB) {
                                        reject(new Error('Database recreation failed'));
                                    }
                                    else {
                                        // Retry the operation after recreation
                                        var retryTransaction = _this.db.transaction([_this.STORE_NAME], 'readwrite');
                                        var retryStore = retryTransaction.objectStore(_this.STORE_NAME);
                                        var retryRequest_1 = retryStore.add(log);
                                        retryRequest_1.onsuccess = function () { return resolve(); };
                                        retryRequest_1.onerror = function () { return reject(retryRequest_1.error); };
                                    }
                                }).catch(function (error) {
                                    console.warn('Database recreation failed:', error);
                                    // Fall back to memory storage and reject
                                    _this.useIndexedDB = false;
                                    reject(error);
                                });
                            };
                            request.onsuccess = function () { return resolve(); };
                            request.onerror = function () {
                                var _a, _b;
                                if (((_a = request.error) === null || _a === void 0 ? void 0 : _a.name) === 'UnknownError' || ((_b = request.error) === null || _b === void 0 ? void 0 : _b.name) === 'NotFoundError') {
                                    // Database might have been deleted, try to recreate
                                    _this.recreateDatabase().catch(function (error) {
                                        console.warn('Database recreation failed:', error);
                                        _this.useIndexedDB = false;
                                    }).finally(function () { return reject(request.error); });
                                }
                                else {
                                    reject(request.error);
                                }
                            };
                        })];
                    case 4:
                        error_1 = _a.sent();
                        this.useIndexedDB = false;
                        throw error_1;
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    AliTrackLogger.prototype.getLogsFromIndexedDB = function (limit) {
        return __awaiter(this, void 0, void 0, function () {
            var error_2;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!this.useIndexedDB)
                            return [2 /*return*/, []];
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 4, , 5]);
                        if (!(!this.db || !this.isDatabaseHealthy())) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.recreateDatabase()];
                    case 2:
                        _a.sent();
                        if (!this.db || !this.useIndexedDB) {
                            return [2 /*return*/, []];
                        }
                        _a.label = 3;
                    case 3: return [2 /*return*/, new Promise(function (resolve, reject) {
                            var transaction = _this.db.transaction([_this.STORE_NAME], 'readonly');
                            var store = transaction.objectStore(_this.STORE_NAME);
                            var index = store.index('timestamp');
                            var request = index.openCursor(null, 'next');
                            var logs = [];
                            transaction.onerror = function () {
                                var _a, _b;
                                if (((_a = transaction.error) === null || _a === void 0 ? void 0 : _a.name) === 'UnknownError' || ((_b = transaction.error) === null || _b === void 0 ? void 0 : _b.name) === 'NotFoundError') {
                                    _this.recreateDatabase().catch(function (error) {
                                        console.warn('Database recreation failed:', error);
                                        _this.useIndexedDB = false;
                                    }).finally(function () { return resolve([]); });
                                }
                                else {
                                    reject(transaction.error);
                                }
                            };
                            request.onsuccess = function (event) {
                                var cursor = event.target.result;
                                if (cursor && logs.length < limit) {
                                    logs.push(cursor.value);
                                    cursor.continue();
                                }
                                else {
                                    resolve(logs.map(function (item) {
                                        var _a;
                                        return __assign(__assign({}, item), { id: (_a = item.id) === null || _a === void 0 ? void 0 : _a.toString() });
                                    }));
                                }
                            };
                            request.onerror = function () {
                                var _a, _b;
                                if (((_a = request.error) === null || _a === void 0 ? void 0 : _a.name) === 'UnknownError' || ((_b = request.error) === null || _b === void 0 ? void 0 : _b.name) === 'NotFoundError') {
                                    _this.recreateDatabase().catch(function (error) {
                                        console.warn('Database recreation failed:', error);
                                        _this.useIndexedDB = false;
                                    }).finally(function () { return resolve([]); });
                                }
                                else {
                                    reject(request.error);
                                }
                            };
                        })];
                    case 4:
                        error_2 = _a.sent();
                        this.useIndexedDB = false;
                        return [2 /*return*/, []];
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    AliTrackLogger.prototype.deleteLogsFromIndexedDB = function (logsToDelete) {
        return __awaiter(this, void 0, void 0, function () {
            var error_3;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!this.useIndexedDB || logsToDelete.length === 0)
                            return [2 /*return*/];
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 4, , 5]);
                        if (!(!this.db || !this.isDatabaseHealthy())) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.recreateDatabase()];
                    case 2:
                        _a.sent();
                        if (!this.db || !this.useIndexedDB) {
                            return [2 /*return*/];
                        }
                        _a.label = 3;
                    case 3: return [2 /*return*/, new Promise(function (resolve, reject) {
                            var transaction = _this.db.transaction([_this.STORE_NAME], 'readwrite');
                            var store = transaction.objectStore(_this.STORE_NAME);
                            var deletePromises = logsToDelete.map(function (log) {
                                return new Promise(function (resolveDelete, rejectDelete) {
                                    var logId = Number(log.id);
                                    if (logId) {
                                        var request_2 = store.delete(logId);
                                        request_2.onsuccess = function () { return resolveDelete(); };
                                        request_2.onerror = function () {
                                            var _a, _b;
                                            if (((_a = request_2.error) === null || _a === void 0 ? void 0 : _a.name) === 'UnknownError' || ((_b = request_2.error) === null || _b === void 0 ? void 0 : _b.name) === 'NotFoundError') {
                                                // Database might have been deleted, ignore deletion errors
                                                resolveDelete();
                                            }
                                            else {
                                                rejectDelete(request_2.error);
                                            }
                                        };
                                    }
                                    else {
                                        resolveDelete();
                                    }
                                });
                            });
                            transaction.onerror = function () {
                                var _a, _b;
                                if (((_a = transaction.error) === null || _a === void 0 ? void 0 : _a.name) === 'UnknownError' || ((_b = transaction.error) === null || _b === void 0 ? void 0 : _b.name) === 'NotFoundError') {
                                    _this.recreateDatabase().catch(function (error) {
                                        console.warn('Database recreation failed:', error);
                                        _this.useIndexedDB = false;
                                    }).finally(function () { return resolve(); });
                                }
                                else {
                                    reject(transaction.error);
                                }
                            };
                            Promise.all(deletePromises)
                                .then(function () { return resolve(); })
                                .catch(reject);
                        })];
                    case 4:
                        error_3 = _a.sent();
                        this.useIndexedDB = false;
                        return [3 /*break*/, 5];
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    AliTrackLogger.prototype.start = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                clearInterval(this.checkTimer);
                this.checkTimer = setInterval(function () { return __awaiter(_this, void 0, void 0, function () {
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, this.uploadLog()];
                            case 1:
                                _a.sent();
                                return [2 /*return*/];
                        }
                    });
                }); }, this.config.checkTime);
                return [2 /*return*/];
            });
        });
    };
    AliTrackLogger.prototype.pause = function () {
        clearInterval(this.checkTimer);
    };
    AliTrackLogger.prototype.addLog = function (message, level, taskId, randomId) {
        return __awaiter(this, void 0, void 0, function () {
            var log, error_4;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        log = { level: level, taskId: taskId, message: message, randomId: randomId, timestamp: Date.now().toString() };
                        if (!this.useIndexedDB) return [3 /*break*/, 5];
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, this.storeLogInIndexedDB(log)];
                    case 2:
                        _a.sent();
                        return [3 /*break*/, 4];
                    case 3:
                        error_4 = _a.sent();
                        this.logList.push(log);
                        return [3 /*break*/, 4];
                    case 4: return [3 /*break*/, 6];
                    case 5:
                        this.logList.push(log);
                        _a.label = 6;
                    case 6: return [2 /*return*/];
                }
            });
        });
    };
    AliTrackLogger.prototype.isDatabaseHealthy = function () {
        if (!this.db)
            return false;
        try {
            // Check if the database connection is still valid and object store exists
            var transaction = this.db.transaction([this.STORE_NAME], 'readonly');
            var store = transaction.objectStore(this.STORE_NAME);
            // Actually try to access the store to validate it exists and works
            var index = store.index('timestamp');
            return true;
        }
        catch (error) {
            console.warn('Database health check failed:', error);
            return false;
        }
    };
    AliTrackLogger.prototype.recreateDatabase = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                return [2 /*return*/, new Promise(function (resolve, reject) {
                        try {
                            // Close existing connection if any
                            if (_this.db) {
                                _this.db.close();
                                _this.db = null;
                            }
                            var request_3 = indexedDB.open(_this.DB_NAME, 1);
                            request_3.onerror = function () {
                                _this.useIndexedDB = false;
                                reject(request_3.error);
                            };
                            request_3.onsuccess = function () {
                                _this.db = request_3.result;
                                _this.useIndexedDB = true;
                                resolve();
                            };
                            request_3.onupgradeneeded = function (event) {
                                var db = event.target.result;
                                if (!db.objectStoreNames.contains(_this.STORE_NAME)) {
                                    var store = db.createObjectStore(_this.STORE_NAME, { keyPath: 'id', autoIncrement: true });
                                    store.createIndex('timestamp', 'timestamp');
                                }
                            };
                        }
                        catch (error) {
                            _this.useIndexedDB = false;
                            reject(error);
                        }
                    })];
            });
        });
    };
    AliTrackLogger.prototype.destroy = function () {
        clearInterval(this.checkTimer);
        window.removeEventListener("message", this.uploadLoggerGlobalEvent);
        if (this.db) {
            this.db.close();
            this.db = null;
        }
    };
    AliTrackLogger.prototype.uploadLog = function (focus) {
        if (focus === void 0) { focus = false; }
        return __awaiter(this, void 0, void 0, function () {
            var uploadLog, errHandle, res, e_1;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        uploadLog = [];
                        if (!this.useIndexedDB) return [3 /*break*/, 2];
                        return [4 /*yield*/, this.getLogsFromIndexedDB(this.BATCH_SIZE)];
                    case 1:
                        uploadLog = _a.sent();
                        if (uploadLog.length === 0) {
                            return [2 /*return*/];
                        }
                        return [3 /*break*/, 3];
                    case 2:
                        if (!(this.logList.length >= this.config.buffer || focus)) {
                            return [2 /*return*/];
                        }
                        uploadLog = this.logList.splice(0);
                        _a.label = 3;
                    case 3:
                        errHandle = function () {
                            if (!_this.useIndexedDB) {
                                _this.logList = uploadLog.concat(_this.logList);
                            }
                            if (_this.retryTimes > 3) {
                                _this.pause();
                            }
                            _this.retryTimes += 1;
                        };
                        _a.label = 4;
                    case 4:
                        _a.trys.push([4, 10, , 11]);
                        return [4 /*yield*/, fetch("https://netless-ppt-plugin-dev.cn-hangzhou.log.aliyuncs.com/logstores/netless-ppt-plugin-dev/track", {
                                method: "POST",
                                headers: {
                                    "x-log-apiversion": "0.6.0",
                                    "Content-Type": "application/json",
                                    "Accept": "*/*"
                                },
                                body: JSON.stringify({
                                    __topic__: "netless-ppt-plugin-dev",
                                    __source__: "netless-ppt-plugin-dev",
                                    __logs__: uploadLog,
                                })
                            })];
                    case 5:
                        res = _a.sent();
                        if (!res.ok) return [3 /*break*/, 8];
                        this.retryTimes = 0;
                        if (!this.useIndexedDB) return [3 /*break*/, 7];
                        return [4 /*yield*/, this.deleteLogsFromIndexedDB(uploadLog)];
                    case 6:
                        _a.sent();
                        _a.label = 7;
                    case 7: return [3 /*break*/, 9];
                    case 8:
                        errHandle();
                        _a.label = 9;
                    case 9: return [3 /*break*/, 11];
                    case 10:
                        e_1 = _a.sent();
                        console.warn(e_1);
                        errHandle();
                        return [3 /*break*/, 11];
                    case 11: return [2 /*return*/];
                }
            });
        });
    };
    AliTrackLogger.prototype.setEnable = function (enable) {
        if (this.enable === enable) {
            return;
        }
        if (enable) {
            this.start();
        }
        else {
            this.pause();
        }
    };
    return AliTrackLogger;
}());
export { AliTrackLogger };
