"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var BlocksQueueLoaderService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BlocksQueueLoaderService = void 0;
const common_1 = require("@nestjs/common");
const exponential_interval_async_1 = require("@easylayer/common/exponential-interval-async");
const load_strategies_1 = require("./load-strategies");
const load_strategy_interface_1 = require("./load-strategies/load-strategy.interface");
let BlocksQueueLoaderService = BlocksQueueLoaderService_1 = class BlocksQueueLoaderService {
    blockchainProviderService;
    mempoolService;
    config;
    log = new common_1.Logger(BlocksQueueLoaderService_1.name);
    _isLoading = false;
    _timer = null;
    _currentStrategy = null;
    _monitoringInterval;
    _strategies = new Map();
    constructor(blockchainProviderService, mempoolService, config) {
        this.blockchainProviderService = blockchainProviderService;
        this.mempoolService = mempoolService;
        this.config = config;
        this._monitoringInterval = Math.max(this.config.blockTimeMs / 2, 3000);
    }
    get isLoading() {
        return this._isLoading;
    }
    async onModuleDestroy() {
        await this._currentStrategy?.stop();
        this._timer?.destroy();
        this._timer = null;
        this._currentStrategy = null;
        this._strategies.clear();
        this._isLoading = false;
    }
    async startBlocksLoading(queue) {
        if (this._isLoading)
            return;
        this._isLoading = true;
        this.createStrategies(queue);
        this._timer = (0, exponential_interval_async_1.exponentialIntervalAsync)(async (resetInterval) => {
            try {
                const currentNetworkHeight = await this.blockchainProviderService.getCurrentBlockHeightFromNetwork();
                await this.mempoolService.refresh(currentNetworkHeight);
                this._currentStrategy = this.getCurrentStrategy(queue, currentNetworkHeight);
                await this._currentStrategy?.load(currentNetworkHeight);
            }
            catch (error) {
                this.log.debug('Loading blocks on pause', { args: { error } });
                await this._currentStrategy?.stop();
                resetInterval();
            }
        }, { interval: 1000, maxInterval: this._monitoringInterval, multiplier: 2 });
    }
    createStrategies(queue) {
        const opts = {
            maxRequestBlocksBatchSize: this.config.queueLoaderRequestBlocksBatchSize,
            basePreloadCount: this.config.basePreloadCount,
            tracesEnabled: this.config.tracesEnabled ?? false,
            verifyTrie: this.config.verifyTrie ?? false,
        };
        this._strategies.set(load_strategy_interface_1.StrategyNames.RPC, new load_strategies_1.RpcProviderStrategy(this.log, this.blockchainProviderService, queue, opts));
        this._strategies.set(load_strategy_interface_1.StrategyNames.WS_SUBSCRIBE, new load_strategies_1.SubscribeWsProviderStrategy(this.log, this.blockchainProviderService, queue, opts));
    }
    getCurrentStrategy(queue, currentNetworkHeight) {
        const configStrategy = this.config.queueLoaderStrategyName;
        if (configStrategy === load_strategy_interface_1.StrategyNames.RPC) {
            return this._strategies.get(load_strategy_interface_1.StrategyNames.RPC);
        }
        if (currentNetworkHeight !== undefined) {
            const diff = currentNetworkHeight - queue.lastHeight;
            const threshold = this.config.strategyThreshold || 20;
            if (diff > threshold)
                return this._strategies.get(load_strategy_interface_1.StrategyNames.RPC);
        }
        return this._strategies.get(load_strategy_interface_1.StrategyNames.WS_SUBSCRIBE);
    }
};
exports.BlocksQueueLoaderService = BlocksQueueLoaderService;
exports.BlocksQueueLoaderService = BlocksQueueLoaderService = BlocksQueueLoaderService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [Function, Function, Object])
], BlocksQueueLoaderService);
