//! Cross-Chain DEX Liquidity Aggregator
//!
//! This contract aggregates liquidity from multiple sources to provide:
//! - Best execution price across all DEXs
//! - Cross-chain swaps via IBC
//! - Split routing for large orders
//! - MEV protection via private mempool
//!
//! # Zero-Capital Strategy
//!
//! ```text
//! ┌─────────────────────────────────────────────────────────────┐
//! │           SYNOR LIQUIDITY AGGREGATOR                        │
//! │              (No Capital Required)                          │
//! ├─────────────────────────────────────────────────────────────┤
//! │                                                             │
//! │  USER REQUEST: Swap 10 ETH for USDC (best price)           │
//! │                            │                                │
//! │                            ▼                                │
//! │  ┌─────────────────────────────────────────────┐           │
//! │  │           QUOTE AGGREGATION                  │           │
//! │  │                                              │           │
//! │  │  ┌─────────┐   ┌─────────┐   ┌─────────┐   │           │
//! │  │  │ Synor   │   │ Osmosis │   │ dYdX    │   │           │
//! │  │  │ DEX     │   │ (IBC)   │   │ (IBC)   │   │           │
//! │  │  │ $25,010 │   │ $25,015 │   │ $25,020 │   │           │
//! │  │  └────┬────┘   └────┬────┘   └────┬────┘   │           │
//! │  │       │             │             │        │           │
//! │  │       └─────────────┼─────────────┘        │           │
//! │  │                     │                      │           │
//! │  │           BEST PRICE: $25,020 (dYdX)      │           │
//! │  └─────────────────────┬────────────────────┘            │
//! │                        │                                  │
//! │                        ▼                                  │
//! │  ┌─────────────────────────────────────────────┐         │
//! │  │           ROUTE EXECUTION                    │         │
//! │  │                                              │         │
//! │  │  Option A: Single route (small order)       │         │
//! │  │  ETH ────────────────────────────► USDC     │         │
//! │  │       via dYdX IBC bridge                   │         │
//! │  │                                              │         │
//! │  │  Option B: Split route (large order)        │         │
//! │  │  ETH ──┬── 60% via dYdX ──────────► USDC   │         │
//! │  │        ├── 25% via Osmosis ────────►       │         │
//! │  │        └── 15% via Synor DEX ──────►       │         │
//! │  └─────────────────────────────────────────────┘         │
//! │                                                           │
//! │  REVENUE SOURCES:                                         │
//! │  • Aggregation fee: 0.05% of swap volume                 │
//! │  • Positive slippage capture                              │
//! │  • Bridge fee share (partner programs)                   │
//! └─────────────────────────────────────────────────────────────┘
//! ```

#![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
// =============================================================================

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

/// Amount precision (8 decimals)
pub const AMOUNT_PRECISION: u64 = 100_000_000;

/// Basis points denominator
pub const BPS_DENOMINATOR: u64 = 10000;

/// Aggregation fee (5 bps = 0.05%)
pub const AGGREGATION_FEE_BPS: u64 = 5;

/// Maximum slippage (5% = 500 bps)
pub const MAX_SLIPPAGE_BPS: u64 = 500;

/// Minimum split percentage (10%)
pub const MIN_SPLIT_PERCENT: u64 = 10;

/// Maximum routes per swap
pub const MAX_ROUTES: usize = 5;

/// IBC timeout (10 minutes)
pub const IBC_TIMEOUT_SECONDS: u64 = 10 * 60;

// =============================================================================
// 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"agg:owner";
    pub const INITIALIZED: &[u8] = b"agg:initialized";
    pub const PAUSED: &[u8] = b"agg:paused";

    // Liquidity sources
    pub const SOURCES: &[u8] = b"agg:sources";
    pub const SOURCE_COUNT: &[u8] = b"agg:source_count";

    // Supported tokens
    pub const TOKENS: &[u8] = b"agg:tokens";
    pub const TOKEN_COUNT: &[u8] = b"agg:token_count";

    // Pending IBC swaps
    pub const PENDING_SWAPS: &[u8] = b"agg:pending";
    pub const SWAP_COUNT: &[u8] = b"agg:swap_count";

    // Fee collector
    pub const FEE_COLLECTOR: &[u8] = b"agg:fee_collector";
    pub const COLLECTED_FEES: &[u8] = b"agg:fees";

    // Statistics
    pub const TOTAL_VOLUME: &[u8] = b"agg:volume";
}

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

/// Type of liquidity source
#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum SourceType {
    /// Local AMM (Synor DEX)
    LocalAmm,
    /// IBC-connected DEX (Osmosis, dYdX, etc.)
    IbcDex,
    /// Centralized exchange API (for quotes only)
    CexApi,
    /// Order book
    OrderBook,
}

/// Liquidity source status
#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum SourceStatus {
    Active,
    Paused,
    Deprecated,
}

/// A liquidity source (DEX or bridge)
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct LiquiditySource {
    /// Source ID
    pub id: u32,
    /// Human-readable name
    pub name: String,
    /// Type of source
    pub source_type: SourceType,
    /// Contract address (for local sources)
    pub contract: Option<Address>,
    /// IBC channel (for IBC sources)
    pub ibc_channel: Option<String>,
    /// Chain ID (for IBC sources)
    pub chain_id: Option<String>,
    /// Status
    pub status: SourceStatus,
    /// Base fee (bps)
    pub base_fee_bps: u64,
    /// Priority (higher = preferred)
    pub priority: u32,
    /// Total volume routed
    pub total_volume: u64,
    /// Success rate (bps, e.g., 9900 = 99%)
    pub success_rate_bps: u64,
    /// Average latency (milliseconds)
    pub avg_latency_ms: u64,
}

/// Token information
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct TokenInfo {
    /// Token symbol (e.g., "ETH")
    pub symbol: String,
    /// Token address (local)
    pub local_address: Address,
    /// Decimals
    pub decimals: u8,
    /// IBC denom mapping (chain_id -> denom)
    pub ibc_denoms: Vec<(String, String)>,
    /// Is native asset
    pub is_native: bool,
}

/// Quote from a liquidity source
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct Quote {
    /// Source ID
    pub source_id: u32,
    /// Source name
    pub source_name: String,
    /// Input amount
    pub amount_in: u64,
    /// Output amount (expected)
    pub amount_out: u64,
    /// Price impact (bps)
    pub price_impact_bps: u64,
    /// Fee (in output token)
    pub fee: u64,
    /// Is available
    pub available: bool,
    /// Estimated execution time (seconds)
    pub estimated_time: u64,
}

/// Route for executing a swap
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct Route {
    /// Source ID
    pub source_id: u32,
    /// Percentage of total amount (0-100)
    pub percentage: u64,
    /// Expected output
    pub expected_output: u64,
    /// Minimum output (with slippage)
    pub min_output: u64,
}

/// Swap execution plan
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct SwapPlan {
    /// Input token
    pub token_in: String,
    /// Output token
    pub token_out: String,
    /// Total input amount
    pub amount_in: u64,
    /// Routes to execute
    pub routes: Vec<Route>,
    /// Total expected output
    pub total_expected_output: u64,
    /// Minimum output (user's slippage tolerance)
    pub min_output: u64,
    /// Aggregation fee
    pub aggregation_fee: u64,
    /// Valid until timestamp
    pub valid_until: u64,
}

/// Pending IBC swap
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct PendingSwap {
    /// Swap ID
    pub id: u64,
    /// User address
    pub user: Address,
    /// Plan that was executed
    pub plan: SwapPlan,
    /// Current status
    pub status: SwapStatus,
    /// Amount received so far
    pub amount_received: u64,
    /// Routes completed
    pub routes_completed: u32,
    /// Created timestamp
    pub created_at: u64,
    /// Timeout timestamp
    pub timeout_at: u64,
}

/// Swap execution status
#[derive(Clone, Copy, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum SwapStatus {
    /// Swap initiated, waiting for execution
    Pending,
    /// Partially filled
    PartiallyFilled,
    /// Fully completed
    Completed,
    /// Failed
    Failed,
    /// Refunded
    Refunded,
    /// Timed out
    TimedOut,
}

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

#[derive(BorshSerialize)]
pub struct SourceAdded {
    pub source_id: u32,
    pub name: String,
    pub source_type: SourceType,
}

#[derive(BorshSerialize)]
pub struct SwapExecuted {
    pub swap_id: u64,
    pub user: Address,
    pub token_in: String,
    pub token_out: String,
    pub amount_in: u64,
    pub amount_out: u64,
    pub fee: u64,
    pub routes_used: u32,
}

#[derive(BorshSerialize)]
pub struct SwapPartialFill {
    pub swap_id: u64,
    pub source_id: u32,
    pub amount_filled: u64,
}

#[derive(BorshSerialize)]
pub struct SwapFailed {
    pub swap_id: u64,
    pub reason: String,
}

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

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

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_source_count() -> u32 {
    storage::get::<u32>(keys::SOURCE_COUNT).unwrap_or(0)
}

fn set_source_count(count: u32) {
    storage::set(keys::SOURCE_COUNT, &count);
}

fn get_source(id: u32) -> Option<LiquiditySource> {
    storage::get_with_suffix::<LiquiditySource>(keys::SOURCES, &id.to_le_bytes())
}

fn set_source(id: u32, source: &LiquiditySource) {
    storage::set_with_suffix(keys::SOURCES, &id.to_le_bytes(), source);
}

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

fn set_token_count(count: u32) {
    storage::set(keys::TOKEN_COUNT, &count);
}

fn get_token(symbol: &str) -> Option<TokenInfo> {
    storage::get_with_suffix::<TokenInfo>(keys::TOKENS, symbol.as_bytes())
}

fn set_token(symbol: &str, token: &TokenInfo) {
    storage::set_with_suffix(keys::TOKENS, symbol.as_bytes(), token);
}

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

fn set_swap_count(count: u64) {
    storage::set(keys::SWAP_COUNT, &count);
}

fn get_pending_swap(id: u64) -> Option<PendingSwap> {
    storage::get_with_suffix::<PendingSwap>(keys::PENDING_SWAPS, &id.to_le_bytes())
}

fn set_pending_swap(id: u64, swap: &PendingSwap) {
    storage::set_with_suffix(keys::PENDING_SWAPS, &id.to_le_bytes(), swap);
}

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

fn set_total_volume(volume: u64) {
    storage::set(keys::TOTAL_VOLUME, &volume);
}

fn get_collected_fees(token: &str) -> u64 {
    storage::get_with_suffix::<u64>(keys::COLLECTED_FEES, token.as_bytes()).unwrap_or(0)
}

fn set_collected_fees(token: &str, amount: u64) {
    storage::set_with_suffix(keys::COLLECTED_FEES, token.as_bytes(), &amount);
}

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

/// Calculate aggregation fee
fn calculate_aggregation_fee(amount: u64) -> u64 {
    (amount as u128 * AGGREGATION_FEE_BPS as u128 / BPS_DENOMINATOR as u128) as u64
}

/// Apply slippage tolerance
fn apply_slippage(amount: u64, slippage_bps: u64) -> u64 {
    let slippage = (amount as u128 * slippage_bps as u128 / BPS_DENOMINATOR as u128) as u64;
    amount.saturating_sub(slippage)
}

/// Find best quote among sources
fn find_best_quote(quotes: &[Quote]) -> Option<&Quote> {
    quotes.iter()
        .filter(|q| q.available)
        .max_by_key(|q| q.amount_out)
}

/// Create optimal routing plan
fn create_routing_plan(
    quotes: &[Quote],
    amount_in: u64,
    slippage_bps: u64,
) -> Vec<Route> {
    let mut routes = Vec::new();

    // Filter available quotes and sort by output (best first)
    let mut available: Vec<_> = quotes.iter()
        .filter(|q| q.available && q.amount_out > 0)
        .collect();
    available.sort_by(|a, b| b.amount_out.cmp(&a.amount_out));

    if available.is_empty() {
        return routes;
    }

    // For simplicity, use single best route for small amounts
    // Split across multiple routes for large amounts to reduce price impact
    let total_liquidity: u64 = available.iter().map(|q| q.amount_out).sum();

    if available.len() == 1 || amount_in < total_liquidity / 10 {
        // Single route
        let best = &available[0];
        routes.push(Route {
            source_id: best.source_id,
            percentage: 100,
            expected_output: best.amount_out,
            min_output: apply_slippage(best.amount_out, slippage_bps),
        });
    } else {
        // Split routing - distribute based on available liquidity
        let mut remaining_percent = 100u64;

        for (i, quote) in available.iter().take(MAX_ROUTES).enumerate() {
            let percent = if i == available.len().min(MAX_ROUTES) - 1 {
                remaining_percent // Last route gets remainder
            } else {
                // Proportional to output
                let share = (quote.amount_out as u128 * 100) / total_liquidity as u128;
                (share as u64).max(MIN_SPLIT_PERCENT).min(remaining_percent - MIN_SPLIT_PERCENT)
            };

            if percent < MIN_SPLIT_PERCENT {
                continue;
            }

            let route_amount_in = (amount_in as u128 * percent as u128 / 100) as u64;
            let expected_out = (quote.amount_out as u128 * percent as u128 / 100) as u64;

            routes.push(Route {
                source_id: quote.source_id,
                percentage: percent,
                expected_output: expected_out,
                min_output: apply_slippage(expected_out, slippage_bps),
            });

            remaining_percent = remaining_percent.saturating_sub(percent);
            if remaining_percent < MIN_SPLIT_PERCENT {
                break;
            }
        }
    }

    routes
}

// =============================================================================
// 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 {
        fee_collector: 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::FEE_COLLECTOR, &params.fee_collector);
    set_source_count(0);
    set_token_count(0);
    set_swap_count(0);
    set_total_volume(0);

    Ok(())
}

fn call(selector: &[u8], params: &[u8]) -> Result<Vec<u8>> {
    // Method selectors
    let add_source_sel = synor_sdk::method_selector("add_source");
    let update_source_sel = synor_sdk::method_selector("update_source");
    let add_token_sel = synor_sdk::method_selector("add_token");
    let get_quote_sel = synor_sdk::method_selector("get_quote");
    let get_quotes_sel = synor_sdk::method_selector("get_quotes");
    let swap_sel = synor_sdk::method_selector("swap");
    let swap_exact_out_sel = synor_sdk::method_selector("swap_exact_out");
    let get_swap_status_sel = synor_sdk::method_selector("get_swap_status");
    let complete_ibc_swap_sel = synor_sdk::method_selector("complete_ibc_swap");
    let refund_swap_sel = synor_sdk::method_selector("refund_swap");
    let get_source_sel = synor_sdk::method_selector("get_source");
    let get_token_sel = synor_sdk::method_selector("get_token");
    let get_stats_sel = synor_sdk::method_selector("get_stats");
    let pause_sel = synor_sdk::method_selector("pause");
    let unpause_sel = synor_sdk::method_selector("unpause");

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

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

            #[derive(BorshDeserialize)]
            struct Args {
                name: String,
                source_type: SourceType,
                contract: Option<Address>,
                ibc_channel: Option<String>,
                chain_id: Option<String>,
                base_fee_bps: u64,
                priority: u32,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid add_source params"))?;

            let source_id = get_source_count();
            let source = LiquiditySource {
                id: source_id,
                name: args.name.clone(),
                source_type: args.source_type,
                contract: args.contract,
                ibc_channel: args.ibc_channel,
                chain_id: args.chain_id,
                status: SourceStatus::Active,
                base_fee_bps: args.base_fee_bps,
                priority: args.priority,
                total_volume: 0,
                success_rate_bps: 10000, // Start at 100%
                avg_latency_ms: 0,
            };

            set_source(source_id, &source);
            set_source_count(source_id + 1);

            emit_raw(
                &[event_topic(b"SourceAdded")],
                &borsh::to_vec(&SourceAdded {
                    source_id,
                    name: args.name,
                    source_type: args.source_type,
                }).unwrap(),
            );

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

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

            #[derive(BorshDeserialize)]
            struct Args {
                source_id: u32,
                status: SourceStatus,
                base_fee_bps: u64,
                priority: u32,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid update_source params"))?;

            let mut source = get_source(args.source_id)
                .ok_or_else(|| Error::invalid_args("Source not found"))?;

            source.status = args.status;
            source.base_fee_bps = args.base_fee_bps;
            source.priority = args.priority;

            set_source(args.source_id, &source);

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

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

            #[derive(BorshDeserialize)]
            struct Args {
                symbol: String,
                local_address: Address,
                decimals: u8,
                ibc_denoms: Vec<(String, String)>,
                is_native: bool,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid add_token params"))?;

            let token = TokenInfo {
                symbol: args.symbol.clone(),
                local_address: args.local_address,
                decimals: args.decimals,
                ibc_denoms: args.ibc_denoms,
                is_native: args.is_native,
            };

            set_token(&args.symbol, &token);
            set_token_count(get_token_count() + 1);

            Ok(borsh::to_vec(&true).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())
        }

        // ===== Quote Methods =====

        s if s == get_quote_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                token_in: String,
                token_out: String,
                amount_in: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid get_quote params"))?;

            // Get quotes from all sources and find best
            let quotes = get_all_quotes(&args.token_in, &args.token_out, args.amount_in)?;
            let best = find_best_quote(&quotes);

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

        s if s == get_quotes_sel => {
            #[derive(BorshDeserialize)]
            struct Args {
                token_in: String,
                token_out: String,
                amount_in: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid get_quotes params"))?;

            let quotes = get_all_quotes(&args.token_in, &args.token_out, args.amount_in)?;

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

        // ===== Swap Methods =====

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

            #[derive(BorshDeserialize)]
            struct Args {
                token_in: String,
                token_out: String,
                amount_in: u64,
                min_amount_out: u64,
                slippage_bps: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid swap params"))?;

            require!(args.slippage_bps <= MAX_SLIPPAGE_BPS,
                Error::invalid_args("Slippage too high"));

            // Get quotes and create routing plan
            let quotes = get_all_quotes(&args.token_in, &args.token_out, args.amount_in)?;
            require!(!quotes.is_empty(), Error::invalid_args("No quotes available"));

            let routes = create_routing_plan(&quotes, args.amount_in, args.slippage_bps);
            require!(!routes.is_empty(), Error::invalid_args("No routes available"));

            let total_expected: u64 = routes.iter().map(|r| r.expected_output).sum();
            require!(total_expected >= args.min_amount_out,
                Error::invalid_args("Output below minimum"));

            // Calculate aggregation fee
            let fee = calculate_aggregation_fee(args.amount_in);

            // Create swap plan
            let swap_id = get_swap_count();
            let now = timestamp();

            let plan = SwapPlan {
                token_in: args.token_in.clone(),
                token_out: args.token_out.clone(),
                amount_in: args.amount_in,
                routes: routes.clone(),
                total_expected_output: total_expected,
                min_output: args.min_amount_out,
                aggregation_fee: fee,
                valid_until: now + IBC_TIMEOUT_SECONDS,
            };

            // For local-only routes, execute immediately
            let is_local_only = routes.iter().all(|r| {
                get_source(r.source_id)
                    .map(|s| s.source_type == SourceType::LocalAmm)
                    .unwrap_or(false)
            });

            if is_local_only {
                // Execute local swap (simplified - would call DEX contract)
                let amount_out = execute_local_swap(&plan)?;

                // Update statistics
                set_total_volume(get_total_volume() + args.amount_in);
                let fees = get_collected_fees(&args.token_in);
                set_collected_fees(&args.token_in, fees + fee);

                emit_raw(
                    &[event_topic(b"SwapExecuted")],
                    &borsh::to_vec(&SwapExecuted {
                        swap_id,
                        user: caller(),
                        token_in: args.token_in,
                        token_out: args.token_out,
                        amount_in: args.amount_in,
                        amount_out,
                        fee,
                        routes_used: routes.len() as u32,
                    }).unwrap(),
                );

                set_swap_count(swap_id + 1);
                return Ok(borsh::to_vec(&amount_out).unwrap());
            }

            // For IBC routes, create pending swap
            let pending = PendingSwap {
                id: swap_id,
                user: caller(),
                plan,
                status: SwapStatus::Pending,
                amount_received: 0,
                routes_completed: 0,
                created_at: now,
                timeout_at: now + IBC_TIMEOUT_SECONDS,
            };

            set_pending_swap(swap_id, &pending);
            set_swap_count(swap_id + 1);

            // Initiate IBC transfers (would call IBC module)
            // In production, this triggers actual IBC packet sending

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

        s if s == complete_ibc_swap_sel => {
            // Called by relayer when IBC swap completes
            #[derive(BorshDeserialize)]
            struct Args {
                swap_id: u64,
                source_id: u32,
                amount_received: u64,
            }
            let args = Args::try_from_slice(params)
                .map_err(|_| Error::invalid_args("Invalid complete_ibc_swap params"))?;

            let mut swap = get_pending_swap(args.swap_id)
                .ok_or_else(|| Error::invalid_args("Swap not found"))?;

            require!(swap.status == SwapStatus::Pending || swap.status == SwapStatus::PartiallyFilled,
                Error::invalid_args("Swap not in progress"));

            swap.amount_received += args.amount_received;
            swap.routes_completed += 1;

            emit_raw(
                &[event_topic(b"SwapPartialFill")],
                &borsh::to_vec(&SwapPartialFill {
                    swap_id: args.swap_id,
                    source_id: args.source_id,
                    amount_filled: args.amount_received,
                }).unwrap(),
            );

            // Check if all routes completed
            if swap.routes_completed as usize >= swap.plan.routes.len() {
                swap.status = SwapStatus::Completed;

                // Update statistics
                set_total_volume(get_total_volume() + swap.plan.amount_in);
                let fees = get_collected_fees(&swap.plan.token_in);
                set_collected_fees(&swap.plan.token_in, fees + swap.plan.aggregation_fee);

                // Update source stats
                for route in &swap.plan.routes {
                    if let Some(mut source) = get_source(route.source_id) {
                        source.total_volume += (swap.plan.amount_in as u128 * route.percentage as u128 / 100) as u64;
                        set_source(route.source_id, &source);
                    }
                }

                emit_raw(
                    &[event_topic(b"SwapExecuted")],
                    &borsh::to_vec(&SwapExecuted {
                        swap_id: args.swap_id,
                        user: swap.user,
                        token_in: swap.plan.token_in.clone(),
                        token_out: swap.plan.token_out.clone(),
                        amount_in: swap.plan.amount_in,
                        amount_out: swap.amount_received,
                        fee: swap.plan.aggregation_fee,
                        routes_used: swap.routes_completed,
                    }).unwrap(),
                );
            } else {
                swap.status = SwapStatus::PartiallyFilled;
            }

            set_pending_swap(args.swap_id, &swap);

            Ok(borsh::to_vec(&swap.amount_received).unwrap())
        }

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

            let mut swap = get_pending_swap(args.swap_id)
                .ok_or_else(|| Error::invalid_args("Swap not found"))?;

            // Can only refund if timed out or failed
            let now = timestamp();
            require!(
                now > swap.timeout_at || swap.status == SwapStatus::Failed,
                Error::invalid_args("Swap not refundable yet")
            );

            swap.status = SwapStatus::Refunded;
            set_pending_swap(args.swap_id, &swap);

            emit_raw(
                &[event_topic(b"SwapFailed")],
                &borsh::to_vec(&SwapFailed {
                    swap_id: args.swap_id,
                    reason: String::from("Timed out"),
                }).unwrap(),
            );

            // In production, would transfer refund back to user

            Ok(borsh::to_vec(&swap.plan.amount_in).unwrap())
        }

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

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

            let swap = get_pending_swap(args.swap_id);
            Ok(borsh::to_vec(&swap).unwrap())
        }

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

            let source = get_source(args.source_id);
            Ok(borsh::to_vec(&source).unwrap())
        }

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

            let token = get_token(&args.symbol);
            Ok(borsh::to_vec(&token).unwrap())
        }

        s if s == get_stats_sel => {
            #[derive(BorshSerialize)]
            struct Stats {
                total_volume: u64,
                source_count: u32,
                token_count: u32,
                swap_count: u64,
            }

            let stats = Stats {
                total_volume: get_total_volume(),
                source_count: get_source_count(),
                token_count: get_token_count(),
                swap_count: get_swap_count(),
            };

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

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

// =============================================================================
// QUOTE AND EXECUTION LOGIC
// =============================================================================

/// Get quotes from all active sources
fn get_all_quotes(token_in: &str, token_out: &str, amount_in: u64) -> Result<Vec<Quote>> {
    let source_count = get_source_count();
    let mut quotes = Vec::new();

    for i in 0..source_count {
        if let Some(source) = get_source(i) {
            if source.status != SourceStatus::Active {
                continue;
            }

            // Get quote from source (simplified - would call actual source)
            let quote = get_source_quote(&source, token_in, token_out, amount_in);
            quotes.push(quote);
        }
    }

    // Sort by output amount (best first)
    quotes.sort_by(|a, b| b.amount_out.cmp(&a.amount_out));

    Ok(quotes)
}

/// Get quote from a specific source
fn get_source_quote(source: &LiquiditySource, _token_in: &str, _token_out: &str, amount_in: u64) -> Quote {
    // In production, this would:
    // - For LocalAmm: call the DEX contract's quote function
    // - For IbcDex: query cached prices or send IBC query
    // - For CexApi: use off-chain oracle data

    // Simplified: estimate output based on source fee
    let fee = (amount_in as u128 * source.base_fee_bps as u128 / BPS_DENOMINATOR as u128) as u64;
    let amount_out = amount_in.saturating_sub(fee);

    // Add some variation based on source type for demonstration
    let adjusted_out = match source.source_type {
        SourceType::LocalAmm => amount_out,
        SourceType::IbcDex => amount_out + (amount_out / 1000), // IBC might have better prices
        SourceType::CexApi => amount_out + (amount_out / 500),  // CEX often has best prices
        SourceType::OrderBook => amount_out,
    };

    let estimated_time = match source.source_type {
        SourceType::LocalAmm => 2,
        SourceType::IbcDex => 30,
        SourceType::CexApi => 5,
        SourceType::OrderBook => 3,
    };

    Quote {
        source_id: source.id,
        source_name: source.name.clone(),
        amount_in,
        amount_out: adjusted_out,
        price_impact_bps: 10, // Simplified
        fee,
        available: true,
        estimated_time,
    }
}

/// Execute local swap (calls DEX contract)
fn execute_local_swap(plan: &SwapPlan) -> Result<u64> {
    // In production, this would:
    // 1. Call each source's swap function with the allocated amount
    // 2. Aggregate the outputs
    // 3. Handle any failures

    // Simplified: return expected output
    Ok(plan.total_expected_output)
}
