import { blockHeader } from '@polkadot-api/substrate-bindings';
import { createClient } from '@polkadot-api/substrate-client';
import { middleware } from '@polkadot-api/ws-middleware';
import { getWsProvider, SocketEvents } from '@polkadot-api/ws-provider';
import { Binary } from 'polkadot-api';
import { logger } from './logger.js';

const wsSource = (url, options = {}) => {
  const log = logger.child({ module: "remote-source" });
  const substrateClient = createClient(
    getWsProvider(url, {
      middleware,
      logger: (evt) => {
        switch (evt.type) {
          case SocketEvents.CONNECTING:
            return log.info({ url: evt.url }, evt.type);
          case SocketEvents.ERROR:
            return log.error({ error: evt.error }, evt.type);
          case SocketEvents.IN:
          case SocketEvents.OUT:
            return log.trace({ msg: evt.msg }, evt.type);
          default:
            return log.info(evt.type);
        }
      }
    })
  );
  const archive = substrateClient.archive;
  const block = new Promise(async (resolve, reject) => {
    try {
      let blockHash;
      if (options.atBlock === void 0) {
        const finalizedHeight = await archive.finalizedHeight();
        const hashes = await archive.hashByHeight(finalizedHeight);
        const hash = hashes[0];
        if (!hash) {
          throw new Error(
            `No block found at finalized height ${finalizedHeight}`
          );
        }
        blockHash = hash;
      } else if (typeof options.atBlock === "number") {
        const hashes = await archive.hashByHeight(options.atBlock);
        const hash = hashes[0];
        if (!hash) {
          throw new Error(`No block found at height ${options.atBlock}`);
        }
        blockHash = hash;
      } else {
        blockHash = options.atBlock;
      }
      log.debug({ blockHash }, "loading block");
      const headerHex = await archive.header(blockHash);
      const header = blockHeader.dec(Binary.fromHex(headerHex));
      const body = await archive.body(blockHash);
      const block2 = { blockHash, header, body: body.map(Binary.fromHex) };
      log.debug({ hash: block2.blockHash }, "block loaded");
      resolve(block2);
    } catch (ex) {
      log.error(ex, "error loading block");
      reject(ex);
    }
  });
  return {
    block,
    async getStorage(key) {
      const { blockHash } = await block;
      const value = await archive.storage(blockHash, "value", key, null);
      return value ? Binary.fromHex(value) : null;
    },
    async getStorageBatch(keys) {
      const { blockHash } = await block;
      return new Promise((resolve, reject) => {
        const results = /* @__PURE__ */ new Map();
        const inputs = keys.map((key) => ({ key, type: "value" }));
        const unsub = archive.storageSubscription(
          blockHash,
          inputs,
          null,
          (item) => {
            results.set(
              item.key,
              item.value ? Binary.fromHex(item.value) : null
            );
          },
          reject,
          () => {
            resolve(keys.map((key) => results.get(key) ?? null));
            unsub();
          }
        );
      });
    },
    async getStorageDescendants(prefix) {
      const { blockHash } = await block;
      const entries = await archive.storage(
        blockHash,
        "descendantsValues",
        prefix,
        null
      );
      const result = {};
      for (const { key, value } of entries) {
        result[key] = Binary.fromHex(value);
      }
      return result;
    },
    getChainSpecData() {
      return substrateClient.getChainSpecData();
    },
    destroy() {
      substrateClient.destroy();
    },
    archive: {
      getBody: async (hash) => (await archive.body(hash)).map(Binary.fromHex),
      getHeader: async (hash) => blockHeader.dec(Binary.fromHex(await archive.header(hash))),
      call: (hash, fnName, parameters) => archive.call(hash, fnName, parameters),
      getHashByHeight: (height) => archive.hashByHeight(height),
      storageSubscription: archive.storageSubscription
    }
  };
};

export { wsSource };
//# sourceMappingURL=source.js.map
