import { sha3_256 } from "@noble/hashes/sha3.js";
import { AccountAddress } from "../../accountAddress.js";
import { DeriveScheme } from "../../../types/index.js";
import { TEXT_ENCODER } from "../../../utils/const.js";

/**
 * Creates an object address from creator address and seed
 *
 * @param creatorAddress The object creator account address
 * @param seed The seed in either Uint8Array | string type
 *
 * @returns The object account address
 * @group Implementation
 * @category Account (On-Chain Model)
 */
export const createObjectAddress = (creatorAddress: AccountAddress, seed: Uint8Array | string): AccountAddress => {
  const creatorBytes = creatorAddress.bcsToBytes();
  const seedBytes = typeof seed === "string" ? TEXT_ENCODER.encode(seed) : seed;

  const bytes = new Uint8Array([...creatorBytes, ...seedBytes, DeriveScheme.DeriveObjectAddressFromSeed]);

  return new AccountAddress(sha3_256(bytes));
};

/**
 * Creates a resource address from creator address and seed
 *
 * @param creatorAddress The creator account address
 * @param seed The seed in either Uint8Array | string type
 *
 * @returns The resource account address
 * @group Implementation
 * @category Account (On-Chain Model)
 */
export const createResourceAddress = (creatorAddress: AccountAddress, seed: Uint8Array | string): AccountAddress => {
  const creatorBytes = creatorAddress.bcsToBytes();

  const seedBytes = typeof seed === "string" ? TEXT_ENCODER.encode(seed) : seed;

  const bytes = new Uint8Array([...creatorBytes, ...seedBytes, DeriveScheme.DeriveResourceAccountAddress]);

  return new AccountAddress(sha3_256(bytes));
};

/**
 * Creates a user derived object address from source address and derive_from address
 *
 * @param sourceAddress The source account address
 * @param deriveFromAddress The address to derive from
 *
 * @returns The user derived object address
 * @group Implementation
 * @category Account (On-Chain Model)
 */
export const createUserDerivedObjectAddress = (
  sourceAddress: AccountAddress,
  deriveFromAddress: AccountAddress,
): AccountAddress => {
  const sourceBytes = sourceAddress.bcsToBytes();
  const deriveFromBytes = deriveFromAddress.bcsToBytes();

  const bytes = new Uint8Array([...sourceBytes, ...deriveFromBytes, DeriveScheme.DeriveObjectAddressFromObject]);

  return new AccountAddress(sha3_256(bytes));
};

/**
 * Creates a token object address from creator address, collection name and token name
 *
 * @param creatorAddress The token creator account address
 * @param collectionName The collection name
 * @param tokenName The token name
 *
 * @returns The token account address
 * @group Implementation
 * @category Account (On-Chain Model)
 */
export const createTokenAddress = (
  creatorAddress: AccountAddress,
  collectionName: string,
  tokenName: string,
): AccountAddress => {
  const seed = `${collectionName}::${tokenName}`;
  return createObjectAddress(creatorAddress, seed);
};
