"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 __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogStoreRegistry = void 0;
const LogStoreManager_json_1 = require("@logsn/contracts/artifacts/src/StoreManager.sol/LogStoreManager.json");
const shared_1 = require("@logsn/shared");
const protocol_1 = require("@streamr/protocol");
const utils_1 = require("@streamr/utils");
const min_1 = __importDefault(require("lodash/min"));
const streamr_client_1 = __importDefault(require("streamr-client"));
const tsyringe_1 = require("tsyringe");
const Config_1 = require("../Config");
const Ethereum_1 = require("../Ethereum");
const events_1 = require("../events");
const Authentication_1 = require("../streamr/Authentication");
const Config_2 = require("../streamr/Config");
const ContractFactory_1 = require("../streamr/ContractFactory");
const LoggerFactory_1 = require("../streamr/LoggerFactory");
const StreamIDBuilder_1 = require("../streamr/StreamIDBuilder");
const StreamrClient_1 = require("../streamr/StreamrClient");
const contract_1 = require("../streamr/utils/contract");
const SynchronizedGraphQLClient_1 = require("../utils/SynchronizedGraphQLClient");
/**
 * Stores storage node assignments (mapping of streamIds <-> storage nodes addresses)
 */
let LogStoreRegistry = class LogStoreRegistry {
    constructor(contractFactory, streamrClient, streamIdBuilder, graphQLClient, eventEmitter, authentication, loggerFactory, logStoreClientConfig, streamrClientConfig) {
        this.contractFactory = contractFactory;
        this.streamrClient = streamrClient;
        this.streamIdBuilder = streamIdBuilder;
        this.graphQLClient = graphQLClient;
        this.eventEmitter = eventEmitter;
        this.authentication = authentication;
        this.logStoreClientConfig = logStoreClientConfig;
        this.streamrClientConfig = streamrClientConfig;
        this.logger = loggerFactory.createLogger(module);
        this.logStoreManagerContractsReadonly = (0, Ethereum_1.getStreamRegistryChainProviders)(this.streamrClientConfig).map((provider) => {
            return this.contractFactory.createReadContract((0, utils_1.toEthereumAddress)(this.logStoreClientConfig.contracts.logStoreStoreManagerChainAddress), LogStoreManager_json_1.abi, provider, 'logStoreManager');
        });
        this.initStreamAssignmentEventListener('addToLogStore', 'StoreUpdated', eventEmitter);
    }
    // TODO: Log Store doesn't actually add Streams to storage directly via the Client...
    initStreamAssignmentEventListener(clientEvent, contractEvent, eventEmitter) {
        const primaryReadonlyContract = this.logStoreManagerContractsReadonly[0];
        // type Listener = (streamId: string, nodeAddress: string, extra: any) => void;
        this.logger.debug('initStreamAssignmentEventListener');
        (0, events_1.initEventGateway)(clientEvent, (emit) => {
            const listener = (store, isNew, amount, extra) => {
                this.logger.debug('Emitting event', {
                    contractEvent,
                    store,
                });
                emit({
                    store,
                    isNew,
                    amount,
                    // nodeAddress: toEthereumAddress(nodeAddress),
                    blockNumber: extra.blockNumber,
                });
            };
            primaryReadonlyContract.on(contractEvent, listener);
            return listener;
        }, (listener) => {
            primaryReadonlyContract.off(contractEvent, listener);
        }, eventEmitter);
    }
    async connectToContract() {
        if (!this.logStoreManagerContract) {
            const chainSigner = await this.authentication.getStreamRegistryChainSigner();
            this.logStoreManagerContract =
                this.contractFactory.createWriteContract((0, utils_1.toEthereumAddress)(this.logStoreClientConfig.contracts.logStoreStoreManagerChainAddress), LogStoreManager_json_1.abi, chainSigner, 'logStoreManager');
        }
    }
    // --------------------------------------------------------------------------------------------
    // Log Store
    // --------------------------------------------------------------------------------------------
    async stakeOrCreateStore(streamIdOrPath, amount, overrides) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        this.logger.debug('adding stream to LogStore', { streamId });
        await this.connectToContract();
        this.logger.debug('approving LogStore contract for token funds', {
            streamId,
        });
        // @dev 'chainSigner' could either be a wallet or a signer
        // @dev depending on if a pk was passed into the contract
        const chainSigner = await this.authentication.getStreamRegistryChainSigner();
        const mergedOverrides = {
            ...(0, Ethereum_1.getStreamRegistryOverrides)(this.streamrClientConfig),
            ...overrides,
        };
        await (0, shared_1.prepareStakeForStoreManager)(chainSigner, amount, false, undefined, true, mergedOverrides);
        this.logger.debug('About to call stake()');
        return this.logStoreManagerContract.stake(streamId, amount, mergedOverrides);
    }
    async getStreamBalance(streamIdOrPath) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        // `stores` maps the streamId to the storage balance
        return await (0, contract_1.queryAllReadonlyContracts)(async (contract) => {
            const storeBalanceBN = await contract.stores(streamId);
            return storeBalanceBN.toBigInt();
        }, this.logStoreManagerContractsReadonly);
    }
    async getStoreBalanceOf(address) {
        return await (0, contract_1.queryAllReadonlyContracts)(async (contract) => {
            const storeBalanceBN = await contract.balanceOf(address);
            return storeBalanceBN.toBigInt();
        }, this.logStoreManagerContractsReadonly);
    }
    async getStoreBalance() {
        const signer = await this.authentication.getStreamRegistryChainSigner();
        const address = await signer.getAddress();
        return await this.getStoreBalanceOf(address);
    }
    async isLogStoreStream(streamIdOrPath) {
        const streamId = await this.streamIdBuilder.toStreamID(streamIdOrPath);
        this.logger.debug('querying if stream is in LogStore', { streamId });
        return (0, contract_1.queryAllReadonlyContracts)((contract) => {
            return contract.exists(streamId);
        }, this.logStoreManagerContractsReadonly);
    }
    async getStoredStreams() {
        this.logger.debug('getting stored streams');
        const blockNumbers = [];
        // await collect(
        const res = await (0, utils_1.collect)(this.graphQLClient.fetchPaginatedResults((lastId, pageSize) => {
            const query = `
					{
						storeUpdateds(first: ${pageSize}, orderBy: store where: {store_gt: "${lastId}"}) {
							store
						}
						_meta {
							block {
								number
							}
						}
					}`;
            return { query };
        }, (response) => {
            // eslint-disable-next-line no-underscore-dangle
            blockNumbers.push(response._meta.block.number);
            return response.storeUpdateds;
            // return response.node !== null ? response.node.storedStreams : [];
        }));
        this.logger.debug('res: ', { res });
        const streams = (await Promise.all(res.map(async (storeUpdated) => {
            try {
                return await this.streamrClient.getStream((0, protocol_1.toStreamID)(storeUpdated.store));
            }
            catch (err) {
                this.logger.error(err);
                return null;
            }
        }))).filter((stream) => stream != null);
        this.logger.debug('streams: ', {
            streams: streams.map((stream) => stream.id.toString()),
        });
        return {
            streams,
            blockNumber: (0, min_1.default)(blockNumbers),
        };
    }
    // --------------------------------------------------------------------------------------------
    // Events
    // --------------------------------------------------------------------------------------------
    /**
     * Adds an event listener to the client.
     * @param eventName - event name, see {@link LogStoreClientEvents} for options
     * @param listener - the callback function
     */
    on(eventName, listener) {
        this.eventEmitter.on(eventName, listener);
    }
    /**
     * Adds an event listener to the client that is invoked only once.
     * @param eventName - event name, see {@link LogStoreClientEvents} for options
     * @param listener - the callback function
     */
    once(eventName, listener) {
        this.eventEmitter.once(eventName, listener);
    }
    /**
     * Removes an event listener from the client.
     * @param eventName - event name, see {@link LogStoreClientEvents} for options
     * @param listener - the callback function to remove
     */
    off(eventName, listener) {
        this.eventEmitter.off(eventName, listener);
    }
};
exports.LogStoreRegistry = LogStoreRegistry;
exports.LogStoreRegistry = LogStoreRegistry = __decorate([
    (0, tsyringe_1.scoped)(tsyringe_1.Lifecycle.ContainerScoped),
    __param(0, (0, tsyringe_1.inject)(ContractFactory_1.ContractFactoryInjectionToken)),
    __param(1, (0, tsyringe_1.inject)(StreamrClient_1.StreamrClientInjectionToken)),
    __param(2, (0, tsyringe_1.inject)(StreamIDBuilder_1.StreamIDBuilderInjectionToken)),
    __param(3, (0, tsyringe_1.inject)(SynchronizedGraphQLClient_1.SynchronizedGraphQLClient)),
    __param(4, (0, tsyringe_1.inject)(events_1.LogStoreClientEventEmitter)),
    __param(5, (0, tsyringe_1.inject)(Authentication_1.AuthenticationInjectionToken)),
    __param(6, (0, tsyringe_1.inject)(LoggerFactory_1.LoggerFactoryInjectionToken)),
    __param(7, (0, tsyringe_1.inject)(Config_1.LogStoreClientConfigInjectionToken)),
    __param(8, (0, tsyringe_1.inject)(Config_2.StreamrClientConfigInjectionToken)),
    __metadata("design:paramtypes", [Object, streamr_client_1.default, Object, SynchronizedGraphQLClient_1.SynchronizedGraphQLClient,
        events_1.LogStoreClientEventEmitter, Object, Object, Object, Object])
], LogStoreRegistry);
//# sourceMappingURL=LogStoreRegistry.js.map