#!/usr/bin/env node
"use strict";
const fs = require("fs");
const path = require("path");
const BN = require("bn.js");
const { Connection, Keypair, SystemProgram, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js");
const {
  TOKEN_2022_PROGRAM_ID,
  ExtensionType,
  getMintLen,
  createInitializeMintInstruction,
  createInitializePermanentDelegateInstruction,
  createInitializeNonTransferableMintInstruction,
  getAssociatedTokenAddressSync,
  createAssociatedTokenAccountIdempotentInstruction,
  createMintToInstruction,
  createSetAuthorityInstruction,
  AuthorityType,
} = require("@solana/spl-token");
const { parseEnvConfig, resolveConfigPath, loadKeypair, clusterUrl, nowStamp, PublicKey } = require("./js_common");

async function main() {
  const target = process.argv[3];
  if (!target) throw new Error("Usage: node 01_create_nft_for_wallet_admin.js <config.env> <target_wallet>");
  const cfg = parseEnvConfig(resolveConfigPath(process.argv[2]));
  const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed");
  const main = loadKeypair(path.resolve(__dirname, cfg.MAIN_KEYPAIR));
  const governance = new PublicKey(cfg.GOVERNANCE);
  const targetPk = new PublicKey(target);
  const mint = Keypair.generate();
  const mintLen = getMintLen([ExtensionType.NonTransferable, ExtensionType.PermanentDelegate]);
  const rentMint = await conn.getMinimumBalanceForRentExemption(mintLen, "confirmed");
  const ata = getAssociatedTokenAddressSync(mint.publicKey, targetPk, false, TOKEN_2022_PROGRAM_ID);
  const tx = new Transaction().add(
    SystemProgram.createAccount({ fromPubkey: main.publicKey, newAccountPubkey: mint.publicKey, space: mintLen, lamports: rentMint, programId: TOKEN_2022_PROGRAM_ID }),
    createInitializeNonTransferableMintInstruction(mint.publicKey, TOKEN_2022_PROGRAM_ID),
    createInitializePermanentDelegateInstruction(mint.publicKey, main.publicKey, TOKEN_2022_PROGRAM_ID),
    createInitializeMintInstruction(mint.publicKey, 0, main.publicKey, main.publicKey, TOKEN_2022_PROGRAM_ID),
    createAssociatedTokenAccountIdempotentInstruction(main.publicKey, ata, targetPk, mint.publicKey, TOKEN_2022_PROGRAM_ID),
    createMintToInstruction(mint.publicKey, ata, main.publicKey, 1n, [], TOKEN_2022_PROGRAM_ID),
    createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.MintTokens, governance, [], TOKEN_2022_PROGRAM_ID),
    createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.FreezeAccount, governance, [], TOKEN_2022_PROGRAM_ID),
    createSetAuthorityInstruction(mint.publicKey, main.publicKey, AuthorityType.PermanentDelegate, governance, [], TOKEN_2022_PROGRAM_ID)
  );
  const sig = await sendAndConfirmTransaction(conn, tx, [main, mint], { commitment: "confirmed" });
  const runs = path.resolve(__dirname, cfg.RUNS_DIR || "./runs");
  fs.mkdirSync(runs, { recursive: true });
  const report = { createdAt: new Date().toISOString(), mint: mint.publicKey.toBase58(), owner: targetPk.toBase58(), ata: ata.toBase58(), tx: sig };
  const rp = path.join(runs, `${nowStamp()}_admin_create_nft_${targetPk.toBase58().slice(0,8)}.json`);
  fs.writeFileSync(rp, JSON.stringify(report, null, 2));
  console.log("NFT created and delegated to governance");
  console.log("mint:", report.mint);
  console.log("owner:", report.owner);
  console.log("report:", rp);
}
main().catch((e)=>{console.error(e?.message||e);process.exit(1);});
