use anyhow::Result;
use solana_sdk::signature::{Keypair, Signer};
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
use solana_streamer_sdk::streaming::event_parser::common::types::EventType;
use solana_streamer_sdk::streaming::event_parser::Protocol;
use solana_streamer_sdk::streaming::yellowstone_grpc::{
    AccountFilter, TransactionFilter, YellowstoneGrpc,
};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tokio::time::sleep;

const PUMPFUN_PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
const RAYDIUM_CPMM_PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";

const GRPC_ENDPOINT: &str = "https://solana-yellowstone-grpc.publicnode.com:443";
const API_KEY: Option<&str> = None;
const MONITORING_DURATION_SECS: u64 = 10;

/// Demonstrates dynamic subscription updates and filter changes in real-time
#[tokio::main]
async fn main() -> Result<()> {
    println!("Connecting to Yellowstone gRPC at {}", GRPC_ENDPOINT);
    let client =
        Arc::new(YellowstoneGrpc::new(GRPC_ENDPOINT.to_string(), API_KEY.map(|s| s.to_string()))?);

    let event_counter = Arc::new(AtomicU64::new(0));
    let counter = event_counter.clone();

    let callback = move |event: solana_streamer_sdk::streaming::event_parser::DexEvent| {
        let count = counter.fetch_add(1, Ordering::Relaxed);

        let protocol = match event.metadata().event_type {
            EventType::PumpFunBuy | EventType::PumpFunSell => "PumpFun",
            EventType::RaydiumCpmmSwapBaseInput | EventType::RaydiumCpmmSwapBaseOutput => {
                "RaydiumCpmm"
            }
            _ => "Unknown",
        };

        println!("Event #{}: {:11} - {:.8}...", count + 1, protocol, event.metadata().signature);
    };

    println!("\n=== Phase 1: PumpFun only ===");
    let pumpfun_filter = TransactionFilter {
        account_include: vec![PUMPFUN_PROGRAM_ID.to_string()],
        account_exclude: vec![],
        account_required: vec![],
    };

    let account_filter = AccountFilter { account: vec![], owner: vec![], filters: vec![] };
    let trade_event_filter = EventTypeFilter::include_only(vec![
        EventType::PumpFunBuy,
        EventType::PumpFunSell,
        EventType::RaydiumCpmmSwapBaseInput,
        EventType::RaydiumCpmmSwapBaseOutput,
    ]);

    if let Err(e) = client
        .subscribe_events_immediate(
            vec![Protocol::PumpFun, Protocol::RaydiumCpmm],
            None,
            vec![pumpfun_filter],
            vec![account_filter],
            Some(trade_event_filter),
            None,
            callback,
        )
        .await
    {
        println!("Failed to create subscription: {}", e);
        return Ok(());
    }

    println!(
        "Subscribed to PumpFun transactions with trade event filters, monitoring for {}s...",
        MONITORING_DURATION_SECS
    );
    sleep(Duration::from_secs(MONITORING_DURATION_SECS)).await;
    let phase1_count = event_counter.load(Ordering::Relaxed);
    println!("Phase 1: {} events", phase1_count);

    println!("\n=== Phase 2: PumpFun + RaydiumCpmm ===");
    let multi_protocol_filter = TransactionFilter {
        account_include: vec![PUMPFUN_PROGRAM_ID.to_string(), RAYDIUM_CPMM_PROGRAM_ID.to_string()],
        account_exclude: vec![],
        account_required: vec![],
    };

    if let Err(e) = client
        .update_subscription(
            vec![multi_protocol_filter],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
        )
        .await
    {
        println!("Failed to update subscription: {}", e);
        return Ok(());
    }

    println!(
        "Updated to PumpFun + RaydiumCpmm transactions, monitoring for {}s...",
        MONITORING_DURATION_SECS
    );
    sleep(Duration::from_secs(MONITORING_DURATION_SECS)).await;
    let phase2_count = event_counter.load(Ordering::Relaxed);
    println!("Phase 2: {} events", phase2_count - phase1_count);

    println!("\n=== Phase 3: RaydiumCpmm only ===");
    let raydium_cpmm_filter = TransactionFilter {
        account_include: vec![RAYDIUM_CPMM_PROGRAM_ID.to_string()],
        account_exclude: vec![],
        account_required: vec![],
    };

    if let Err(e) = client
        .update_subscription(
            vec![raydium_cpmm_filter],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
        )
        .await
    {
        println!("Failed to update subscription: {}", e);
        return Ok(());
    }

    sleep(Duration::from_secs(MONITORING_DURATION_SECS)).await;
    println!(
        "Updated to RaydiumCpmm transactions only, monitoring for {}s...",
        MONITORING_DURATION_SECS
    );
    let phase3_count = event_counter.load(Ordering::Relaxed);
    println!("Phase 3: {} events", phase3_count - phase2_count);

    println!("\n=== Phase 4: Back to PumpFun only ===");
    let pumpfun_only_filter = TransactionFilter {
        account_include: vec![PUMPFUN_PROGRAM_ID.to_string()],
        account_exclude: vec![],
        account_required: vec![],
    };

    if let Err(e) = client
        .update_subscription(
            vec![pumpfun_only_filter],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
        )
        .await
    {
        println!("Failed to update subscription: {}", e);
        return Ok(());
    }

    sleep(Duration::from_secs(MONITORING_DURATION_SECS)).await;
    println!(
        "Updated to PumpFun transactions only, monitoring for {}s...",
        MONITORING_DURATION_SECS
    );
    let phase4_count = event_counter.load(Ordering::Relaxed);
    println!("Phase 4: {} events", phase4_count - phase3_count);

    println!("\n=== Phase 5: All events ===");
    let empty_filter = TransactionFilter {
        account_include: vec![],
        account_exclude: vec![],
        account_required: vec![],
    };

    if let Err(e) = client
        .update_subscription(
            vec![empty_filter],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
        )
        .await
    {
        println!("Failed to update subscription: {}", e);
        return Ok(());
    }

    sleep(Duration::from_secs(MONITORING_DURATION_SECS)).await;
    println!(
        "Updated to all transactions (no filters), monitoring for {}s...",
        MONITORING_DURATION_SECS
    );
    let phase5_count = event_counter.load(Ordering::Relaxed);
    println!("Phase 5: {} events", phase5_count - phase4_count);

    println!("\n=== Phase 6: Silence ===");

    let random_keypair_1 = Keypair::new();
    let random_keypair_2 = Keypair::new();
    let random_pubkey_1 = random_keypair_1.pubkey();
    let random_pubkey_2 = random_keypair_2.pubkey();

    let silence_filter = TransactionFilter {
        account_include: vec![],
        account_exclude: vec![],
        account_required: vec![random_pubkey_1.to_string(), random_pubkey_2.to_string()],
    };

    if let Err(e) = client
        .update_subscription(
            vec![silence_filter],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
        )
        .await
    {
        println!("Failed to update subscription: {}", e);
        return Ok(());
    }

    println!("Updated to random addresses (expecting silence), monitoring for 3s...");
    let before_silence = event_counter.load(Ordering::Relaxed);
    let start_time = Instant::now();
    let last_event_time = Arc::new(Mutex::new(start_time));
    let last_event_time_clone = last_event_time.clone();

    let mut last_count = before_silence;
    for _ in 0..6 {
        sleep(Duration::from_millis(500)).await;
        let current_count = event_counter.load(Ordering::Relaxed);
        if current_count > last_count {
            if let Ok(mut time) = last_event_time_clone.lock() {
                *time = Instant::now();
            }
            last_count = current_count;
        }
    }

    let final_count = event_counter.load(Ordering::Relaxed);
    let events_during_silence = final_count - before_silence;

    if events_during_silence == 0 {
        println!("Phase 6: 0 events (immediate filter application)");
    } else if let Ok(last_time) = last_event_time.lock() {
        let propagation_time = last_time.duration_since(start_time);
        println!(
            "Phase 6: {} events during propagation, filter took {}ms",
            events_during_silence,
            propagation_time.as_millis()
        );
    }

    println!("\n=== Phase 7: Shutdown ===");

    let shutdown_client =
        Arc::new(YellowstoneGrpc::new(GRPC_ENDPOINT.to_string(), API_KEY.map(|s| s.to_string()))?);

    let shutdown_event_counter = Arc::new(AtomicU64::new(0));
    let shutdown_counter = shutdown_event_counter.clone();
    let shutdown_callback =
        move |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {
            shutdown_counter.fetch_add(1, Ordering::Relaxed);
        };

    if let Err(e) = shutdown_client
        .subscribe_events_immediate(
            vec![Protocol::PumpFun, Protocol::RaydiumCpmm],
            None,
            vec![TransactionFilter {
                account_include: vec![],
                account_exclude: vec![],
                account_required: vec![],
            }],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
            None,
            None,
            shutdown_callback,
        )
        .await
    {
        println!("Failed to subscribe shutdown client: {}", e);
        return Ok(());
    }

    sleep(Duration::from_millis(1000)).await;
    let pre_stop_count = shutdown_event_counter.load(Ordering::Relaxed);
    println!("Received {} events before stop", pre_stop_count);

    let stop_time = Instant::now();
    shutdown_client.stop().await;
    let shutdown_duration = stop_time.elapsed();
    println!("stop() completed in {:.1}ms", shutdown_duration.as_millis());

    let post_stop_count = shutdown_event_counter.load(Ordering::Relaxed);
    let during_stop = post_stop_count - pre_stop_count;
    if during_stop > 0 {
        println!("  {} events received during stop()", during_stop);
    }

    let last_event_time = Arc::new(Mutex::new(stop_time));
    let last_event_time_clone = last_event_time.clone();
    let mut last_count = post_stop_count;

    for _ in 0..20 {
        sleep(Duration::from_millis(100)).await;
        let current_count = shutdown_event_counter.load(Ordering::Relaxed);
        if current_count > last_count {
            if let Ok(mut time) = last_event_time_clone.lock() {
                *time = Instant::now();
            }
            last_count = current_count;
        }
    }

    let final_count = shutdown_event_counter.load(Ordering::Relaxed);
    let after_stop = final_count - post_stop_count;

    if after_stop == 0 {
        println!("Phase 7: Clean shutdown - no events after stop()");
    } else if let Ok(last_time) = last_event_time.lock() {
        let post_stop_duration = last_time.duration_since(stop_time);
        let silence_duration = Instant::now().duration_since(*last_time);
        println!(
            "Phase 7: {} events arrived up to {}ms after stop(), then silent for {}ms",
            after_stop,
            post_stop_duration.as_millis(),
            silence_duration.as_millis()
        );
    }

    println!("\n=== Subscription enforcement ===");

    let test_callback = |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {};

    match client
        .subscribe_events_immediate(
            vec![Protocol::RaydiumCpmm],
            None,
            vec![TransactionFilter {
                account_include: vec![RAYDIUM_CPMM_PROGRAM_ID.to_string()],
                account_exclude: vec![],
                account_required: vec![],
            }],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
            None,
            None,
            test_callback,
        )
        .await
    {
        Ok(_) => println!("ERROR: Same client created second subscription"),
        Err(e) if e.to_string().contains("Already subscribed") => {
            println!("✓ Single subscription enforcement working");
        }
        Err(e) => println!("Unexpected error: {}", e),
    }

    let client2 =
        Arc::new(YellowstoneGrpc::new(GRPC_ENDPOINT.to_string(), API_KEY.map(|s| s.to_string()))?);

    let client2_counter = Arc::new(AtomicU64::new(0));
    let counter2 = client2_counter.clone();
    let client2_callback = move |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {
        counter2.fetch_add(1, Ordering::Relaxed);
    };

    match client2
        .subscribe_events_immediate(
            vec![Protocol::RaydiumCpmm],
            None,
            vec![TransactionFilter {
                account_include: vec![RAYDIUM_CPMM_PROGRAM_ID.to_string()],
                account_exclude: vec![],
                account_required: vec![],
            }],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
            None,
            None,
            client2_callback,
        )
        .await
    {
        Ok(_) => {
            sleep(Duration::from_millis(500)).await;
            let count = client2_counter.load(Ordering::Relaxed);
            println!("✓ Second client: {} events", count);
            client2.stop().await;
        }
        Err(e) => println!("ERROR: Second client failed: {}", e),
    }

    println!("\n=== Advanced subscription enforcement ===");

    let test_callback_advanced =
        |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {};

    let client3 =
        Arc::new(YellowstoneGrpc::new(GRPC_ENDPOINT.to_string(), API_KEY.map(|s| s.to_string()))?);

    // First subscription should succeed
    match client3
        .subscribe_events_immediate(
            vec![Protocol::RaydiumCpmm],
            None,
            vec![TransactionFilter {
                account_include: vec![RAYDIUM_CPMM_PROGRAM_ID.to_string()],
                account_exclude: vec![],
                account_required: vec![],
            }],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
            None,
            None,
            test_callback_advanced,
        )
        .await
    {
        Ok(_) => {
            // Second subscription attempt on same client should fail
            match client3
                .subscribe_events_immediate(
                    vec![Protocol::RaydiumCpmm],
                    None,
                    vec![TransactionFilter {
                        account_include: vec![RAYDIUM_CPMM_PROGRAM_ID.to_string()],
                        account_exclude: vec![],
                        account_required: vec![],
                    }],
                    vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
                    None,
                    None,
                    |_| {},
                )
                .await
            {
                Ok(_) => println!("ERROR: Same client created second advanced subscription"),
                Err(e) if e.to_string().contains("Already subscribed") => {
                    println!("✓ Advanced single subscription enforcement working");
                }
                Err(e) => println!("Unexpected error: {}", e),
            }
        }
        Err(e) => println!("ERROR: First advanced subscription failed: {}", e),
    }

    // Test that a second client can subscribe using advanced method
    let client4 =
        Arc::new(YellowstoneGrpc::new(GRPC_ENDPOINT.to_string(), API_KEY.map(|s| s.to_string()))?);

    let client4_counter = Arc::new(AtomicU64::new(0));
    let counter4 = client4_counter.clone();
    let client4_callback = move |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {
        counter4.fetch_add(1, Ordering::Relaxed);
    };

    match client4
        .subscribe_events_immediate(
            vec![Protocol::RaydiumCpmm],
            None,
            vec![TransactionFilter {
                account_include: vec![RAYDIUM_CPMM_PROGRAM_ID.to_string()],
                account_exclude: vec![],
                account_required: vec![],
            }],
            vec![AccountFilter { account: vec![], owner: vec![], filters: vec![] }],
            None,
            None,
            client4_callback,
        )
        .await
    {
        Ok(_) => {
            sleep(Duration::from_millis(500)).await;
            let count = client4_counter.load(Ordering::Relaxed);
            println!("✓ Second client (advanced): {} events", count);
            client4.stop().await;
        }
        Err(e) => println!("ERROR: Second client (advanced) failed: {}", e),
    }

    client3.stop().await;

    client.stop().await;

    Ok(())
}
