//! Perpetual Futures Trading Contract
//!
//! This contract implements perpetual futures trading with:
//! - Long and Short positions
//! - Leverage from 2x to 100x
//! - Funding rate mechanism (keeps price anchored to spot)
//! - Mark price for liquidations (manipulation resistant)
//! - Index price from oracles
//! - Liquidation engine with insurance fund
//! - Cross-margin and isolated margin modes
//!
//! # Architecture (Similar to dYdX/GMX)
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────┐
//! │                    PERPETUAL FUTURES                        │
//! ├─────────────────────────────────────────────────────────────┤
//! │  PRICE FEEDS:                                               │
//! │  ┌─────────┐     ┌───────────┐     ┌─────────────┐         │
//! │  │ Oracle  │────►│Index Price│────►│ Mark Price  │         │
//! │  │ (Pyth)  │     │ (Spot)    │     │ (EMA-based) │         │
//! │  └─────────┘     └───────────┘     └─────────────┘         │
//! │                                           │                 │
//! │  POSITIONS:                               ▼                 │
//! │  ┌─────────────────────────────────────────────────────┐   │
//! │  │ Position { size, collateral, entry_price, leverage }│   │
//! │  │ PnL = size * (mark_price - entry_price)             │   │
//! │  │ Liquidation when: margin_ratio < maintenance_margin │   │
//! │  └─────────────────────────────────────────────────────┘   │
//! │                                                             │
//! │  FUNDING:                                                   │
//! │  ┌─────────────────────────────────────────────────────┐   │
//! │  │ funding_rate = (mark_price - index_price) / 8h      │   │
//! │  │ if mark > index: longs pay shorts                   │   │
//! │  │ if mark < index: shorts pay longs                   │   │
//! │  └─────────────────────────────────────────────────────┘   │
//! └─────────────────────────────────────────────────────────────┘
//! ```

#![no_std]

extern crate alloc;

use alloc::string::String;
use alloc::vec::Vec;
use borsh::{BorshDeserialize, BorshSerialize};
use synor_sdk::prelude::*;
use synor_sdk::{require, require_auth};

// =============================================================================
// CONSTANTS
// =============================================================================

/// Minimum leverage (2x)
pub const MIN_LEVERAGE: u64 = 2;

/// Maximum leverage (100x)
pub const MAX_LEVERAGE: u64 = 100;

/// Basis points denominator (10000 = 100%)
pub const BPS_DENOMINATOR: u64 = 10000;

/// Initial margin requirement (1% at 100x leverage = 100 bps)
pub const INITIAL_MARGIN_BPS: u64 = 100;

/// Maintenance margin (0.5% = 50 bps) - below this, liquidation
pub const MAINTENANCE_MARGIN_BPS: u64 = 50;

/// Liquidation fee (5% = 500 bps) - goes to liquidator + insurance
pub const LIQUIDATION_FEE_BPS: u64 = 500;

/// Insurance fund share of liquidation fee (50%)
pub const INSURANCE_SHARE_BPS: u64 = 5000;

/// Maximum funding rate (0.1% per 8h = 10 bps)
pub const MAX_FUNDING_RATE_BPS: u64 = 10;

/// Funding interval (8 hours in seconds)
pub const FUNDING_INTERVAL: u64 = 8 * 60 * 60;

/// Price precision (6 decimals, like USDC)
pub const PRICE_PRECISION: u64 = 1_000_000;

/// Size precision (8 decimals)
pub const SIZE_PRECISION: u64 = 100_000_000;

// =============================================================================
// EVENT TOPICS
// =============================================================================

fn event_topic(name: &[u8]) -> [u8; 32] {
    use synor_sdk::crypto::blake3_hash;
    blake3_hash(name).0
}

// =============================================================================
// STORAGE KEYS
// =============================================================================

mod keys {
    pub const OWNER: &[u8] = b"perps:owner";
    pub const INITIALIZED: &[u8] = b"perps:initialized";
    pub const PAUSED: &[u8] = b"perps:paused";

    // Market configuration
    pub const MARKETS: &[u8] = b"perps:markets";
    pub const MARKET_COUNT: &[u8] = b"perps:market_count";

    // Positions
    pub const POSITIONS: &[u8] = b"perps:positions";
    pub const POSITION_COUNT: &[u8] = b"perps:position_count";
    pub const USER_POSITIONS: &[u8] = b"perps:user_positions";

    // Funding
    pub const LAST_FUNDING_TIME: &[u8] = b"perps:last_funding";
    pub const CUMULATIVE_FUNDING: &[u8] = b"perps:cum_funding";

    // Insurance fund
    pub const INSURANCE_FUND: &[u8] = b"perps:insurance";

    // Oracle
    pub const ORACLE: &[u8] = b"perps:oracle";
    pub const PRICES: &[u8] = b"perps:prices";
}

// =============================================================================
// DATA STRUCTURES
// =============================================================================

/// Position direction
#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum Direction {
    Long,
    Short,
}

/// Margin mode
#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum MarginMode {
    /// Each position has isolated margin
    Isolated,
    /// All positions share margin
    Cross,
}

/// Market status
#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum MarketStatus {
    Active,
    Paused,
    SettleOnly,
    Delisted,
}

/// Market configuration
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct Market {
    /// Market ID
    pub id: u32,
    /// Base asset symbol (e.g., "BTC")
    pub base_asset: String,
    /// Quote asset symbol (e.g., "USD")
    pub quote_asset: String,
    /// Market status
    pub status: MarketStatus,
    /// Maximum leverage allowed
    pub max_leverage: u64,
    /// Maintenance margin requirement (bps)
    pub maintenance_margin_bps: u64,
    /// Taker fee (bps)
    pub taker_fee_bps: u64,
    /// Maker fee (bps, can be negative for rebates)
    pub maker_fee_bps: i64,
    /// Minimum position size
    pub min_size: u64,
    /// Maximum position size per user
    pub max_size: u64,
    /// Total open interest (long)
    pub open_interest_long: u64,
    /// Total open interest (short)
    pub open_interest_short: u64,
    /// Maximum open interest
    pub max_open_interest: u64,
}

/// A trading position
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct Position {
    /// Position ID
    pub id: u64,
    /// Owner address
    pub owner: Address,
    /// Market ID
    pub market_id: u32,
    /// Position direction
    pub direction: Direction,
    /// Position size (in SIZE_PRECISION)
    pub size: u64,
    /// Collateral amount (in PRICE_PRECISION)
    pub collateral: u64,
    /// Entry price (in PRICE_PRECISION)
    pub entry_price: u64,
    /// Leverage (2x to 100x)
    pub leverage: u64,
    /// Margin mode
    pub margin_mode: MarginMode,
    /// Cumulative funding at entry
    pub entry_funding: i64,
    /// Timestamp when opened
    pub opened_at: u64,
    /// Last update timestamp
    pub updated_at: u64,
    /// Whether position is active
    pub is_open: bool,
}

/// Price data from oracle
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct PriceData {
    /// Index price (spot price from oracles)
    pub index_price: u64,
    /// Mark price (EMA-smoothed, used for liquidations)
    pub mark_price: u64,
    /// Last update timestamp
    pub timestamp: u64,
    /// Confidence interval
    pub confidence: u64,
}

/// Funding rate data
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct FundingData {
    /// Current funding rate (can be negative, in bps)
    pub rate_bps: i64,
    /// Cumulative funding for longs
    pub cumulative_long: i64,
    /// Cumulative funding for shorts
    pub cumulative_short: i64,
    /// Last funding timestamp
    pub last_update: u64,
}

// =============================================================================
// EVENTS
// =============================================================================

#[derive(BorshSerialize)]
pub struct MarketCreated {
    pub market_id: u32,
    pub base_asset: String,
    pub quote_asset: String,
    pub max_leverage: u64,
}

#[derive(BorshSerialize)]
pub struct PositionOpened {
    pub position_id: u64,
    pub owner: Address,
    pub market_id: u32,
    pub direction: Direction,
    pub size: u64,
    pub collateral: u64,
    pub entry_price: u64,
    pub leverage: u64,
}

#[derive(BorshSerialize)]
pub struct PositionClosed {
    pub position_id: u64,
    pub owner: Address,
    pub exit_price: u64,
    pub realized_pnl: i64,
    pub fee_paid: u64,
}

#[derive(BorshSerialize)]
pub struct PositionLiquidated {
    pub position_id: u64,
    pub owner: Address,
    pub liquidator: Address,
    pub size: u64,
    pub collateral_seized: u64,
    pub liquidation_price: u64,
    pub insurance_fund_share: u64,
    pub liquidator_reward: u64,
}

#[derive(BorshSerialize)]
pub struct FundingPaid {
    pub market_id: u32,
    pub funding_rate_bps: i64,
    pub total_long_payment: i64,
    pub total_short_payment: i64,
    pub timestamp: u64,
}

#[derive(BorshSerialize)]
pub struct CollateralModified {
    pub position_id: u64,
    pub old_collateral: u64,
    pub new_collateral: u64,
    pub new_leverage: u64,
}

// =============================================================================
// STORAGE HELPERS
// =============================================================================

fn get_owner() -> Option<Address> {
    storage::get::<Address>(keys::OWNER)
}

fn is_owner(addr: &Address) -> bool {
    get_owner().map(|o| o == *addr).unwrap_or(false)
}

fn is_initialized() -> bool {
    storage::get::<bool>(keys::INITIALIZED).unwrap_or(false)
}

fn is_paused() -> bool {
    storage::get::<bool>(keys::PAUSED).unwrap_or(false)
}

fn get_market_count() -> u32 {
    storage::get::<u32>(keys::MARKET_COUNT).unwrap_or(0)
}

fn set_market_count(count: u32) {
    storage::set(keys::MARKET_COUNT, &count);
}

fn get_market(id: u32) -> Option<Market> {
    storage::get_with_suffix::<Market>(keys::MARKETS, &id.to_le_bytes())
}

fn set_market(id: u32, market: &Market) {
    storage::set_with_suffix(keys::MARKETS, &id.to_le_bytes(), market);
}

fn get_position_count() -> u64 {
    storage::get::<u64>(keys::POSITION_COUNT).unwrap_or(0)
}

fn set_position_count(count: u64) {
    storage::set(keys::POSITION_COUNT, &count);
}

fn get_position(id: u64) -> Option<Position> {
    storage::get_with_suffix::<Position>(keys::POSITIONS, &id.to_le_bytes())
}

fn set_position(id: u64, position: &Position) {
    storage::set_with_suffix(keys::POSITIONS, &id.to_le_bytes(), position);
}

fn get_price(market_id: u32) -> Option<PriceData> {
    storage::get_with_suffix::<PriceData>(keys::PRICES, &market_id.to_le_bytes())
}

fn set_price(market_id: u32, price: &PriceData) {
    storage::set_with_suffix(keys::PRICES, &market_id.to_le_bytes(), price);
}

fn get_funding(market_id: u32) -> Option<FundingData> {
    storage::get_with_suffix::<FundingData>(keys::CUMULATIVE_FUNDING, &market_id.to_le_bytes())
}

fn set_funding(market_id: u32, funding: &FundingData) {
    storage::set_with_suffix(keys::CUMULATIVE_FUNDING, &market_id.to_le_bytes(), funding);
}

fn get_insurance_fund() -> u64 {
    storage::get::<u64>(keys::INSURANCE_FUND).unwrap_or(0)
}

fn set_insurance_fund(amount: u64) {
    storage::set(keys::INSURANCE_FUND, &amount);
}

fn get_oracle() -> Option<Address> {
    storage::get::<Address>(keys::ORACLE)
}

// =============================================================================
// CALCULATION HELPERS
// =============================================================================

/// Calculate unrealized PnL for a position
fn calculate_pnl(position: &Position, mark_price: u64) -> i64 {
    let size_value = (position.size as u128 * mark_price as u128) / SIZE_PRECISION as u128;
    let entry_value = (position.size as u128 * position.entry_price as u128) / SIZE_PRECISION as u128;

    match position.direction {
        Direction::Long => (size_value as i64) - (entry_value as i64),
        Direction::Short => (entry_value as i64) - (size_value as i64),
    }
}

/// Calculate margin ratio (collateral + pnl) / position_value
fn calculate_margin_ratio(position: &Position, mark_price: u64) -> u64 {
    let pnl = calculate_pnl(position, mark_price);
    let effective_collateral = if pnl >= 0 {
        position.collateral + (pnl as u64)
    } else {
        position.collateral.saturating_sub((-pnl) as u64)
    };

    let position_value = (position.size as u128 * mark_price as u128) / SIZE_PRECISION as u128;
    if position_value == 0 {
        return BPS_DENOMINATOR;
    }

    ((effective_collateral as u128 * BPS_DENOMINATOR as u128) / position_value) as u64
}

/// Check if position should be liquidated
fn is_liquidatable(position: &Position, mark_price: u64) -> bool {
    let margin_ratio = calculate_margin_ratio(position, mark_price);
    margin_ratio < MAINTENANCE_MARGIN_BPS
}

/// Calculate liquidation price
fn calculate_liquidation_price(position: &Position) -> u64 {
    // For long: liq_price = entry_price * (1 - maintenance_margin / leverage)
    // For short: liq_price = entry_price * (1 + maintenance_margin / leverage)

    let margin_factor = (MAINTENANCE_MARGIN_BPS as u128 * PRICE_PRECISION as u128)
        / (position.leverage as u128 * 100);

    match position.direction {
        Direction::Long => {
            position.entry_price.saturating_sub(margin_factor as u64)
        }
        Direction::Short => {
            position.entry_price + margin_factor as u64
        }
    }
}

/// Calculate funding payment for a position
fn calculate_funding_payment(position: &Position, current_funding: &FundingData) -> i64 {
    let funding_delta = match position.direction {
        Direction::Long => current_funding.cumulative_long - position.entry_funding,
        Direction::Short => current_funding.cumulative_short - position.entry_funding,
    };

    // funding_payment = position_size * funding_delta / SIZE_PRECISION
    ((position.size as i128 * funding_delta as i128) / SIZE_PRECISION as i128) as i64
}

/// Calculate new funding rate based on mark vs index price
fn calculate_funding_rate(mark_price: u64, index_price: u64) -> i64 {
    if index_price == 0 {
        return 0;
    }

    // funding_rate = (mark_price - index_price) / index_price
    // Scaled to bps
    let price_diff = mark_price as i64 - index_price as i64;
    let rate = (price_diff as i128 * BPS_DENOMINATOR as i128) / index_price as i128;

    // Clamp to max funding rate
    rate.max(-(MAX_FUNDING_RATE_BPS as i128)).min(MAX_FUNDING_RATE_BPS as i128) as i64
}

/// Calculate trading fee
fn calculate_fee(size: u64, price: u64, fee_bps: u64) -> u64 {
    let notional = (size as u128 * price as u128) / SIZE_PRECISION as u128;
    ((notional * fee_bps as u128) / BPS_DENOMINATOR as u128) as u64
}

// =============================================================================
// ENTRY POINTS
// =============================================================================

synor_sdk::entry_point!(init, call);

fn init(params: &[u8]) -> Result<()> {
    require!(!is_initialized(), Error::invalid_args("Already initialized"));

    #[derive(BorshDeserialize)]
    struct InitParams {
        oracle: Address,
    }

    let params = InitParams::try_from_slice(params)
        .map_err(|_| Error::invalid_args("Invalid init params"))?;

    storage::set(keys::OWNER, &caller());
    storage::set(keys::INITIALIZED, &true);
    storage::set(keys::PAUSED, &false);
    storage::set(keys::ORACLE, &params.oracle);
    set_market_count(0);
    set_position_count(0);
    set_insurance_fund(0);

    Ok(())
}

fn call(selector: &[u8], params: &[u8]) -> Result<Vec<u8>> {
    // Method selectors
    let create_market_sel = synor_sdk::method_selector("create_market");
    let open_position_sel = synor_sdk::method_selector("open_position");
    let close_position_sel = synor_sdk::method_selector("close_position");
    let add_collateral_sel = synor_sdk::method_selector("add_collateral");
    let remove_collateral_sel = synor_sdk::method_selector("remove_collateral");
    let liquidate_sel = synor_sdk::method_selector("liquidate");
    let update_price_sel = synor_sdk::method_selector("update_price");
    let settle_funding_sel = synor_sdk::method_selector("settle_funding");
    let get_position_sel = synor_sdk::method_selector("get_position");
    let get_market_sel = synor_sdk::method_selector("get_market");
    let get_price_sel = synor_sdk::method_selector("get_price");
    let get_pnl_sel = synor_sdk::method_selector("get_pnl");
    let pause_sel = synor_sdk::method_selector("pause");
    let unpause_sel = synor_sdk::method_selector("unpause");

    match selector {
        // ===== Admin Methods =====

        s if s == create_market_sel => {
            let owner = get_owner().ok_or(Error::Unauthorized)?;
            require_auth!(owner);

            #[derive(BorshDeserialize)]
            struct Args {
                base_asset: String,
                quote_asset: String,
                max_leverage: u64,
                taker_fee_bps: u64,
                maker_fee_bps: i64,
                min_size: u64,
                max_size: u64,
                max_open_interest: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid create_market params"))?;

            require!(args.max_leverage >= MIN_LEVERAGE && args.max_leverage <= MAX_LEVERAGE,
                Error::invalid_args("Leverage must be 2-100x"));

            let market_id = get_market_count();
            let market = Market {
                id: market_id,
                base_asset: args.base_asset.clone(),
                quote_asset: args.quote_asset.clone(),
                status: MarketStatus::Active,
                max_leverage: args.max_leverage,
                maintenance_margin_bps: MAINTENANCE_MARGIN_BPS,
                taker_fee_bps: args.taker_fee_bps,
                maker_fee_bps: args.maker_fee_bps,
                min_size: args.min_size,
                max_size: args.max_size,
                open_interest_long: 0,
                open_interest_short: 0,
                max_open_interest: args.max_open_interest,
            };

            set_market(market_id, &market);
            set_market_count(market_id + 1);

            // Initialize funding data
            let funding = FundingData {
                rate_bps: 0,
                cumulative_long: 0,
                cumulative_short: 0,
                last_update: timestamp(),
            };
            set_funding(market_id, &funding);

            emit_raw(
                &[event_topic(b"MarketCreated")],
                &borsh::to_vec(&MarketCreated {
                    market_id,
                    base_asset: args.base_asset,
                    quote_asset: args.quote_asset,
                    max_leverage: args.max_leverage,
                }).unwrap(),
            );

            Ok(borsh::to_vec(&market_id).unwrap())
        }

        s if s == pause_sel => {
            let owner = get_owner().ok_or(Error::Unauthorized)?;
            require_auth!(owner);
            storage::set(keys::PAUSED, &true);
            Ok(borsh::to_vec(&true).unwrap())
        }

        s if s == unpause_sel => {
            let owner = get_owner().ok_or(Error::Unauthorized)?;
            require_auth!(owner);
            storage::set(keys::PAUSED, &false);
            Ok(borsh::to_vec(&true).unwrap())
        }

        // ===== Oracle Methods =====

        s if s == update_price_sel => {
            // Only oracle can update prices
            let oracle = get_oracle().ok_or(Error::Unauthorized)?;
            require_auth!(oracle);

            #[derive(BorshDeserialize)]
            struct Args {
                market_id: u32,
                index_price: u64,
                confidence: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid update_price params"))?;

            let _market = get_market(args.market_id)
                .ok_or_else(|| Error::invalid_args("Market not found"))?;

            // Get previous mark price for EMA calculation
            let prev_price = get_price(args.market_id);
            let prev_mark = prev_price.map(|p| p.mark_price).unwrap_or(args.index_price);

            // Mark price is EMA of index price (smooths out manipulation)
            // mark_price = 0.9 * prev_mark + 0.1 * index_price
            let mark_price = (prev_mark * 9 + args.index_price) / 10;

            let price_data = PriceData {
                index_price: args.index_price,
                mark_price,
                timestamp: timestamp(),
                confidence: args.confidence,
            };

            set_price(args.market_id, &price_data);

            Ok(borsh::to_vec(&price_data).unwrap())
        }

        // ===== Trading Methods =====

        s if s == open_position_sel => {
            require!(!is_paused(), Error::invalid_args("Contract paused"));

            #[derive(BorshDeserialize)]
            struct Args {
                market_id: u32,
                direction: Direction,
                size: u64,
                collateral: u64,
                leverage: u64,
                margin_mode: MarginMode,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid open_position params"))?;

            let mut market = get_market(args.market_id)
                .ok_or_else(|| Error::invalid_args("Market not found"))?;
            require!(market.status == MarketStatus::Active,
                Error::invalid_args("Market not active"));

            let price = get_price(args.market_id)
                .ok_or_else(|| Error::invalid_args("Price not available"))?;

            // Validate leverage
            require!(args.leverage >= MIN_LEVERAGE && args.leverage <= market.max_leverage,
                Error::invalid_args("Invalid leverage"));

            // Validate size
            require!(args.size >= market.min_size && args.size <= market.max_size,
                Error::invalid_args("Invalid position size"));

            // Validate collateral matches leverage requirement
            let required_collateral = (args.size as u128 * price.mark_price as u128)
                / (SIZE_PRECISION as u128 * args.leverage as u128);
            require!(args.collateral >= required_collateral as u64,
                Error::invalid_args("Insufficient collateral for leverage"));

            // Check open interest limits
            let new_oi = match args.direction {
                Direction::Long => market.open_interest_long + args.size,
                Direction::Short => market.open_interest_short + args.size,
            };
            require!(new_oi <= market.max_open_interest,
                Error::invalid_args("Exceeds max open interest"));

            // Get current funding
            let funding = get_funding(args.market_id).unwrap_or(FundingData {
                rate_bps: 0,
                cumulative_long: 0,
                cumulative_short: 0,
                last_update: timestamp(),
            });

            let entry_funding = match args.direction {
                Direction::Long => funding.cumulative_long,
                Direction::Short => funding.cumulative_short,
            };

            // Calculate and deduct fee
            let fee = calculate_fee(args.size, price.mark_price, market.taker_fee_bps);
            let net_collateral = args.collateral.saturating_sub(fee);

            // Create position
            let position_id = get_position_count();
            let position = Position {
                id: position_id,
                owner: caller(),
                market_id: args.market_id,
                direction: args.direction,
                size: args.size,
                collateral: net_collateral,
                entry_price: price.mark_price,
                leverage: args.leverage,
                margin_mode: args.margin_mode,
                entry_funding,
                opened_at: timestamp(),
                updated_at: timestamp(),
                is_open: true,
            };

            // Update open interest
            match args.direction {
                Direction::Long => market.open_interest_long += args.size,
                Direction::Short => market.open_interest_short += args.size,
            }

            set_position(position_id, &position);
            set_position_count(position_id + 1);
            set_market(args.market_id, &market);

            emit_raw(
                &[event_topic(b"PositionOpened")],
                &borsh::to_vec(&PositionOpened {
                    position_id,
                    owner: caller(),
                    market_id: args.market_id,
                    direction: args.direction,
                    size: args.size,
                    collateral: net_collateral,
                    entry_price: price.mark_price,
                    leverage: args.leverage,
                }).unwrap(),
            );

            Ok(borsh::to_vec(&position_id).unwrap())
        }

        s if s == close_position_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                position_id: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid close_position params"))?;

            let mut position = get_position(args.position_id)
                .ok_or_else(|| Error::invalid_args("Position not found"))?;
            require!(position.is_open, Error::invalid_args("Position already closed"));
            require!(position.owner == caller(), Error::Unauthorized);

            let mut market = get_market(position.market_id)
                .ok_or_else(|| Error::invalid_args("Market not found"))?;

            let price = get_price(position.market_id)
                .ok_or_else(|| Error::invalid_args("Price not available"))?;

            // Calculate PnL
            let pnl = calculate_pnl(&position, price.mark_price);

            // Calculate funding owed
            let funding = get_funding(position.market_id).unwrap_or(FundingData {
                rate_bps: 0,
                cumulative_long: 0,
                cumulative_short: 0,
                last_update: timestamp(),
            });
            let funding_payment = calculate_funding_payment(&position, &funding);

            // Calculate fee
            let fee = calculate_fee(position.size, price.mark_price, market.taker_fee_bps);

            // Calculate final payout
            let total_pnl = pnl - funding_payment - fee as i64;
            let payout = if total_pnl >= 0 {
                position.collateral + total_pnl as u64
            } else {
                position.collateral.saturating_sub((-total_pnl) as u64)
            };

            // Update open interest
            match position.direction {
                Direction::Long => market.open_interest_long = market.open_interest_long.saturating_sub(position.size),
                Direction::Short => market.open_interest_short = market.open_interest_short.saturating_sub(position.size),
            }

            // Close position
            position.is_open = false;
            position.updated_at = timestamp();

            set_position(args.position_id, &position);
            set_market(position.market_id, &market);

            emit_raw(
                &[event_topic(b"PositionClosed")],
                &borsh::to_vec(&PositionClosed {
                    position_id: args.position_id,
                    owner: position.owner,
                    exit_price: price.mark_price,
                    realized_pnl: total_pnl,
                    fee_paid: fee,
                }).unwrap(),
            );

            Ok(borsh::to_vec(&payout).unwrap())
        }

        s if s == add_collateral_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                position_id: u64,
                amount: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid add_collateral params"))?;

            let mut position = get_position(args.position_id)
                .ok_or_else(|| Error::invalid_args("Position not found"))?;
            require!(position.is_open, Error::invalid_args("Position closed"));
            require!(position.owner == caller(), Error::Unauthorized);

            let old_collateral = position.collateral;
            position.collateral += args.amount;
            position.updated_at = timestamp();

            // Recalculate effective leverage
            let price = get_price(position.market_id)
                .ok_or_else(|| Error::invalid_args("Price not available"))?;
            let notional = (position.size as u128 * price.mark_price as u128) / SIZE_PRECISION as u128;
            let new_leverage = if position.collateral > 0 {
                (notional / position.collateral as u128) as u64
            } else {
                MAX_LEVERAGE
            };
            position.leverage = new_leverage.max(MIN_LEVERAGE);

            set_position(args.position_id, &position);

            emit_raw(
                &[event_topic(b"CollateralModified")],
                &borsh::to_vec(&CollateralModified {
                    position_id: args.position_id,
                    old_collateral,
                    new_collateral: position.collateral,
                    new_leverage: position.leverage,
                }).unwrap(),
            );

            Ok(borsh::to_vec(&position.collateral).unwrap())
        }

        s if s == remove_collateral_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                position_id: u64,
                amount: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid remove_collateral params"))?;

            let mut position = get_position(args.position_id)
                .ok_or_else(|| Error::invalid_args("Position not found"))?;
            require!(position.is_open, Error::invalid_args("Position closed"));
            require!(position.owner == caller(), Error::Unauthorized);

            let market = get_market(position.market_id)
                .ok_or_else(|| Error::invalid_args("Market not found"))?;

            let price = get_price(position.market_id)
                .ok_or_else(|| Error::invalid_args("Price not available"))?;

            let old_collateral = position.collateral;
            let new_collateral = position.collateral.saturating_sub(args.amount);

            // Check that new collateral maintains margin requirement
            let notional = (position.size as u128 * price.mark_price as u128) / SIZE_PRECISION as u128;
            let new_leverage = if new_collateral > 0 {
                (notional / new_collateral as u128) as u64
            } else {
                MAX_LEVERAGE + 1
            };

            require!(new_leverage <= market.max_leverage,
                Error::invalid_args("Would exceed max leverage"));

            // Check margin ratio after removal
            position.collateral = new_collateral;
            require!(!is_liquidatable(&position, price.mark_price),
                Error::invalid_args("Would become liquidatable"));

            position.leverage = new_leverage;
            position.updated_at = timestamp();

            set_position(args.position_id, &position);

            emit_raw(
                &[event_topic(b"CollateralModified")],
                &borsh::to_vec(&CollateralModified {
                    position_id: args.position_id,
                    old_collateral,
                    new_collateral,
                    new_leverage,
                }).unwrap(),
            );

            Ok(borsh::to_vec(&args.amount).unwrap())
        }

        s if s == liquidate_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                position_id: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid liquidate params"))?;

            let mut position = get_position(args.position_id)
                .ok_or_else(|| Error::invalid_args("Position not found"))?;
            require!(position.is_open, Error::invalid_args("Position closed"));

            let mut market = get_market(position.market_id)
                .ok_or_else(|| Error::invalid_args("Market not found"))?;

            let price = get_price(position.market_id)
                .ok_or_else(|| Error::invalid_args("Price not available"))?;

            // Verify position is liquidatable
            require!(is_liquidatable(&position, price.mark_price),
                Error::invalid_args("Position not liquidatable"));

            // Calculate liquidation amounts
            let collateral_seized = position.collateral;
            let liquidation_fee = (collateral_seized as u128 * LIQUIDATION_FEE_BPS as u128
                / BPS_DENOMINATOR as u128) as u64;

            let insurance_share = (liquidation_fee as u128 * INSURANCE_SHARE_BPS as u128
                / BPS_DENOMINATOR as u128) as u64;
            let liquidator_reward = liquidation_fee - insurance_share;

            // Update insurance fund
            let insurance = get_insurance_fund();
            set_insurance_fund(insurance + insurance_share);

            // Update open interest
            match position.direction {
                Direction::Long => market.open_interest_long = market.open_interest_long.saturating_sub(position.size),
                Direction::Short => market.open_interest_short = market.open_interest_short.saturating_sub(position.size),
            }

            // Close position
            position.is_open = false;
            position.collateral = 0;
            position.updated_at = timestamp();

            set_position(args.position_id, &position);
            set_market(position.market_id, &market);

            emit_raw(
                &[event_topic(b"PositionLiquidated")],
                &borsh::to_vec(&PositionLiquidated {
                    position_id: args.position_id,
                    owner: position.owner,
                    liquidator: caller(),
                    size: position.size,
                    collateral_seized,
                    liquidation_price: price.mark_price,
                    insurance_fund_share: insurance_share,
                    liquidator_reward,
                }).unwrap(),
            );

            Ok(borsh::to_vec(&liquidator_reward).unwrap())
        }

        s if s == settle_funding_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                market_id: u32,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid settle_funding params"))?;

            let market = get_market(args.market_id)
                .ok_or_else(|| Error::invalid_args("Market not found"))?;

            let price = get_price(args.market_id)
                .ok_or_else(|| Error::invalid_args("Price not available"))?;

            let mut funding = get_funding(args.market_id).unwrap_or(FundingData {
                rate_bps: 0,
                cumulative_long: 0,
                cumulative_short: 0,
                last_update: timestamp(),
            });

            let now = timestamp();
            let time_elapsed = now - funding.last_update;

            // Only settle if enough time has passed
            require!(time_elapsed >= FUNDING_INTERVAL,
                Error::invalid_args("Funding interval not elapsed"));

            // Calculate new funding rate
            let new_rate = calculate_funding_rate(price.mark_price, price.index_price);

            // Calculate funding payments
            // Positive rate = longs pay shorts
            // Negative rate = shorts pay longs
            let long_payment = (market.open_interest_long as i128 * new_rate as i128
                / BPS_DENOMINATOR as i128) as i64;
            let short_payment = -long_payment;

            // Update cumulative funding
            funding.rate_bps = new_rate;
            funding.cumulative_long += long_payment;
            funding.cumulative_short += short_payment;
            funding.last_update = now;

            set_funding(args.market_id, &funding);

            emit_raw(
                &[event_topic(b"FundingPaid")],
                &borsh::to_vec(&FundingPaid {
                    market_id: args.market_id,
                    funding_rate_bps: new_rate,
                    total_long_payment: long_payment,
                    total_short_payment: short_payment,
                    timestamp: now,
                }).unwrap(),
            );

            Ok(borsh::to_vec(&new_rate).unwrap())
        }

        // ===== Query Methods =====

        s if s == get_position_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                position_id: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Expected (position_id: u64)"))?;

            let position = get_position(args.position_id);
            Ok(borsh::to_vec(&position).unwrap())
        }

        s if s == get_market_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                market_id: u32,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Expected (market_id: u32)"))?;

            let market = get_market(args.market_id);
            Ok(borsh::to_vec(&market).unwrap())
        }

        s if s == get_price_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                market_id: u32,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Expected (market_id: u32)"))?;

            let price = get_price(args.market_id);
            Ok(borsh::to_vec(&price).unwrap())
        }

        s if s == get_pnl_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                position_id: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Expected (position_id: u64)"))?;

            let position = get_position(args.position_id)
                .ok_or_else(|| Error::invalid_args("Position not found"))?;

            let price = get_price(position.market_id)
                .ok_or_else(|| Error::invalid_args("Price not available"))?;

            let pnl = calculate_pnl(&position, price.mark_price);
            let liq_price = calculate_liquidation_price(&position);
            let margin_ratio = calculate_margin_ratio(&position, price.mark_price);

            #[derive(BorshSerialize)]
            struct PnlResult {
                unrealized_pnl: i64,
                liquidation_price: u64,
                margin_ratio_bps: u64,
                is_liquidatable: bool,
            }

            let result = PnlResult {
                unrealized_pnl: pnl,
                liquidation_price: liq_price,
                margin_ratio_bps: margin_ratio,
                is_liquidatable: is_liquidatable(&position, price.mark_price),
            };

            Ok(borsh::to_vec(&result).unwrap())
        }

        _ => Err(Error::InvalidMethod),
    }
}
