import { u64 } from "@saberhq/token-utils";
import { PublicKey } from "@solana/web3.js";

import type { SwapTokenInfo } from "../instructions/swap.js";
import type { Fees } from "./fees.js";
import { decodeFees } from "./fees.js";
import { StableSwapLayout } from "./layout.js";

export * from "./fees.js";
export * from "./layout.js";

/**
 * State of a StableSwap, read from the swap account.
 */
export interface StableSwapState {
  /**
   * Whether or not the swap is initialized.
   */
  isInitialized: boolean;

  /**
   * Whether or not the swap is paused.
   */
  isPaused: boolean;

  /**
   * Nonce used to generate the swap authority.
   */
  nonce: number;

  /**
   * Mint account for pool token
   */
  poolTokenMint: PublicKey;

  /**
   * Admin account
   */
  adminAccount: PublicKey;

  tokenA: SwapTokenInfo;
  tokenB: SwapTokenInfo;

  /**
   * Initial amplification coefficient (A)
   */
  initialAmpFactor: u64;

  /**
   * Target amplification coefficient (A)
   */
  targetAmpFactor: u64;

  /**
   * Ramp A start timestamp
   */
  startRampTimestamp: number;

  /**
   * Ramp A start timestamp
   */
  stopRampTimestamp: number;

  /**
   * When the future admin can no longer become the admin, if applicable.
   */
  futureAdminDeadline: number;

  /**
   * The next admin.
   */
  futureAdminAccount: PublicKey;

  /**
   * Fee schedule
   */
  fees: Fees;
}

/**
 * Decodes the Swap account.
 * @param data
 * @returns
 */
export const decodeSwap = (data: Buffer): StableSwapState => {
  const stableSwapData = StableSwapLayout.decode(data);
  if (!stableSwapData.isInitialized) {
    throw new Error(`Invalid token swap state`);
  }
  const adminAccount = new PublicKey(stableSwapData.adminAccount);
  const adminFeeAccountA = new PublicKey(stableSwapData.adminFeeAccountA);
  const adminFeeAccountB = new PublicKey(stableSwapData.adminFeeAccountB);
  const tokenAccountA = new PublicKey(stableSwapData.tokenAccountA);
  const tokenAccountB = new PublicKey(stableSwapData.tokenAccountB);
  const poolTokenMint = new PublicKey(stableSwapData.tokenPool);
  const mintA = new PublicKey(stableSwapData.mintA);
  const mintB = new PublicKey(stableSwapData.mintB);
  const initialAmpFactor = u64.fromBuffer(
    Buffer.from(stableSwapData.initialAmpFactor),
  );
  const targetAmpFactor = u64.fromBuffer(
    Buffer.from(stableSwapData.targetAmpFactor),
  );
  const startRampTimestamp = stableSwapData.startRampTs;
  const stopRampTimestamp = stableSwapData.stopRampTs;
  const fees = decodeFees(stableSwapData.fees);
  return {
    isInitialized: !!stableSwapData.isInitialized,
    isPaused: !!stableSwapData.isPaused,
    nonce: stableSwapData.nonce,
    futureAdminDeadline: stableSwapData.futureAdminDeadline,
    futureAdminAccount: new PublicKey(stableSwapData.futureAdminAccount),
    adminAccount,
    tokenA: {
      adminFeeAccount: adminFeeAccountA,
      reserve: tokenAccountA,
      mint: mintA,
    },
    tokenB: {
      adminFeeAccount: adminFeeAccountB,
      reserve: tokenAccountB,
      mint: mintB,
    },
    poolTokenMint,
    initialAmpFactor,
    targetAmpFactor,
    startRampTimestamp,
    stopRampTimestamp,
    fees,
  };
};
