'use strict';

var setErc20Allowance = require('./setErc20Allowance-f4b04a7b.cjs.dev.js');
var ethers = require('ethers');
require('zod');

function _interopNamespace(e) {
  if (e && e.__esModule) return e;
  var n = Object.create(null);
  if (e) {
    Object.keys(e).forEach(function (k) {
      if (k !== 'default') {
        var d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: function () { return e[k]; }
        });
      }
    });
  }
  n["default"] = e;
  return Object.freeze(n);
}

/**
 * @internal
 */

/**
 * @internal
 */
const InterfaceId_IERC721 = /* @__PURE__ */(() => ethers.utils.arrayify("0x80ac58cd"))();

/**
 * @internal
 */
const InterfaceId_IERC1155 = /* @__PURE__ */(() => ethers.utils.arrayify("0xd9b67a26"))();

const FALLBACK_METADATA = {
  name: "Failed to load NFT metadata"
};

/**
 * fetches the token metadata
 * @param tokenId - the id (to get it back in the output)
 * @param tokenUri - the uri to fetch
 * @param storage - which storage to fetch from
 *
 * @internal
 */
async function fetchTokenMetadata(tokenId, tokenUri, storage) {
  // check for base64 encoded JSON
  if (tokenUri.startsWith("data:application/json;base64") && typeof Buffer !== "undefined") {
    const base64 = tokenUri.split(",")[1];
    const jsonMetadata = JSON.parse(Buffer.from(base64, "base64").toString("utf-8"));
    return setErc20Allowance.CommonNFTOutput.parse({
      ...jsonMetadata,
      id: ethers.BigNumber.from(tokenId).toString(),
      uri: tokenUri
    });
  }
  // handle dynamic id URIs (2 possible formats)
  const parsedUri = tokenUri.replace("{id}", ethers.utils.hexZeroPad(ethers.BigNumber.from(tokenId).toHexString(), 32).slice(2));
  let jsonMetadata;
  try {
    jsonMetadata = await storage.downloadJSON(parsedUri);
  } catch (err) {
    const unparsedTokenIdUri = tokenUri.replace("{id}", ethers.BigNumber.from(tokenId).toString());
    try {
      jsonMetadata = await storage.downloadJSON(unparsedTokenIdUri);
    } catch (e) {
      console.warn(`failed to get token metadata: ${JSON.stringify({
        tokenId: tokenId.toString(),
        tokenUri
      })} -- falling back to default metadata`);
      jsonMetadata = FALLBACK_METADATA;
    }
  }
  return setErc20Allowance.CommonNFTOutput.parse({
    ...jsonMetadata,
    id: ethers.BigNumber.from(tokenId).toString(),
    uri: tokenUri
  });
}

// Used for marketplace to fetch NFT metadata from contract address + tokenId
/**
 * @internal
 * @param contractAddress - the contract address
 * @param provider - the provider to use
 * @param tokenId - the token id
 * @param storage - the storage to use
 */
async function fetchTokenMetadataForContract(contractAddress, provider, tokenId, storage) {
  let uri;
  const ERC165MetadataAbi = (await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('@thirdweb-dev/contracts-js/dist/abis/IERC165.json')); })).default;
  const erc165 = new ethers.Contract(contractAddress, ERC165MetadataAbi, provider);
  const [isERC721, isERC1155] = await Promise.all([erc165.supportsInterface(InterfaceId_IERC721), erc165.supportsInterface(InterfaceId_IERC1155)]);
  if (isERC721) {
    const ERC721MetadataAbi = (await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('@thirdweb-dev/contracts-js/dist/abis/IERC721Metadata.json')); })).default;
    const erc721 = new ethers.Contract(contractAddress, ERC721MetadataAbi, provider);
    uri = await erc721.tokenURI(tokenId);
  } else if (isERC1155) {
    const ERC1155MetadataAbi = (await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('@thirdweb-dev/contracts-js/dist/abis/IERC1155Metadata.json')); })).default;
    const erc1155 = new ethers.Contract(contractAddress, ERC1155MetadataAbi, provider);
    uri = await erc1155.uri(tokenId);
  } else {
    throw Error("Contract must implement ERC 1155 or ERC 721.");
  }
  if (!uri) {
    // no uri found, return fallback metadata
    return setErc20Allowance.CommonNFTOutput.parse({
      ...FALLBACK_METADATA,
      id: ethers.BigNumber.from(tokenId).toString(),
      uri: ""
    });
  }
  return fetchTokenMetadata(tokenId, uri, storage);
}

/**
 * @internal
 * @param metadata - the metadata to upload
 * @param storage - the storage to use
 */
async function uploadOrExtractURI(metadata, storage) {
  if (typeof metadata === "string") {
    return metadata;
  } else {
    return await storage.upload(setErc20Allowance.CommonNFTInput.parse(metadata));
  }
}

/**
 * @internal
 * @param metadatas - the metadata to upload
 * @param storage - the storage to use
 * @param startNumber - the number to start the file names at
 * @param contractAddress - the contract address
 * @param signerAddress - the signer address
 * @param options - options
 */
async function uploadOrExtractURIs(metadatas, storage, startNumber, options) {
  if (isUriList(metadatas)) {
    return metadatas;
  } else if (isMetadataList(metadatas)) {
    const uris = await storage.uploadBatch(metadatas.map(m => setErc20Allowance.CommonNFTInput.parse(m)), {
      rewriteFileNames: {
        fileStartNumber: startNumber || 0
      },
      onProgress: options?.onProgress
    });
    return uris;
  } else {
    throw new Error("NFT metadatas must all be of the same type (all URI or all NFTMetadataInput)");
  }
}
function getBaseUriFromBatch(uris) {
  const baseUri = uris[0].substring(0, uris[0].lastIndexOf("/"));
  for (let i = 0; i < uris.length; i++) {
    const uri = uris[i].substring(0, uris[i].lastIndexOf("/"));
    if (baseUri !== uri) {
      throw new Error(`Can only create batches with the same base URI for every entry in the batch. Expected '${baseUri}' but got '${uri}'`);
    }
  }

  // Ensure that baseUri ends with trailing slash
  return baseUri.replace(/\/$/, "") + "/";
}
function isUriList(metadatas) {
  return metadatas.find(m => typeof m !== "string") === undefined;
}
function isMetadataList(metadatas) {
  return metadatas.find(m => typeof m !== "object") === undefined;
}

/**
 * @internal
 */
const DEFAULT_QUERY_ALL_COUNT = 100;

/**
 * Pagination Parameters
 * @public
 */

exports.DEFAULT_QUERY_ALL_COUNT = DEFAULT_QUERY_ALL_COUNT;
exports.FALLBACK_METADATA = FALLBACK_METADATA;
exports.InterfaceId_IERC1155 = InterfaceId_IERC1155;
exports.InterfaceId_IERC721 = InterfaceId_IERC721;
exports.fetchTokenMetadata = fetchTokenMetadata;
exports.fetchTokenMetadataForContract = fetchTokenMetadataForContract;
exports.getBaseUriFromBatch = getBaseUriFromBatch;
exports.uploadOrExtractURI = uploadOrExtractURI;
exports.uploadOrExtractURIs = uploadOrExtractURIs;
