// src/networks/solana/constants.ts
import { PublicKey, StakeProgram } from '@solana/web3.js';
import { SolChainID } from '@wireio/core';

import mainnetLiqsolCoreIDL from '../../assets/solana/mainnet/idl/liqsol_core.json';
import mainnetLiqSolTokenIDL from '../../assets/solana/mainnet/idl/liqsol_token.json';
import mainnetValidatorLeaderboardIDL from '../../assets/solana/mainnet/idl/validator_leaderboard.json';
import mainnetTransferHookIDL from '../../assets/solana/mainnet/idl/transfer_hook.json';

import devnetLiqsolCoreIDL from '../../assets/solana/devnet/idl/liqsol_core.json';
import devnetLiqSolTokenIDL from '../../assets/solana/devnet/idl/liqsol_token.json';
import devnetValidatorLeaderboardIDL from '../../assets/solana/devnet/idl/validator_leaderboard.json';
import devnetTransferHookIDL from '../../assets/solana/devnet/idl/transfer_hook.json';
import { SupportedSolChainID } from '../../types';

/**
 * Per-chain program IDs (NOT global anymore).
 */
export type SolanaProgramIds = {
    LIQSOL_CORE: PublicKey;
    LIQSOL_TOKEN: PublicKey;
    VALIDATOR_LEADERBOARD: PublicKey;
    TRANSFER_HOOK: PublicKey;
    ALT: PublicKey;
};

const MAINNET_PROGRAM_IDS: SolanaProgramIds = {
    LIQSOL_CORE: new PublicKey(mainnetLiqsolCoreIDL.address),
    LIQSOL_TOKEN: new PublicKey(mainnetLiqSolTokenIDL.address),
    VALIDATOR_LEADERBOARD: new PublicKey(mainnetValidatorLeaderboardIDL.address),
    TRANSFER_HOOK: new PublicKey(mainnetTransferHookIDL.address),
    ALT: new PublicKey("AQXTHwkdNBEiXeQuVA5uCoxvzgYUmudRxthQY4vWKCPS")
};

const DEVNET_PROGRAM_IDS: SolanaProgramIds = {
    LIQSOL_CORE: new PublicKey(devnetLiqsolCoreIDL.address),
    LIQSOL_TOKEN: new PublicKey(devnetLiqSolTokenIDL.address),
    VALIDATOR_LEADERBOARD: new PublicKey(devnetValidatorLeaderboardIDL.address),
    TRANSFER_HOOK: new PublicKey(devnetTransferHookIDL.address),
    ALT: new PublicKey("AZQmTbacBuAsJpncg2N2AwcZsLG1Sxeohbau9vjezmTz")
};

export const PROGRAM_IDS_BY_CHAIN: Record<SupportedSolChainID, SolanaProgramIds> = {
    [SolChainID.Mainnet]: MAINNET_PROGRAM_IDS,
    [SolChainID.Devnet]: DEVNET_PROGRAM_IDS,
};

export const getProgramIds = (chainId: SolChainID): SolanaProgramIds => {
    const ids = PROGRAM_IDS_BY_CHAIN[chainId];
    if (!ids) throw new Error(`Unsupported Solana chainId=${chainId}`);
    return ids;
};

/**
 * ---------------------------------------------------------------------------
 *  PDA SEEDS (must match on-chain programs)
 * ---------------------------------------------------------------------------
 */
export const PDA_SEEDS = {
    // GLOBAL CONFIG
    GLOBAL_CONFIG: 'global_config',

    // liqsol_core: deposit / stake controller
    DEPOSIT_AUTHORITY: 'deposit_authority',
    VAULT: 'vault',
    RESERVE_POOL: 'reserve_pool',
    STAKE_CONTROLLER_STATE: 'stake_controller',
    PAYOUT_STATE: 'payout_state',

    // liqsol_token: mint + bucket
    LIQSOL_MINT: 'liqsol_mint',
    LIQSOL_MINT_AUTHORITY: 'mint_authority',
    BUCKET_AUTHORITY: 'liqsol_bucket',

    // distribution program (embedded in liqsol_core)
    DISTRIBUTION_STATE: 'distribution_state',
    USER_RECORD: 'user_record',

    // pay-rate / price history
    PAY_RATE_HISTORY: 'pay_rate_history',
    PRICE_HISTORY: 'price_history',

    // validator leaderboard (state + records)
    LEADERBOARD_STATE: 'leaderboard_state',
    VALIDATOR_RECORD: 'validator',
    GLOBAL_STAKE_INFO: 'global_stake_info',

    // Outpost / Wire pretokens
    OUTPOST_GLOBAL_STATE: 'outpost_global_state',
    LIQSOL_POOL: 'liqsol_pool',
    OUTPOST_ACCOUNT: 'outpost_account',
    USER_PRETOKEN_RECORD: 'user_pretoken_record',
    TRANCHE_STATE: 'tranche_state',

    // BAR module (bonded actor roles)
    BAR_STATE_SEED: 'bar_state',
    BONDED_ACTOR_SEED: 'bonded_actor',
    BOND_LEVEL_SEED: 'bond_level',

    // SPL transfer-hook extra account metas
    EXTRA_ACCOUNT_METAS: 'extra-account-metas',

    // Withdraw module / receipts
    WITHDRAW_GLOBAL: 'global',
    STAKE_ALLOCATION_STATE: 'stake_allocation_state',
    STAKE_METRICS: 'stake_metrics',
    MAINTENANCE_LEDGER: 'maintenance_ledger',
    WITHDRAW_MINT_AUTHORITY: 'mint_authority',
    MINT_METADATA: 'mint_metadata',
    LIQ_RECEIPT_DATA: 'liq_receipt_data',
    WITHDRAW_MINT: 'mint',

    // New
    PRETOKEN_PURCHASE_HISTORY: 'pretoken_purchase_history',
} as const;

/**
 * Ephemeral stake account address seed helper
 * (this one really is global & chain-agnostic).
 */
export const deriveEphemeralStakeAddress = async (
    user: PublicKey,
    seed: number,
): Promise<PublicKey> => {
    const seedStr = `ephemeral_${seed}`;
    return PublicKey.createWithSeed(user, seedStr, StakeProgram.programId);
};

/**
 * Constant keys (Chainlink)
 */
export const CHAINLINK_FEED = new PublicKey(
    'CH31Xns5z3M1cTAbKW34jcxPPciazARpijcHj9rxtemt',
);
export const CHAINLINK_PROGRAM = new PublicKey(
    'HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny',
);

/**
 * ---------------------------------------------------------------------------
 *  ECONOMICS & MATH CONSTANTS
 * ---------------------------------------------------------------------------
 */
export const PAY_RATE_SCALE_FACTOR = BigInt(1_000_000_000_000); // 10^12
export const DEFAULT_AVERAGE_PAY_RATE = BigInt(191_780_821);
export const DEFAULT_PAY_RATE_LOOKBACK = 5;
export const EPHEMERAL_RENT_EXEMPTION = 2_282_880;

export const LAMPORTS_PER_SOL = 1_000_000_000;
export const INDEX_SCALE = BigInt(1_000_000_000_000); // 1e12

export const lamportsToSol = (lamports: number | bigint): number =>
    Number(lamports) / LAMPORTS_PER_SOL;

export const solToLamports = (sol: number): bigint =>
    BigInt(Math.round(sol * LAMPORTS_PER_SOL));