import { BaseSignerWalletAdapter } from '@solana/wallet-adapter-base';
import { PublicKey as SolPubKey } from '@solana/web3.js';
import { ChainID, EvmChainID, ExternalNetwork, PublicKey, SolChainID } from '@wireio/core';
import { ethers } from 'ethers';

export type StakerConfig = {
    network: ExternalNetwork;
    provider?: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
    pubKey?: PublicKey;
    extras?: {
        squadsX?: SquadsXConfig;
    }
}

export type SquadsXConfig = {
    multisigPDA: string;        // REQUIRED, base58 multisig address string
    vaultIndex?: number;        // default 0
};

export enum SupportedEvmChainID {
    Ethereum = EvmChainID.Ethereum,
    Hoodi = EvmChainID.Hoodi,
}
export enum SupportedSolChainID {
    Mainnet = SolChainID.Mainnet,
    Devnet = SolChainID.Devnet,
}

export type ChainSymbol = 'ETH' | 'SOL';
export type WithdrawStatus = 'queued' | 'ready' | 'claimed';

export interface IStakingClient {
    pubKey?: PublicKey;
    network: ExternalNetwork;

    /** Amount is in the chain's smallest unit (lamports/wei, etc.) */
    deposit(amount: bigint): Promise<string>;
    withdraw(amount: bigint): Promise<string>;
    stake(amount: bigint): Promise<string>;
    unstake(amount: bigint): Promise<string>;
    buy(amount: bigint): Promise<string>;
    
    /** Claim a withdrawal receipt (burn NFT + receive ETH/SOL) via claim_withdraw. */
    claimWithdraw(tokenId: bigint): Promise<string>

    /** Solana only: claim accrued liqSOL distribution rewards. */
    // claimLiqsolRewards?(): Promise<string>

    /** Atomically deposit native token to liq token, then stake that liq token. On Solana: amount in lamports. On Ethereum: amount in wei. */
    depositAndStake(amount: bigint): Promise<string>

    /** Atomically deposit native token to liq token, then buy pretokens with that liq token. On Solana: amount in lamports. On Ethereum: amount in wei. */
    depositAndBuy(amount: bigint): Promise<string>

    /** Atomically unstake liq token, then request withdrawal. On Solana: amount in lamports. On Ethereum: tokenId (ReceiptNFT ID). */
    unstakeAndWithdraw(amount: bigint): Promise<string>
    
    /** Enumerate withdrawal receipt NFTs held by the user (queued/ready/claimed). */
    getPendingWithdraws(address?: string): Promise<WithdrawReceipt[]>

    /** Fetch the complete user portfolio */
    getPortfolio(address?: string): Promise<Portfolio | null>;

    // Estimated total APY for staking yeild
    getSystemAPY(): Promise<number>;

    /**
     * Program-level prelaunch WIRE/tranche snapshot for this chain.
     *
     * Returns:
     *  - `TrancheSnapshot` when the chain supports pretoken/tranches
     *  - `null` if this chain has no WIRE/pretoken integration
     */
    getTrancheSnapshot(chainId: ChainID): Promise<TrancheSnapshot | null>;

    /**
     * Protocol fee charged for deposit from Native to LIQ
     * in Solana: amount in lamports
     * in Ethereum: amount in wei
     */
    getDepositFee(amount: bigint): Promise<bigint>;

    /**
     * Estimate a conservative ETH(wei) / SOL(lamports) buffer to leave in the wallet
     * so the user can pay fees for the current deposit and at least one
     * more transaction, plus a bit extra for future interactions.
    */
    getDepositBuffer(options?: {
        txCount?: number;                // how many txs to cover in gas buffer (default 2)
        safetyMultiplier?: number;       // safety multiplier on per-tx fee (default 3x)
        minBufferLamports?: bigint;      // minimum gas buffer (default ~0.01 SOL)
        balanceOverrideLamports?: bigint; // for tests/custom callers
    }): Promise<bigint>

    // Removed : ValidatorDeposit not required for Solana
    // validatorDeposit(): Promise<string>
}

/**
 * Cross-chain portfolio view for a single account/wallet.
 *
 * On Solana (liqSOL + Outpost + pretokens) we fill:
 *  - native: SOL wallet lamports
 *  - liq:    liqSOL in user ATA
 *  - staked: liqSOL staked into Outpost (stakedLiqsol)
 *  - wire:   WIRE pretokens purchased (1e8 scale)
 *  - yield:  Outpost yield view (index/shares + implied claim/yield)
 *
 * On other chains:
 *  - native/liq/wire/staked can still be populated as appropriate
 *  - yield may be undefined if no index/shares concept exists.
 */
export interface Portfolio {
    /** Native balance on chain: ETH, SOL, etc. */
    native: BalanceView;

    /** Liquid staking token balance (LiqETH, LiqSOL, etc.). */
    liq: BalanceView;

    /**
     * Solana-only liqSOL rewards currently claimable from the distribution bucket.
     * Not populated on Ethereum.
     */
    claimable?: BalanceView;

    /**
     * Outpost-staked balance:
     *  - On Solana: liqSOL staked via `synd` (stakedLiqsol, in lamports).
     *  - On other chains: protocol-specific staked principal, if any.
     */
    staked: BalanceView;

    /**
     * Prelaunch WIRE “shares” (pretokens).
     *  - On Solana: totalPretokensPurchased (1e8 scale).
     *  - On other chains: analogous pretoken balance if supported.
     */
    wire: BalanceView;

    /**
     * Yield / index-based view (SOL + Outpost only for now).
     *
     * If the connected chain doesn’t expose an index+shares model,
     * this will be undefined.
     */
    yield?: YieldView;

    /**
     * Extra PDAs, account addresses, and raw state useful for debugging
     * and advanced UI (e.g. tranche price, global index, PDAs).
     */
    extras?: Record<string, any>;

    /** Chain ID of the network from which this portfolio was fetched. */
    chainID: ChainID;
}

/**
 * Canonical view of a single balance on a given chain.
 *
 * amount   – integer base units (wei, lamports, token base units)
 * symbol   – display symbol (“SOL”, “LiqSOL”, “$WIRE”, etc.)
 * decimals – how many decimal places to use when formatting
 * ata      – optional SPL ATA / ERC-20 address (where applicable)
 */
export interface BalanceView {
    amount: bigint;
    symbol: string;
    decimals: number;
    ata?: SolPubKey;
}

/**
 * Outpost yield view.
 *
 * All amounts are integers in base units (lamports for liqSOL, wei for liqETH).
 *
 * Math matches capital-staking:
 *   INDEX_SCALE      = 1e12
 *   sharesToTokens = shares * currentIndex / INDEX_SCALE
 */
export interface YieldView {
    /**
     * Current global index for the Outpost pool.
     *  - 1e12 scale (INDEX_SCALE)
     *  - sharesToTokens(shares) = shares * currentIndex / indexScale
     */
    currentIndex: bigint;

    /**
     * Fixed scale factor used for index math.
     *  - Always 1_000_000_000_000n (1e12) on Solana Outpost.
     */
    indexScale: bigint;

    /** Total shares outstanding in the Outpost pool (all users). */
    totalShares: bigint;

    /** User’s staked shares in the Outpost pool. */
    userShares: bigint;

    /**
     * Total liq (wei/lamports) the user could claim right now if they fully
     * unwound their stake:
     *   estimatedClaim = userShares * currentIndex / indexScale
     */
    estimatedClaim?: bigint;

    /**
     * Portion of estimatedClaim that is “yield” above principal:
     *   estimatedYield = max(0, estimatedClaim - staked)
     *
     * NOTE: staked principal itself is surfaced separately as
     *       Portfolio.staked.amount.
     */
    estimatedYield?: bigint;
}

export interface TrancheLadderItem {
    /** On-chain tranche id, 0-based (0,1,2,...) */
    id: number;
    /** Total capacity for this tranche (pretokens, 1e8 scale) */
    capacity: bigint;
    /** Sold amount in this tranche (1e8 scale) */
    sold: bigint;
    /** Remaining = capacity - sold (1e8 scale) */
    remaining: bigint;
    /** Price for this tranche in USD (1e8 scale) */
    priceUsd: bigint;
}

export interface TrancheLadderItem {
    /** On-chain tranche id, 0-based (0,1,2,...) */
    id: number;
    /** Total capacity for this tranche (pretokens, 1e8 scale) */
    capacity: bigint;
    /** Sold amount in this tranche (1e8 scale) */
    sold: bigint;
    /** Remaining = capacity - sold (1e8 scale) */
    remaining: bigint;
    /** Price for this tranche in USD (1e8 scale) */
    priceUsd: bigint;
}

/**
 * Unified pretoken/tranche snapshot for any chain.
 * ETH / other chains just fill the same shape from their own contracts.
 */
export interface TrancheSnapshot {
    chainID: ChainID;

    /** Global share index (1e12 on Sol today; other chains can use their own scale) */
    currentIndex: bigint;
    /** Total accounting shares (wire pretoken “shares”) */
    totalShares: bigint;

    /** Current tranche id as stored on chain (0-based) */
    currentTranche: number;

    /** Current tranche price in USD (1e8 scale) */
    currentPriceUsd: bigint;

    supplyGrowthBps: number; // 2.5% growth per tranche

    priceGrowthCents: number;  // $0.02 USD growth per tranche

    totalPretokensSold: bigint;  // total pretokens sold across all tranches (1e8 scale)

    /** Current tranche supply state (1e8 scale) */
    currentTrancheSupply: bigint;   // remaining in current tranche
    initialTrancheSupply: bigint;   // capacity for current tranche

    /** Native token → USD price if available (SOL/USD, ETH/USD, etc, 1e8 scale) */
    nativePriceUsd?: bigint;
    /** Optional timestamp (sec) for the last recorded native price */
    nativePriceTimestamp?: number;

    /**
     * Local window of tranche “rows” centered around current.
     * Used directly by the frontend for ladder graphs.
     */
    ladder: TrancheLadderItem[];
}


// Enum describing which asset is being used to buy pretoken
export enum PurchaseAsset {
    SOL = 'SOL',
    LIQSOL = 'LIQSOL',
    ETH = 'ETH',
    LIQETH = 'LIQETH',
    YIELD = 'YIELD',
}

export interface PurchaseQuote {
    purchaseAsset: PurchaseAsset;
    amountIn: bigint;       // lamports / wei / token units

    /** Expected pretoken “shares” (pretokens) and decimals */
    wireShares: bigint;     // 1e8 scale
    wireDecimals: number;   // always 8 for now

    /** Current price + notional in USD (1e8 scale) */
    wirePriceUsd: bigint;
    notionalUsd: bigint;
}

// export interface OPPEvent {
//     type: 'liq_pretoken_purchase' | 'yield_pretoken_purchase' | 'pretoken_purchase' | 'stake' | 'unstake' | 'unknown';
//     amount: bigint | null;
//     chain: 'ETH' | 'SOL';
//     timestamp: number | null;
//     from: string | null;
//     to: string | null;
//     txHash: string;
//     raw: any;
//     assertions: OPPAssertion[]
// }

export interface OPPAssertion {
    type: 'liq_pretoken_purchase' | 'yield_pretoken_purchase' | 'pretoken_purchase' | 'stake' | 'unstake' | 'bonded_actor' | 'unbonded_actor' | 'unknown',
    data: any
    chain: 'ETH' | 'SOL';
    timestamp: number | null;
    from: string | null;
    to: string | null;
    txHash: string;
    raw: any;
}

export enum ReceiptNFTKind {
    STAKE = 0,
    PRETOKEN_PURCHASE = 1,
}

/**
 * Unified cross-chain withdraw receipt (ETH + SOL).
 *
 * tokenId   – NFT id (ERC721 on ETH, PDA-seeded mint on SOL)
 * receipt   – chain-specific data normalized for UI
 */
export interface WithdrawReceipt {
    tokenId: bigint;
    receipt: {
        /** Display balance (wei or lamports) with symbol/decimals. */
        amount: BalanceView;
        /** Claimable time in ms since epoch (readyAt on ETH; ETA from epoch on SOL). */
        readyAt: number;
        /** Chain discriminator. */
        chain: ChainSymbol;
        /** Solana-only: epoch when claimable. */
        epoch?: bigint;
        /** queued | ready | claimed */
        status?: WithdrawStatus;
        /** NFT mint (SOL) or ERC721 contract address (ETH queue address). */
        mint?: string;
        /** Owner token account (SOL) for the NFT. */
        ownerAta?: string;
        /** Optional explicit contract address for ETH queue NFT. */
        contractAddress?: string;
    };
}
