import * as scale from '@pezkuwi/bizinikiwi-bindings';
import { mapObject } from '@pezkuwi/papi-utils';
import { getLookupCodecBuilder } from './lookup-codec-builder.mjs';

const nullCodec = scale.enhanceCodec(
  scale._void,
  () => void 0,
  () => null
);
const getDynamicBuilder = (getLookupEntryDef) => {
  const { metadata } = getLookupEntryDef;
  let buildDefinition = getLookupCodecBuilder(getLookupEntryDef);
  const prefix = metadata.pallets.find((x) => x.name === "System")?.constants.find((x) => x.name === "SS58Prefix");
  let ss58Prefix;
  if (prefix) {
    try {
      const prefixVal = buildDefinition(prefix.type).dec(prefix.value);
      if (typeof prefixVal === "number") {
        ss58Prefix = prefixVal;
        buildDefinition = getLookupCodecBuilder(
          getLookupEntryDef,
          scale.AccountId(prefixVal)
        );
      }
    } catch (_) {
    }
  }
  const storagePallets = /* @__PURE__ */ new Map();
  const buildStorage = (pallet, entry) => {
    let storagePallet = storagePallets.get(pallet);
    if (!storagePallet)
      storagePallets.set(pallet, storagePallet = scale.Storage(pallet));
    const storageEntry = metadata.pallets.find((x) => x.name === pallet).storage.items.find((s) => s.name === entry);
    const withNullVoid = (codec) => codec === scale._void ? nullCodec : codec;
    const storageWithFallback = (len, value2, ...args) => {
      const keys = storagePallet(...args);
      const [, ...encodersWithHash] = args;
      return {
        args: scale.Tuple(...encodersWithHash.map(([codec]) => codec)),
        keys,
        value: value2,
        len,
        fallback: storageEntry.modifier === 1 ? value2.dec(storageEntry.fallback) : void 0
      };
    };
    if (storageEntry.type.tag === "plain")
      return storageWithFallback(
        0,
        withNullVoid(buildDefinition(storageEntry.type.value)),
        entry
      );
    const { key, value, hashers } = storageEntry.type.value;
    const val = withNullVoid(buildDefinition(value));
    const hashes = hashers.map((x) => scale[x.tag]);
    const hashArgs = (() => {
      if (hashes.length === 1) {
        return [[buildDefinition(key), hashes[0]]];
      }
      const keyDef = getLookupEntryDef(key);
      switch (keyDef.type) {
        case "array":
          return hashes.map((hash) => [buildDefinition(keyDef.value.id), hash]);
        case "tuple":
          return keyDef.value.map((x, idx) => [
            buildDefinition(x.id),
            hashes[idx]
          ]);
        default:
          throw new Error("Invalid key type");
      }
    })();
    return storageWithFallback(hashes.length, val, entry, ...hashArgs);
  };
  const buildEnumEntry = (entry) => {
    switch (entry.type) {
      case "void":
        return scale._void;
      case "lookupEntry":
        return buildDefinition(entry.value.id);
      case "tuple":
        return scale.Tuple(
          ...Object.values(entry.value).map((l) => buildDefinition(l.id))
        );
      case "struct":
        return scale.Struct(
          mapObject(entry.value, (x) => buildDefinition(x.id))
        );
      case "array":
        return scale.Vector(buildDefinition(entry.value.id), entry.len);
    }
  };
  const buildConstant = (pallet, constantName) => {
    const storageEntry = metadata.pallets.find((x) => x.name === pallet).constants.find((s) => s.name === constantName);
    return buildDefinition(storageEntry.type);
  };
  const buildVariant = (type) => (pallet, name) => {
    const palletEntry = metadata.pallets.find((x) => x.name === pallet);
    const lookup = getLookupEntryDef(palletEntry[type].type);
    if (lookup.type !== "enum") throw null;
    const entry = lookup.value[name];
    return {
      location: [palletEntry.index, entry.idx],
      codec: buildEnumEntry(lookup.value[name])
    };
  };
  const buildViewFn = (pallet, entry) => {
    const fn = metadata.pallets.find((x) => x.name === pallet)?.viewFns.find((x) => x.name === entry);
    if (!fn) throw null;
    return {
      args: scale.Tuple(...fn.inputs.map((x) => buildDefinition(x.type))),
      value: buildDefinition(fn.output)
    };
  };
  const buildRuntimeCall = (api, method) => {
    const entry = metadata.apis.find((x) => x.name === api)?.methods.find((x) => x.name === method);
    if (!entry) throw null;
    return {
      args: scale.Tuple(...entry.inputs.map((x) => buildDefinition(x.type))),
      value: buildDefinition(entry.output)
    };
  };
  return {
    buildDefinition,
    buildStorage,
    buildEvent: buildVariant("events"),
    buildError: buildVariant("errors"),
    buildViewFn,
    buildRuntimeCall,
    buildCall: buildVariant("calls"),
    buildConstant,
    ss58Prefix
  };
};

export { getDynamicBuilder };
//# sourceMappingURL=dynamic-builder.mjs.map
