import type { SuiClient } from '@mysten/sui/client';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import type { SuiConfig } from '../../config.js';
import { BaseSDK } from '../sdk.js';
import { createSuiClient } from './client.js';
import { getSuiOrderTransaction } from './order-transaction.js';
import { getSuiCancelCrossChainOrder } from './cancel.js';
import type { CrossChainOrderPrepared, SingleChainOrderPrepared } from '../../types/intent.js';
import type { CrossChainOrder } from '../orders/cross-chain.js';
import type { ApiUserOrders } from '../../types/api.js';
import { fetchJWTToken, fetchSiweMessage } from '../../auth/siwe.js';
import { fetchUserOrders } from '../orders/api/fetch.js';
import { ChainID } from '../../chains.js';

/**
 * Sui-specific SDK implementation
 *
 * Handles Sui-specific aspects of cross-chain swaps using the Sui blockchain.
 * Uses @mysten/sui libraries for transaction creation, signing, and submission.
 * Supports cross-chain swaps from Sui to other supported chains.
 */
export class SuiSDK extends BaseSDK {
  /** Configuration for Sui connection and authentication */
  private readonly config: SuiConfig;
  /** Client for Sui RPC interactions and transaction handling */
  private client: SuiClient;
  private token?: string;

  /**
   * Creates a new instance of the Sui SDK
   *
   * @param config Sui configuration including privateKey and optional RPC URL
   */
  constructor(config: SuiConfig) {
    super();
    this.config = config;
    this.client = createSuiClient();
  }

  /**
   * Gets the user's Sui wallet address derived from their private key
   *
   * Uses Ed25519Keypair to securely derive the wallet address from the private key
   *
   * @returns Promise resolving to the user's Sui address as a hex string
   */
  public async getUserAddress(): Promise<string> {
    return this.getKeyPair().toSuiAddress();
  }

  /**
   * Creates an Ed25519Keypair from the user's private key
   *
   * The keypair is used for transaction signing and address derivation
   *
   * @returns Ed25519Keypair for cryptographic operations
   * @private
   */
  private getKeyPair(): Ed25519Keypair {
    return Ed25519Keypair.fromSecretKey(this.config.privateKey);
  }

  protected override prepareSingleChainOrder(): Promise<SingleChainOrderPrepared> {
    throw new Error('Single-chain orders are not supported on Sui');
  }

  public async authenticate(token?: string): Promise<string> {
    const wallet = await this.getUserAddress();

    const response = await fetchSiweMessage({
      chainId: ChainID.Sui,
      wallet,
    });

    const message = response.data!;

    const keypair = this.getKeyPair();

    const messageBytes = new TextEncoder().encode(message);

    const { signature } = await keypair.signPersonalMessage(messageBytes);

    const jwt = await fetchJWTToken(
      {
        message,
        signature,
      },
      token,
    );

    const newToken = jwt.data!;

    return newToken;
  }

  public setToken(token: string) {
    this.token = token;

    return this;
  }

  public override async getOrders(): Promise<ApiUserOrders> {
    if (!this.token) {
      throw new Error('No token provided');
    }

    const orders = await fetchUserOrders(this.token);

    return orders;
  }
  /**
   * Prepares a Sui order for submission
   *
   * This method:
   * 1. Gets the user's keypair from their private key
   * 2. Generates Sui-specific transaction for the order
   * 3. Signs and executes the transaction on the Sui blockchain
   * 4. Returns the prepared order with the transaction hash for tracking
   *
   * @param order The validated order to prepare
   * @returns Promise resolving to a prepared order with Sui-specific data
   * @protected
   */
  protected async prepareCrossChainOrder(order: CrossChainOrder): Promise<CrossChainOrderPrepared> {
    const signer = this.getKeyPair();

    const transaction = await getSuiOrderTransaction(order);

    const tx = await this.client.signAndExecuteTransaction({
      signer,
      transaction,
    });

    await this.client.waitForTransaction({ digest: tx.digest });

    return {
      order,
      preparedData: {
        transactionHash: tx.digest,
      },
    };
  }

  public async cancelCrossChainOrder(orderId: string): Promise<string> {
    const signer = this.getKeyPair();

    const transaction = await getSuiCancelCrossChainOrder(orderId);

    const sender = await this.getUserAddress();

    transaction.setSender(sender);

    const tx = await this.client.signAndExecuteTransaction({
      signer,
      transaction,
    });

    await this.client.waitForTransaction({ digest: tx.digest });

    return tx.digest;
  }
}
