import { Turnkey } from "@turnkey/sdk-server";
import {
  AptosClient,
  AptosAccount,
  TxnBuilderTypes,
  BCS,
  TransactionBuilder,
  HexString,
} from "aptos";
// Custom Turnkey signer for Aptos
class TurnkeyAptosSigner {
  private turnkeyClient: Turnkey;
  private aptosClient: AptosClient;
  private address: string;
  private organizationId: string;
  constructor(
    apiPrivateKey: string,
    apiPublicKey: string,
    organizationId: string,
    address: string,
    nodeUrl: string = "https://fullnode.mainnet.aptoslabs.com/v1"
  ) {
    this.turnkeyClient = new Turnkey({
      apiBaseUrl: "https://api.turnkey.com",
      apiPrivateKey,
      apiPublicKey,
      defaultOrganizationId: organizationId,
    });
    this.aptosClient = new AptosClient(nodeUrl);
    this.address = address;
    this.organizationId = organizationId;
  }
  // Get the account address
  getAddress(): string {
    return this.address;
  }
  // Sign a transaction using Turnkey
  async signTransaction(
    rawTxn: TxnBuilderTypes.RawTransaction
  ): Promise<Uint8Array> {
    // Serialize the raw transaction to BCS
    const serializer = new BCS.Serializer();
    rawTxn.serialize(serializer);
    const toSign = serializer.getBytes();
    // Sign the serialized transaction with Turnkey
    const signResult = await this.turnkeyClient.signRawPayload({
      organizationId: this.organizationId,
      signWith: this.address,
      payload: Buffer.from(toSign).toString("hex"),
      encoding: "hex",
    });
    // Return the signed transaction bytes
    return Buffer.from(signResult.signature, "hex");
  }
  // Submit a transaction
  async submitTransaction(payload: any): Promise<string> {
    try {
      // Create a raw transaction from the payload
      const rawTxn = await this.createRawTransaction(payload);
      // Sign the transaction
      const signature = await this.signTransaction(rawTxn);
      // Get the authenticator
      const authenticator = new TxnBuilderTypes.TransactionAuthenticatorEd25519(
        new TxnBuilderTypes.Ed25519PublicKey(
          // Note: In a real implementation, you would need to get the actual public key
          new Uint8Array(32) // Placeholder - replace with actual public key
        ),
        new TxnBuilderTypes.Ed25519Signature(signature)
      );
      // Create a signed transaction
      const signedTxn = new TxnBuilderTypes.SignedTransaction(
        rawTxn,
        authenticator
      );
      // Submit the transaction
      const pendingTxn = await this.aptosClient.submitSignedBCSTransaction(
        BCS.bcsToBytes(signedTxn)
      );
      return pendingTxn.hash;
    } catch (error) {
      console.error("Error submitting transaction:", error);
      throw error;
    }
  }
  // Helper method to create a raw transaction
  private async createRawTransaction(
    payload: any
  ): Promise<TxnBuilderTypes.RawTransaction> {
    const account = await this.aptosClient.getAccount(this.address);
    const chainId = await this.aptosClient.getChainId();
    // Create a raw transaction
    return new TxnBuilderTypes.RawTransaction(
      TxnBuilderTypes.AccountAddress.fromHex(this.address),
      BigInt(account.sequence_number),
      payload,
      BigInt(2000), // Max gas amount
      BigInt(100), // Gas unit price
      BigInt(Math.floor(Date.now() / 1000) + 30), // Expiration timestamp (30 seconds from now)
      new TxnBuilderTypes.ChainId(chainId)
    );
  }
}
// Example usage: Transfer coins
async function transferCoins() {
  const signer = new TurnkeyAptosSigner(
    process.env.API_PRIVATE_KEY!,
    process.env.API_PUBLIC_KEY!,
    process.env.ORGANIZATION_ID!,
    process.env.APTOS_ADDRESS!, // Your Aptos address in Turnkey
    "https://fullnode.testnet.aptoslabs.com/v1" // Testnet URL
  );
  const recipientAddress = "0x..."; // Recipient address
  const amount = 1000000; // Amount in octas (1 APT = 100,000,000 octas)
  // Create a transfer transaction payload
  const payload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
    TxnBuilderTypes.EntryFunction.natural(
      "0x1::coin",
      "transfer",
      [
        new TxnBuilderTypes.TypeTagStruct(
          TxnBuilderTypes.StructTag.fromString("0x1::aptos_coin::AptosCoin")
        ),
      ],
      [
        BCS.bcsToBytes(
          TxnBuilderTypes.AccountAddress.fromHex(recipientAddress)
        ),
        BCS.bcsSerializeUint64(amount),
      ]
    )
  );
  try {
    const txnHash = await signer.submitTransaction(payload);
    console.log(`Transaction submitted successfully! Hash: ${txnHash}`);
    return txnHash;
  } catch (error) {
    console.error("Error transferring coins:", error);
    throw error;
  }
}