"use strict";
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 __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Polymesh = void 0;
const api_1 = require("@polkadot/api");
const apollo_cache_inmemory_1 = require("apollo-cache-inmemory");
const apollo_client_1 = require("apollo-client");
const apollo_link_1 = require("apollo-link");
const apollo_link_context_1 = require("apollo-link-context");
const apollo_link_http_1 = require("apollo-link-http");
const cross_fetch_1 = __importDefault(require("cross-fetch"));
const schema_1 = __importDefault(require("./polkadot/schema"));
const AccountManagement_1 = require("./AccountManagement");
const Assets_1 = require("./Assets");
const Identities_1 = require("./Identities");
const internal_1 = require("./internal");
const queries_1 = require("./middleware/queries");
const Settlements_1 = require("./Settlements");
const types_1 = require("./types");
const conversion_1 = require("./utils/conversion");
const internal_2 = require("./utils/internal");
const Claims_1 = require("./Claims");
const Network_1 = require("./Network");
/**
 * Main entry point of the Polymesh SDK
 */
class Polymesh {
    /**
     * @hidden
     */
    constructor(context) {
        this.context = {};
        this.context = context;
        this.claims = new Claims_1.Claims(context);
        this.network = new Network_1.Network(context);
        this.settlements = new Settlements_1.Settlements(context);
        this.accountManagement = new AccountManagement_1.AccountManagement(context);
        this.identities = new Identities_1.Identities(context);
        this.assets = new Assets_1.Assets(context);
    }
    /**
     * Create an SDK instance and connect to a Polymesh node
     *
     * @param params.nodeUrl - URL of the Polymesh node this instance will be connecting to
     * @param params.signingManager - object in charge of managing keys and signing transactions
     *   (optional, if not passed the SDK will not be able to submit transactions). Can be set later with
     *   `setSigningManager`
     * @param params.middleware - middleware API URL and key (optional, used for historic queries)
     */
    static connect(params) {
        return __awaiter(this, void 0, void 0, function* () {
            const { nodeUrl, signingManager, middleware } = params;
            let context;
            yield (0, internal_2.assertExpectedChainVersion)(nodeUrl).catch(error => {
                if (error instanceof internal_1.PolymeshError &&
                    error.message.includes('Unsupported Polymesh version')) {
                    console.warn(error.message);
                }
                else {
                    throw error;
                }
            });
            try {
                const { types, rpc } = schema_1.default;
                const polymeshApi = yield api_1.ApiPromise.create({
                    provider: new api_1.WsProvider(nodeUrl),
                    types,
                    rpc,
                });
                let middlewareApi = null;
                if (middleware) {
                    middlewareApi = new apollo_client_1.ApolloClient({
                        link: (0, apollo_link_context_1.setContext)((_, { headers }) => {
                            return {
                                headers: Object.assign(Object.assign({}, headers), { 
                                    // eslint-disable-next-line @typescript-eslint/naming-convention
                                    'x-api-key': middleware.key }),
                            };
                        }).concat(apollo_link_1.ApolloLink.from([
                            new apollo_link_http_1.HttpLink({
                                uri: middleware.link,
                                fetch: cross_fetch_1.default,
                            }),
                        ])),
                        cache: new apollo_cache_inmemory_1.InMemoryCache(),
                        defaultOptions: {
                            watchQuery: {
                                fetchPolicy: 'no-cache',
                            },
                            query: {
                                fetchPolicy: 'no-cache',
                            },
                        },
                    });
                }
                context = yield internal_1.Context.create({
                    polymeshApi,
                    middlewareApi,
                    signingManager,
                });
            }
            catch (err) {
                const { message, code } = err;
                throw new internal_1.PolymeshError({
                    code,
                    message: `Error while connecting to "${nodeUrl}": "${message || 'The node couldn’t be reached'}"`,
                });
            }
            if (middleware) {
                try {
                    yield context.queryMiddleware((0, queries_1.heartbeat)());
                }
                catch (err) {
                    if (err.message.indexOf('Forbidden') > -1 ||
                        err.message.indexOf('Missing Authentication Token') > -1) {
                        throw new internal_1.PolymeshError({
                            code: types_1.ErrorCode.FatalError,
                            message: 'Incorrect middleware URL or API key',
                        });
                    }
                }
            }
            return new Polymesh(context);
        });
    }
    /**
     * Retrieve the Identity associated to the signing Account (null if there is none)
     *
     * @throws if there is no signing Account associated to the SDK
     */
    getSigningIdentity() {
        return this.context.getSigningAccount().getIdentity();
    }
    /**
     * Handle connection errors
     *
     * @returns an unsubscribe callback
     */
    onConnectionError(callback) {
        const { context: { polymeshApi }, } = this;
        polymeshApi.on('error', callback);
        return () => {
            polymeshApi.off('error', callback);
        };
    }
    /**
     * Handle disconnection
     *
     * @returns an unsubscribe callback
     */
    onDisconnect(callback) {
        const { context: { polymeshApi }, } = this;
        polymeshApi.on('disconnected', callback);
        return () => {
            polymeshApi.off('disconnected', callback);
        };
    }
    /**
     * Disconnect the client and close all open connections and subscriptions
     *
     * @note the SDK will become unusable after this operation. It will throw an error when attempting to
     *   access any chain or middleware data. If you wish to continue using the SDK, you must
     *   create a new instance by calling {@link connect}
     */
    disconnect() {
        return this.context.disconnect();
    }
    /**
     * Set the SDK's signing Account to the provided one
     *
     * @throws if the passed Account is not present in the Signing Manager (or there is no Signing Manager)
     */
    setSigningAccount(signer) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.context.setSigningAddress((0, conversion_1.signerToString)(signer));
        });
    }
    /**
     * Set the SDK's Signing Manager to the provided one
     */
    setSigningManager(signingManager) {
        return this.context.setSigningManager(signingManager);
    }
    /* eslint-disable @typescript-eslint/naming-convention */
    /* istanbul ignore next: not part of the official public API */
    /**
     * Polkadot client
     */
    get _polkadotApi() {
        return this.context.getPolymeshApi();
    }
    /* istanbul ignore next: not part of the official public API */
    /**
     * signing address (to manually submit transactions with the polkadot API)
     */
    get _signingAddress() {
        return this.context.getSigningAddress();
    }
    /* istanbul ignore next: not part of the official public API */
    /**
     * Middleware client
     */
    get _middlewareApi() {
        return this.context.middlewareApi;
    }
}
exports.Polymesh = Polymesh;
//# sourceMappingURL=Polymesh.js.map