import { PublicKey, TokenAmount, Transaction, VersionedTransaction } from "@solana/web3.js";
import { BN } from '@coral-xyz/anchor';

/**
 * ============================================================
 *  Common / Wallet Types
 * ============================================================
 */

export type SolanaTransaction = Transaction | VersionedTransaction;

export type WalletLike = {
    publicKey: PublicKey;
    signTransaction: <T extends SolanaTransaction>(tx: T) => Promise<T>;
    signAllTransactions?: <T extends SolanaTransaction>(txs: T[]) => Promise<T[]>;
};

export type ParsedAccountInfo = {
    extensions: Array<{ extension: string }>;
    isNative: boolean;
    mint: string;
    owner: string;
    state: string;
    tokenAmount: TokenAmount;
};

/**
 * ============================================================
 *  Wire Outpost Snapshot
 * ============================================================
 *
 * This is the high-level “view model” the SDK returns after
 * fetching all relevant on-chain accounts for a given user.
 */

export type OutpostWireStateSnapshot = {
    /**
     * Global Wire/outpost state.
     * May be null if the fetch fails.
     */
    globalState: GlobalState | null;

    /**
     * Per-user Outpost account (old “wire receipt”), or null if user
     * hasn’t interacted yet.
     */
    outpostAccount: OutpostAccount | null;

    /**
     * Index-based liqSOL distribution state, or null if not initialized.
     */
    distributionState: DistributionState | null;

    /**
     * Global tranche pricing/supply state (`trancheState`).
     * May be null if not initialized.
     */
    trancheState: TrancheState | null;

    /**
     * Per-user pretoken purchase record (`userPretokenRecord` in the IDL).
     * May be null if the user never purchased.
     */
    userPretokenRecord: UserPretokenRecord | null;

    // Balances
    /** liqSOL in the pool ATA (Token-2022 raw amount), or null on fetch failure */
    liqsolPoolBalance: BN | null;

    /** liqSOL in the user’s ATA (Token-2022 raw amount), or null on fetch failure */
    userLiqsolBalance: BN | null;
};

/**
 * ============================================================
 *  Core Enums / Aliases (from IDL variants)
 * ============================================================
 */

/**
 * `wireState` enum from IDL:
 * - preLaunch: pretokens phase, no refunds
 * - postLaunch: normal operation
 * - refund: refund mode active
 */
export type WireState = 'preLaunch' | 'postLaunch' | 'refund';

/**
 * `role` enum from IDL:
 * Yield operator, batch operator, underwriter, pool operator.
 */
export type Role =
    | 'yieldOperator'
    | 'batchOperator'
    | 'underwriter'
    | 'poolOperator';

/**
 * Validator reputation from IDL `validatorReputation`.
 */
export type ValidatorReputation =
    | 'trusted'
    | 'blacklisted'
    | 'underPerforming';

/**
 * Validator lifecycle state from IDL `validatorState`.
 */
export type ValidatorState =
    | 'warming'
    | 'notDelegated'
    | 'cooling'
    | 'warm'
    | 'readyToCool';

/**
 * ============================================================
 *  Distribution / Index Types
 * ============================================================
 */

/**
 * IDL: `distributionState`
 *
 * Index-based distribution for liqSOL earnings.
 * Note: this is separate from your local `UserRecord` type above.
 */
export type DistributionState = {
    /** liqSOL mint (Token-2022) */
    liqsolMint: PublicKey;

    /** Current index (scaled) */
    currentIndex: BN;

    /** Total shares across all distribution users */
    totalShares: BN;

    /** Last observed bucket balance (for incremental index updates) */
    lastBucketBalance: BN;

    /** PDA bumps and cached authorities for bucket/pool */
    bump: number;
    bucketBump: number;
    poolBump: number;
    bucketAuthority: PublicKey;
    poolAuthority: PublicKey;
};

/**
 * IDL: `userRecord`
 *
 * On-chain distribution user record; tracks shares only.
 */
export type DistributionUserRecord = {
    shares: BN;
    bump: number;
};

/**
 * Simple preview container for “how many pretokens do I get?”
 * Matches the calculateExpectedPretokens math from capital-staking.
 */
export interface SharesPreview {
    // Input
    liqsolAmountLamports: BN;
    solPriceUsd: BN;          // 1e8 scale
    pretokenPriceUsd: BN;     // 1e8 scale
    // Output
    estimatedPretokens: BN;   // 1e8 scale
}
/**
 * ============================================================
 *  Global / GlobalState (New IDL Layout)
 * ============================================================
 */

/**
 * IDL: `global`
 *
 * Withdraw operator state:
 * - NFT withdrawal receipts
 * - Encumbered funds for pending withdrawals
 */
export type GlobalAccount = {
    /** PDA bump */
    bump: number;

    /** Authority that can update serviceable epochs / receipts */
    authority: PublicKey;

    /** liqSOL mint used/burned when processing withdrawals */
    liqsolMint: PublicKey;

    /** Highest epoch which can currently be serviced/claimed */
    serviceableEpoch: BN;

    /** Total SOL encumbered (reserved for open withdrawal receipts) */
    totalEncumberedFunds: BN;

    /** Monotonic counter for generating unique receipt IDs */
    nextReceiptId: BN;
};

/**
 * IDL: `globalState`
 *
 * Wire pretokens + outpost configuration:
 * - Admin / deployment timing
 * - Refund delay
 * - Total staked/purchased liqSOL via outpost
 * - Global share accounting
 * - Protocol shares
 * - Role bonding requirements and warmup
 * - `wireState` lifecycle (preLaunch/postLaunch/refund)
 */
export type GlobalState = {
    /** Deployment timestamp (Unix, seconds) */
    deployedAt: BN;

    /** Delay before refunds are permitted (seconds) */
    refundDelaySeconds: BN;

    /** Global pause flag (if true, most ops are disabled) */
    paused: boolean;

    /** Total liqSOL staked via outpost (principal) */
    totalStakedLiqsol: BN;

    /** Total liqSOL used to purchase pretokens */
    totalPurchasedLiqsol: BN;

    /** Total shares issued to all users (staking + purchases) */
    totalShares: BN;

    /** Total shares allocated to protocol */
    protocolShares: BN;

    /**
     * Current share-to-liqSOL index
     * (scaled by a fixed INDEX_SCALE constant from the program).
     */
    currentIndex: BN;

    /**
     * Replaced legacy `lastPoolLiqsolBalance`
     * with expected balance based on total shares and current index.
     */
    expectedPoolBalance: BN;

    /**
     * Accumulated liqSOL yield available to the protocol
     * (used for `purchaseFromYield`-style flows).
     */
    yieldAccumulatedLiqsol: BN;

    /**
     * Role principals in liqSOL; index by Role:
     * [YieldOperator, BatchOperator, Underwriter, PoolOperator]
     */
    rolePrincipals: BN[];

    /**
     * Global warmup duration in seconds for newly bonded roles.
     * Before this time passes, roles are not considered active.
     */
    roleWarmupDuration: BN;

    /** Wire lifecycle state (preLaunch / postLaunch / refund) */
    wireState: WireState | any;

    /** PDA bump */
    bump: number;
};


/**
 * IDL: `globalConfig`
 *
 * Zero-copy global config PDA.
 * Authority is taken from StakeControllerState, not stored here.
 */
export type GlobalConfig = {
    /** PDA bump */
    bump: number;

    /** 7-byte padding (unused) */
    padding: number[]; // u8[7]

    /** Minimum SOL amount a user can deposit (lamports, u64) */
    minUserDeposit: BN;

    /** Minimum SOL amount for an unstake/withdrawal request (lamports, u64) */
    minUnstakeRequest: BN;

    /** Minimum stake delta to trigger a stake rebalance order (lamports, u64) */
    minRebalanceStakeDelta: BN;

    /** Minimum unstake delta to trigger an unstake rebalance order (lamports, u64) */
    minRebalanceUnstakeDelta: BN;

    /** Minimum transient stake to include in effective stake calculations (lamports, u64) */
    transientThreshold: BN;

    /**
     * Minimum slots that must have elapsed in the epoch before late epoch operations can execute (u64)
     */
    minLateEpochSlotGate: BN;

    /** Reserved u64[2] */
    reservedU64: BN[];

    /**
     * Epochs a validator must wait in the graveyard before it is booted.
     * This begins after the last recorded state change (u16)
     */
    cooldownEpochs: number;

    /**
     * Multiplier for deposit fee calculation (u16).
     * Typically: avg_pay_rate * expected_warmup_epochs
     */
    depositFeeMultiplier: number;

    /**
     * Minimum VPP score required to enter the active validator set,
     * fallback when the validator set is very small (u16)
     */
    minVppEntry: number;

    /**
     * VPP score threshold below which a validator is removed from active set,
     * fallback threshold (u16)
     */
    minVppExit: number;

    /**
     * Max validators for "tiny" network band (uses fixed VPP thresholds) (u16)
     */
    tinyNetworkThreshold: number;

    /**
     * Max validators for "small" network band (uses percentile-based selection) (u16)
     */
    smallNetworkThreshold: number;

    /**
     * Max validators for "medium" network band (uses percentile-based selection) (u16)
     */
    mediumNetworkThreshold: number;

    /**
     * Fixed rank threshold to enter active set in large networks (0-indexed, u16)
     */
    largeNetworkEntryRank: number;

    /**
     * Fixed rank threshold to exit active set in large networks (0-indexed, u16)
     */
    largeNetworkExitRank: number;

    /** Reserved u16[3] */
    reservedU16: number[];

    /**
     * Percentile rank required to enter active set in small networks (u8)
     */
    smallNetworkEntryPercent: number;

    /**
     * Percentile rank below which validators exit in small networks (u8)
     */
    smallNetworkExitPercent: number;

    /**
     * Percentile rank required to enter active set in medium networks (u8)
     */
    mediumNetworkEntryPercent: number;

    /**
     * Percentile rank below which validators exit in medium networks (u8)
     */
    mediumNetworkExitPercent: number;

    /** Reserved u8[2] */
    reservedU8: number[];

    /**
     * Feature flags (u16 bitfield):
     *  Bit 0: DepositsEnabled
     *  Bit 1: WithdrawalsEnabled
     *  Bit 2: ClaimWithdrawalsEnabled
     *  Bit 3: ProcessStakeOrdersEnabled
     *  Bit 4: ProcessUnstakeOrdersEnabled
     *  Bit 5: ProcessPayCycleEnabled
     *  Bit 6: RebalancingEnabled
     *  Bits 7–15: Reserved
     */
    featureFlags: number;

    /** Reserved flags (u16[1]) */
    reservedFlags: number[];
};

/**
 * ============================================================
 *  Outpost / Pretoken Accounts
 * ============================================================
 */

/**
 * IDL: `outpostAccount`
 *
 * Per-user Wire Outpost state:
 * - Staked liqSOL + shares
 * - Purchased liqSOL + shares
 * - Role bonding principals and bitmap flags
 * - Warmup timestamp
 */
export type OutpostAccount = {
    /** User owner of this outpost account */
    user: PublicKey;

    /** Principal liqSOL staked via outpost */
    stakedLiqsol: BN;

    /** Shares from staking (index-based accounting) */
    stakedShares: BN;

    /** Principal liqSOL used for purchases */
    purchasedLiqsol: BN;

    /** Shares from liqSOL purchases */
    purchasedShares: BN;

    /**
     * LiqSOL principals locked by roles:
     * [YieldOperator, BatchOperator, Underwriter, PoolOperator]
     */
    bondedPrincipals: BN[];

    /**
     * Bitmap of bonded roles (bits 0–3 correspond to Role enum order).
     */
    bondedRoles: number;

    /**
     * Bitmap of roles that have requested unbonding (bits 0–3).
     */
    unbondRequested: number;

    /**
     * Warmup end time; roles are not active until this timestamp.
     */
    warmupEndsAt: BN;

    /** PDA bump */
    bump: number;
};

/**
 * Backwards-compat: old name `WireReceipt` used in SDK now maps
 * directly to the new `OutpostAccount` layout.
 */
export type WireReceipt = OutpostAccount;

/**
 * IDL: `userPretokenRecord`
 *
 * Per-user pretoken purchase history:
 * - Total SOL deposited
 * - Total pretokens purchased (8-decimal i128)
 * - Last tranche number & price used
 */
export type UserPretokenRecord = {
    user: PublicKey;

    /** Total SOL deposited for pretokens (unscaled u64) */
    totalSolDeposited: BN;

    /** Total pretokens purchased (8-decimal scaled i128) */
    totalPretokensPurchased: BN;

    /** Last tranche number used for a purchase */
    lastTrancheNumber: BN;

    /** Last tranche price in USD (8-decimal scaled i128) */
    lastTranchePriceUsd: BN;

    bump: number;
};

/**
 * IDL: `trancheState`
 *
 * Global tranche configuration and pricing.
 * All price/supply fields use 8-decimal precision (SCALE = 1e8).
 */
export type TrancheState = {
    /** Current tranche number */
    currentTrancheNumber: BN;

    /** Remaining supply in the current tranche (8-decimal i128) */
    currentTrancheSupply: BN;

    /** Current tranche price in USD (8-decimal i128) */
    currentTranchePriceUsd: BN;

    /** Total pretokens sold across all tranches (8-decimal i128) */
    totalPretokensSold: BN;

    /** Initial per-tranche supply (8-decimal i128) */
    initialTrancheSupply: BN;

    /** Supply growth in basis points per tranche */
    supplyGrowthBps: number;

    /** Price growth in cents per tranche (0.01 USD units) */
    priceGrowthCents: number;

    /** Minimum valid SOL/USD price for Chainlink validation (8-decimal i128) */
    minPriceUsd: BN;

    /** Maximum valid SOL/USD price for Chainlink validation (8-decimal i128) */
    maxPriceUsd: BN;

    /** Maximum staleness in seconds for Chainlink price data */
    maxStalenessSeconds: BN;

    /** Chainlink program address */
    chainlinkProgram: PublicKey;

    /** Chainlink SOL/USD feed address */
    chainlinkFeed: PublicKey;

    /** PDA bump */
    bump: number;
};

/**
 * ============================================================
 *  Pay Rate / Price History
 * ============================================================
 */

/**
 * IDL: `payRateEntry`
 */
export type PayRateEntry = {
    /** Unix timestamp for this rate sample */
    timestamp: BN;

    /** Scaled pay rate (u64; program defines scale) */
    scaledRate: BN;
};

/**
 * IDL: `payRateHistory`
 *
 * Circular buffer of recent pay rates.
 */
export type PayRateHistory = {
    /** Current index in the circular buffer */
    currentIndex: number;

    /** Total entries ever added (monotonic counter) */
    totalEntriesAdded: BN;

    /** Buffer of entries (fixed-capacity on-chain) */
    entries: PayRateEntry[];

    /** Maximum entries the buffer can hold */
    maxEntries: number;

    /** PDA bump */
    bump: number;
};

/**
 * IDL: `priceHistory`
 *
 * Circular buffer of recent SOL/USD prices, 8-decimal precision.
 */
export type PriceHistory = {
    /** Window size used for moving average, etc. */
    windowSize: number;

    /** Recent prices (8-decimal scaled i128) */
    prices: BN[];

    /** Number of valid entries in the buffer */
    count: number;

    /** Index to write the next price into */
    nextIndex: number;

    /** PDA bump */
    bump: number;
};

/**
 * ============================================================
 *  Leaderboard Types
 * ============================================================
 */

/**
 * On-chain leaderboard snapshot.
 *
 * IDL account: `leaderboardState`
 *
 * - `scores[i]` is the numeric score for validator with registry index `i`
 * - `sortedIndices` is a permutation of [0..N) giving indices from
 *    highest score to lowest.
 * - `voteAccounts[i]` is the vote account pubkey corresponding to
 *    validator registry index `i`.
 */
export type LeaderboardState = {
    /**
     * Per-validator scores (i64 on-chain).
     * Index corresponds to `registryIndex` in ValidatorRecord.
     */
    scores: number[];

    /**
     * Sorted indices into `scores[]` / `voteAccounts[]`,
     * typically highest score first.
     */
    sortedIndices: number[]; // u32 on-chain

    /**
     * Vote accounts for validators. Parallel array to `scores`.
     * `voteAccounts[i]` is the vote account for validator with
     * `registryIndex == i`.
     */
    voteAccounts: { bytes: number[]; }[];

    /**
     * Number of active validators currently tracked in the leaderboard.
     * Only the first `numValidators` entries of the arrays are meaningful.
     */
    numValidators: number; // u32 on-chain

    /** PDA bump seed for leaderboardState. */
    bump: number; // u8 on-chain

    /**
     * Reserved padding / future-proofing on-chain (u64[8]).
     * Not used by client logic, but surfaced for completeness.
     */
    padding: number[];
};

/**
 * Per-validator record tying Solana vote account to
 * Wire staking / MEV metadata.
 *
 * IDL account: `validatorRecord`
 */
export type ValidatorRecord = {
    /**
     * The validator's Solana vote account.
     */
    voteAccount: PublicKey;

    /**
     * Index into the global validator registry / leaderboard arrays.
     * This is the index used for `scores[]`, `voteAccounts[]`, etc.
     */
    registryIndex: number;

    /**
     * Last observed vote credits for this validator,
     * used for rewards / performance accounting.
     */
    creditsObserved: BN;

    /**
     * Historical commission values over time (e.g., per epoch).
     * Exact semantics depend on on-chain implementation, but this is
     * typically a ring buffer of u8/u16 basis-point values.
     */
    commissionHistory: number[];

    /**
     * Current index into `commissionHistory` ring buffer.
     */
    commissionIndex: number;

    /**
     * Last time (unix timestamp or slot-aligned time) the base
     * commission was updated.
     */
    lastCommissionUpdate: BN;

    /**
     * Last epoch for which this record was updated.
     */
    epoch: BN;

    /**
     * Last observed slot for this validator record.
     */
    slot: BN;

    /**
     * Whether this validator is operated by a Wire-approved operator.
     * Used for preferential routing / UX labeling.
     */
    wireOperator: boolean;

    /**
     * Current MEV commission (e.g., bps or percentage).
     */
    mevCommission: number;

    /**
     * Historical MEV commission values over time.
     */
    mevCommissionHistory: number[];

    /**
     * Current index into `mevCommissionHistory` ring buffer.
     */
    mevCommissionIndex: number;

    /**
     * Last time the MEV commission was updated.
     */
    lastMevCommissionUpdate: BN;

    /**
     * PDA bump seed for validatorRecord.
     */
    bump: number;
};

/**
 * IDL: `receiptData`
 *
 * Withdrawal receipt data structure tracking pending withdrawals.
 * Each receipt represents a user's request to withdraw liqSOL,
 * with the withdrawal contingent on sufficient encumbered funds
 * being available at the serviceable epoch.
 */
export type ReceiptData = {
    /** Unique receipt identifier (monotonically increasing, u64) */
    receiptId: bigint;

    /** Amount of liqSOL requested for withdrawal (Token-2022 raw amount, u64) */
    liqports: bigint;

    /** Epoch at which this receipt becomes claimable (u64) */
    epoch: bigint;

    /** Flag indicating whether this receipt has been fulfilled/claimed (bool) */
    fulfilled: boolean;
};
