use anyhow::{Result, Context};
use colored::Colorize;
use futures_util::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::collections::HashSet;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use tokio::time::{sleep, Duration, interval};
use tokio_tungstenite::{connect_async, tungstenite::Message};
use toml;

use super::auth::{load_config, load_config_with_refresh, AuthConfig};

// Load collection config with priority: env vars > project config.toml > defaults
fn load_collection_config(project_dir: Option<&Path>) -> Result<(String, String)> {
    // 1. Check environment variables first (highest priority)
    if let (Ok(comment), Ok(user)) = (
        std::env::var("AILOG_COLLECTION_COMMENT"),
        std::env::var("AILOG_COLLECTION_USER")
    ) {
        println!("{}", "📂 Using collection config from environment variables".cyan());
        return Ok((comment, user));
    }

    // 2. Try to load from project config.toml (second priority)
    if let Some(project_path) = project_dir {
        match load_collection_config_from_project(project_path) {
            Ok(config) => {
                println!("{}", format!("📂 Using collection config from: {}", project_path.display()).cyan());
                return Ok(config);
            }
            Err(e) => {
                println!("{}", format!("⚠️  Failed to load project config: {}", e).yellow());
                println!("{}", "📂 Falling back to default collections".cyan());
            }
        }
    }

    // 3. Use defaults (lowest priority)
    println!("{}", "📂 Using default collection configuration".cyan());
    Ok(("ai.syui.log".to_string(), "ai.syui.log.user".to_string()))
}

// Load collection config from project's config.toml
fn load_collection_config_from_project(project_dir: &Path) -> Result<(String, String)> {
    let config_path = project_dir.join("config.toml");
    if !config_path.exists() {
        return Err(anyhow::anyhow!("config.toml not found in {}", project_dir.display()));
    }

    let config_content = fs::read_to_string(&config_path)
        .with_context(|| format!("Failed to read config.toml from {}", config_path.display()))?;
    
    let config: toml::Value = config_content.parse()
        .with_context(|| "Failed to parse config.toml")?;

    let oauth_config = config.get("oauth")
        .and_then(|v| v.as_table())
        .ok_or_else(|| anyhow::anyhow!("No [oauth] section found in config.toml"))?;

    let collection_comment = oauth_config.get("collection_comment")
        .and_then(|v| v.as_str())
        .unwrap_or("ai.syui.log")
        .to_string();

    let collection_user = oauth_config.get("collection_user")
        .and_then(|v| v.as_str())
        .unwrap_or("ai.syui.log.user")
        .to_string();

    Ok((collection_comment, collection_user))
}

#[derive(Debug, Serialize, Deserialize)]
struct JetstreamMessage {
    collection: Option<String>,
    commit: Option<JetstreamCommit>,
    did: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct JetstreamCommit {
    operation: Option<String>,
    uri: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct UserRecord {
    did: String,
    handle: String,
    pds: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct UserListRecord {
    #[serde(rename = "$type")]
    record_type: String,
    users: Vec<UserRecord>,
    #[serde(rename = "createdAt")]
    created_at: String,
    #[serde(rename = "updatedBy")]
    updated_by: UserInfo,
    metadata: Option<Value>,
}

#[derive(Debug, Serialize, Deserialize)]
struct UserInfo {
    did: String,
    handle: String,
}

fn get_pid_file() -> Result<PathBuf> {
    let home = std::env::var("HOME").context("HOME environment variable not set")?;
    let pid_dir = PathBuf::from(home).join(".config").join("syui").join("ai").join("log");
    fs::create_dir_all(&pid_dir)?;
    Ok(pid_dir.join("stream.pid"))
}

pub async fn start(project_dir: Option<PathBuf>, daemon: bool) -> Result<()> {
    let mut config = load_config_with_refresh().await?;
    
    // Load collection config with priority: env vars > project config > defaults
    let (collection_comment, collection_user) = load_collection_config(project_dir.as_deref())?;
    
    // Update config with loaded collections
    config.collections.comment = collection_comment.clone();
    config.collections.user = collection_user;
    config.jetstream.collections = vec![collection_comment];
    
    let pid_file = get_pid_file()?;
    
    // Check if already running
    if pid_file.exists() {
        let pid = fs::read_to_string(&pid_file)?;
        println!("{}", format!("⚠️  Stream monitor already running (PID: {})", pid.trim()).yellow());
        println!("Use 'ailog stream stop' to stop it first.");
        return Ok(());
    }
    
    if daemon {
        println!("{}", "🚀 Starting stream monitor as daemon...".cyan());
        
        // Fork process for daemon mode
        let current_exe = std::env::current_exe()?;
        let mut args = vec!["stream".to_string(), "start".to_string()];
        
        // Add project_dir argument if provided
        if let Some(project_path) = &project_dir {
            args.push(project_path.to_string_lossy().to_string());
        }
        
        let child = Command::new(current_exe)
            .args(&args)
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()?;
        
        // Save PID
        fs::write(&pid_file, child.id().to_string())?;
        
        println!("{}", format!("✅ Stream monitor started as daemon (PID: {})", child.id()).green());
        println!("Use 'ailog stream status' to check status");
        println!("Use 'ailog stream stop' to stop monitoring");
        
        return Ok(());
    }
    
    // Save current process PID for non-daemon mode
    let pid = std::process::id();
    fs::write(&pid_file, pid.to_string())?;
    
    println!("{}", "🎯 Starting ATProto stream monitor...".cyan());
    println!("👤 Authenticated as: {}", config.admin.handle.green());
    println!("📡 Connecting to: {}", config.jetstream.url);
    println!("📂 Monitoring collections: {}", config.jetstream.collections.join(", "));
    println!();
    
    // Setup graceful shutdown
    let pid_file_clone = pid_file.clone();
    tokio::spawn(async move {
        tokio::signal::ctrl_c().await.ok();
        println!("\n{}", "🛑 Shutting down stream monitor...".yellow());
        let _ = fs::remove_file(&pid_file_clone);
        std::process::exit(0);
    });
    
    // Start monitoring
    let mut reconnect_attempts = 0;
    let max_reconnect_attempts = 10;
    let mut config = config; // Make config mutable for token refresh
    
    loop {
        match run_monitor(&mut config).await {
            Ok(_) => {
                println!("{}", "Monitor loop ended normally".blue());
                break;
            }
            Err(e) => {
                reconnect_attempts += 1;
                if reconnect_attempts <= max_reconnect_attempts {
                    let delay = std::cmp::min(5 * reconnect_attempts, 30);
                    println!("{}", format!("❌ Monitor error: {}", e).red());
                    
                    // Show debug information
                    if reconnect_attempts == 1 {
                        println!("{}", "🔍 Debug information:".yellow());
                        println!("   - Jetstream URL: {}", config.jetstream.url);
                        println!("   - Collections: {:?}", config.jetstream.collections);
                        
                        // Test basic connectivity
                        println!("{}", "🧪 Testing basic connectivity...".cyan());
                        test_connectivity().await;
                    }
                    
                    println!("{}", format!("🔄 Reconnecting in {}s... ({}/{})", 
                             delay, reconnect_attempts, max_reconnect_attempts).yellow());
                    sleep(Duration::from_secs(delay)).await;
                } else {
                    println!("{}", "❌ Max reconnection attempts reached".red());
                    let _ = fs::remove_file(&pid_file);
                    return Err(e);
                }
            }
        }
    }
    
    let _ = fs::remove_file(&pid_file);
    Ok(())
}

async fn run_monitor(config: &mut AuthConfig) -> Result<()> {
    // Connect to Jetstream
    println!("{}", format!("🔗 Attempting to connect to: {}", config.jetstream.url).blue());
    
    // Create request with HTTP/1.1 headers to ensure WebSocket compatibility
    let request = tungstenite::http::Request::builder()
        .method("GET")
        .uri(&config.jetstream.url)
        .header("Host", config.jetstream.url.replace("wss://", "").replace("/subscribe", ""))
        .header("Upgrade", "websocket")
        .header("Connection", "Upgrade")
        .header("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
        .header("Sec-WebSocket-Version", "13")
        .body(())?;
    
    let (ws_stream, response) = connect_async(request).await
        .with_context(|| format!("Failed to connect to Jetstream at {}", config.jetstream.url))?;
    
    println!("{}", format!("📡 WebSocket handshake status: {}", response.status()).blue());
    
    println!("{}", "✅ Connected to Jetstream".green());
    
    // Since Jetstream may not include custom collections, we'll use a hybrid approach:
    // 1. Keep WebSocket connection for any potential custom collection events
    // 2. Add periodic polling for ai.syui.log collection
    
    let (mut write, mut read) = ws_stream.split();
    
    // Subscribe to collections
    let subscribe_msg = json!({
        "wantedCollections": config.jetstream.collections
    });
    
    write.send(Message::Text(subscribe_msg.to_string())).await?;
    println!("{}", "📨 Subscribed to collections".blue());
    
    // Start periodic polling task
    let config_clone = config.clone();
    let polling_task = tokio::spawn(async move {
        poll_comments_periodically(config_clone).await
    });
    
    // Process WebSocket messages
    let ws_task = async {
        while let Some(msg) = read.next().await {
            match msg? {
                Message::Text(text) => {
                    // Filter out standard Bluesky collections for cleaner output
                    let should_debug = std::env::var("AILOG_DEBUG").is_ok();
                    let is_standard_collection = text.contains("app.bsky.feed.") || 
                                               text.contains("app.bsky.actor.") ||
                                               text.contains("app.bsky.graph.");
                    
                    // Only show debug for custom collections or when explicitly requested
                    if should_debug && (!is_standard_collection || std::env::var("AILOG_DEBUG_ALL").is_ok()) {
                        println!("{}", format!("🔍 Received: {}", text).blue());
                    }
                    
                    if let Err(e) = handle_message(&text, config).await {
                        println!("{}", format!("⚠️  Failed to handle message: {}", e).yellow());
                    }
                }
                Message::Close(_) => {
                    println!("{}", "🔌 WebSocket closed by server".yellow());
                    break;
                }
                _ => {}
            }
        }
        Ok::<(), anyhow::Error>(())
    };
    
    // Run both tasks concurrently
    tokio::select! {
        result = polling_task => {
            match result {
                Ok(Ok(_)) => println!("{}", "📊 Polling task completed".blue()),
                Ok(Err(e)) => println!("{}", format!("❌ Polling task error: {}", e).red()),
                Err(e) => println!("{}", format!("❌ Polling task panic: {}", e).red()),
            }
        }
        result = ws_task => {
            match result {
                Ok(_) => println!("{}", "📡 WebSocket task completed".blue()),
                Err(e) => println!("{}", format!("❌ WebSocket task error: {}", e).red()),
            }
        }
    }
    
    Ok(())
}

async fn handle_message(text: &str, config: &mut AuthConfig) -> Result<()> {
    let message: JetstreamMessage = serde_json::from_str(text)?;
    
    // Debug: Check all received collections (but filter standard ones)
    if let Some(collection) = &message.collection {
        let is_standard_collection = collection.starts_with("app.bsky.");
        
        if std::env::var("AILOG_DEBUG").is_ok() && (!is_standard_collection || std::env::var("AILOG_DEBUG_ALL").is_ok()) {
            println!("{}", format!("📂 Collection: {}", collection).cyan());
        }
        
        // Skip processing standard Bluesky collections
        if is_standard_collection {
            return Ok(());
        }
    }
    
    // Check if this is a comment creation
    if let (Some(collection), Some(commit), Some(did)) = 
        (&message.collection, &message.commit, &message.did) {
        
        if collection == &config.collections.comment && commit.operation.as_deref() == Some("create") {
            let unknown_uri = "unknown".to_string();
            let uri = commit.uri.as_ref().unwrap_or(&unknown_uri);
            
            println!("{}", "🆕 New comment detected!".green().bold());
            println!("   📝 URI: {}", uri);
            println!("   👤 Author DID: {}", did);
            
            // Resolve handle
            match resolve_handle(did).await {
                Ok(handle) => {
                    println!("   🏷️  Handle: {}", handle.cyan());
                    
                    // Update user list
                    if let Err(e) = update_user_list(config, did, &handle).await {
                        println!("{}", format!("   ⚠️  Failed to update user list: {}", e).yellow());
                    }
                }
                Err(e) => {
                    println!("{}", format!("   ⚠️  Failed to resolve handle: {}", e).yellow());
                }
            }
            
            println!();
        }
    }
    
    Ok(())
}

async fn resolve_handle(did: &str) -> Result<String> {
    let client = reqwest::Client::new();
    let url = format!("https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor={}", 
                     urlencoding::encode(did));
    
    let response = client.get(&url).send().await?;
    
    if !response.status().is_success() {
        return Err(anyhow::anyhow!("Failed to resolve handle: {}", response.status()));
    }
    
    let profile: Value = response.json().await?;
    let handle = profile["handle"].as_str()
        .ok_or_else(|| anyhow::anyhow!("Handle not found in profile response"))?;
    
    Ok(handle.to_string())
}

async fn update_user_list(config: &mut AuthConfig, did: &str, handle: &str) -> Result<()> {
    // Get current user list
    let current_users = get_current_user_list(config).await?;
    
    // Check if user already exists
    if current_users.iter().any(|u| u.did == did) {
        println!("   ℹ️  User already in list: {}", handle.blue());
        return Ok(());
    }
    
    println!("   ➕ Adding new user to list: {}", handle.green());
    
    // Detect PDS
    let pds = if handle.ends_with(".syu.is") {
        "https://syu.is"
    } else {
        "https://bsky.social"
    };
    
    // Add new user
    let new_user = UserRecord {
        did: did.to_string(),
        handle: handle.to_string(),
        pds: pds.to_string(),
    };
    
    let mut updated_users = current_users;
    updated_users.push(new_user);
    
    // Post updated user list
    post_user_list(config, &updated_users, json!({
        "reason": "auto_add_commenter",
        "trigger_did": did,
        "trigger_handle": handle
    })).await?;
    
    println!("{}", "   ✅ User list updated successfully".green());
    
    Ok(())
}

async fn get_current_user_list(config: &mut AuthConfig) -> Result<Vec<UserRecord>> {
    let client = reqwest::Client::new();
    let url = format!("{}/xrpc/com.atproto.repo.listRecords?repo={}&collection={}&limit=10",
                     config.admin.pds,
                     urlencoding::encode(&config.admin.did),
                     urlencoding::encode(&config.collections.user));
    
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", config.admin.access_jwt))
        .send()
        .await?;
    
    if !response.status().is_success() {
        if response.status().as_u16() == 401 {
            // Token expired, try to refresh
            if let Ok(_) = super::auth::load_config_with_refresh().await {
                // Retry with refreshed token  
                let refreshed_config = super::auth::load_config()?;
                *config = refreshed_config;
                return Box::pin(get_current_user_list(config)).await;
            }
        }
        return Ok(Vec::new());
    }
    
    let data: Value = response.json().await?;
    let empty_vec = vec![];
    let records = data["records"].as_array().unwrap_or(&empty_vec);
    
    if records.is_empty() {
        return Ok(Vec::new());
    }
    
    // Get the latest record
    let latest_record = &records[0];
    let empty_users = vec![];
    let users = latest_record["value"]["users"].as_array().unwrap_or(&empty_users);
    
    let mut user_list = Vec::new();
    for user in users {
        if let (Some(did), Some(handle), Some(pds)) = (
            user["did"].as_str(),
            user["handle"].as_str(),
            user["pds"].as_str(),
        ) {
            user_list.push(UserRecord {
                did: did.to_string(),
                handle: handle.to_string(),
                pds: pds.to_string(),
            });
        }
    }
    
    Ok(user_list)
}

async fn post_user_list(config: &mut AuthConfig, users: &[UserRecord], metadata: Value) -> Result<()> {
    let client = reqwest::Client::new();
    
    let now = chrono::Utc::now();
    // Extract short ID from DID (did:plc:xxx -> xxx) for rkey
    let short_did = config.admin.did
        .strip_prefix("did:plc:")
        .unwrap_or(&config.admin.did);
    let rkey = format!("{}-{}", short_did, now.format("%Y-%m-%dT%H-%M-%S-%3fZ").to_string().replace(".", "-"));
    
    let record = UserListRecord {
        record_type: config.collections.user.clone(),
        users: users.to_vec(),
        created_at: now.to_rfc3339(),
        updated_by: UserInfo {
            did: config.admin.did.clone(),
            handle: config.admin.handle.clone(),
        },
        metadata: Some(metadata.clone()),
    };
    
    let url = format!("{}/xrpc/com.atproto.repo.putRecord", config.admin.pds);
    
    let request_body = json!({
        "repo": config.admin.did,
        "collection": config.collections.user,
        "rkey": rkey,
        "record": record
    });
    
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", config.admin.access_jwt))
        .header("Content-Type", "application/json")
        .json(&request_body)
        .send()
        .await?;
    
    if !response.status().is_success() {
        let status = response.status();
        if status.as_u16() == 401 {
            // Token expired, try to refresh and retry
            if let Ok(_) = super::auth::load_config_with_refresh().await {
                let refreshed_config = super::auth::load_config()?;
                *config = refreshed_config;
                return Box::pin(post_user_list(config, users, metadata)).await;
            }
        }
        let error_text = response.text().await?;
        return Err(anyhow::anyhow!("Failed to post user list: {} - {}", status, error_text));
    }
    
    Ok(())
}

pub async fn stop() -> Result<()> {
    let pid_file = get_pid_file()?;
    
    if !pid_file.exists() {
        println!("{}", "ℹ️  Stream monitor is not running.".blue());
        return Ok(());
    }
    
    let pid_str = fs::read_to_string(&pid_file)?;
    let pid = pid_str.trim();
    
    println!("{}", format!("🛑 Stopping stream monitor (PID: {})...", pid).cyan());
    
    // Try to kill the process
    let output = Command::new("kill")
        .arg(pid)
        .output()?;
    
    if output.status.success() {
        // Wait a bit for the process to stop
        sleep(Duration::from_secs(2)).await;
        
        // Remove PID file
        fs::remove_file(&pid_file)?;
        
        println!("{}", "✅ Stream monitor stopped successfully".green());
    } else {
        println!("{}", format!("⚠️  Failed to stop process: {}", 
                 String::from_utf8_lossy(&output.stderr)).yellow());
        
        // Force remove PID file anyway
        fs::remove_file(&pid_file)?;
    }
    
    Ok(())
}

pub async fn status() -> Result<()> {
    let pid_file = get_pid_file()?;
    
    println!("{}", "📊 Stream Monitor Status".cyan().bold());
    println!("─────────────────────────");
    
    if !pid_file.exists() {
        println!("{}", "📴 Status: Not running".red());
        return Ok(());
    }
    
    let pid_str = fs::read_to_string(&pid_file)?;
    let pid = pid_str.trim();
    
    // Check if process is actually running
    let output = Command::new("ps")
        .args(&["-p", pid])
        .output()?;
    
    if output.status.success() {
        println!("{}", "✅ Status: Running".green());
        println!("🆔 PID: {}", pid);
        println!("📁 PID file: {}", pid_file.display());
        
        // Show config info
        match load_config() {
            Ok(config) => {
                println!("👤 Authenticated as: {}", config.admin.handle);
                println!("📡 Jetstream URL: {}", config.jetstream.url);
                println!("📂 Monitoring: {}", config.jetstream.collections.join(", "));
            }
            Err(_) => {
                println!("{}", "⚠️  No authentication config found".yellow());
            }
        }
    } else {
        println!("{}", "❌ Status: Process not found (stale PID file)".red());
        println!("🗑️  Removing stale PID file...");
        fs::remove_file(&pid_file)?;
    }
    
    Ok(())
}

async fn test_connectivity() {
    let endpoints = [
        "wss://jetstream2.us-east.bsky.network/subscribe",
        "wss://jetstream1.us-east.bsky.network/subscribe", 
        "wss://jetstream2.us-west.bsky.network/subscribe",
    ];
    
    for endpoint in &endpoints {
        print!("   Testing {}: ", endpoint);
        
        // Test basic HTTP connectivity first
        let http_url = endpoint.replace("wss://", "https://").replace("/subscribe", "");
        match reqwest::Client::new()
            .head(&http_url)
            .timeout(Duration::from_secs(5))
            .send()
            .await 
        {
            Ok(response) => {
                if response.status().as_u16() == 405 {
                    println!("{}", "✅ HTTP reachable".green());
                } else {
                    println!("{}", format!("⚠️  HTTP status: {}", response.status()).yellow());
                }
            }
            Err(e) => {
                println!("{}", format!("❌ HTTP failed: {}", e).red());
                continue;
            }
        }
        
        // Test WebSocket connectivity  
        print!("   WebSocket {}: ", endpoint);
        match tokio::time::timeout(Duration::from_secs(5), connect_async(*endpoint)).await {
            Ok(Ok(_)) => println!("{}", "✅ Connected".green()),
            Ok(Err(e)) => println!("{}", format!("❌ Failed: {}", e).red()),
            Err(_) => println!("{}", "❌ Timeout".red()),
        }
    }
}

async fn poll_comments_periodically(mut config: AuthConfig) -> Result<()> {
    println!("{}", "📊 Starting periodic comment polling...".cyan());
    
    let mut known_comments = HashSet::new();
    let mut interval = interval(Duration::from_secs(30)); // Poll every 30 seconds
    
    // Initial population of known comments
    if let Ok(comments) = get_recent_comments(&mut config).await {
        for comment in &comments {
            if let Some(uri) = comment.get("uri").and_then(|v| v.as_str()) {
                known_comments.insert(uri.to_string());
                if std::env::var("AILOG_DEBUG").is_ok() {
                    println!("{}", format!("🔍 Existing comment: {}", uri).blue());
                }
            }
        }
        println!("{}", format!("📝 Found {} existing comments", known_comments.len()).blue());
        
        // Debug: Show full response for first comment
        if std::env::var("AILOG_DEBUG").is_ok() && !comments.is_empty() {
            println!("{}", format!("🔍 Sample comment data: {}", serde_json::to_string_pretty(&comments[0]).unwrap_or_default()).yellow());
        }
    } else {
        println!("{}", "⚠️  Failed to get initial comments".yellow());
    }
    
    loop {
        interval.tick().await;
        
        if std::env::var("AILOG_DEBUG").is_ok() {
            println!("{}", "🔄 Polling for new comments...".cyan());
        }
        
        match get_recent_comments(&mut config).await {
            Ok(comments) => {
                if std::env::var("AILOG_DEBUG").is_ok() {
                    println!("{}", format!("📊 Retrieved {} comments from API", comments.len()).cyan());
                }
                
                for comment in comments {
                    if let (Some(uri), Some(value)) = (
                        comment.get("uri").and_then(|v| v.as_str()),
                        comment.get("value")
                    ) {
                        if !known_comments.contains(uri) {
                            // New comment detected
                            known_comments.insert(uri.to_string());
                            
                            if let Some(created_at) = value.get("createdAt").and_then(|v| v.as_str()) {
                                // Check if this comment is recent (within last 5 minutes)
                                if is_recent_comment(created_at) {
                                    println!("{}", "🆕 New comment detected via polling!".green().bold());
                                    println!("   📝 URI: {}", uri);
                                    
                                    // Extract author DID from URI
                                    if let Some(did) = extract_did_from_uri(uri) {
                                        println!("   👤 Author DID: {}", did);
                                        
                                        // Resolve handle and update user list
                                        match resolve_handle(&did).await {
                                            Ok(handle) => {
                                                println!("   🏷️  Handle: {}", handle.cyan());
                                                
                                                if let Err(e) = update_user_list(&mut config, &did, &handle).await {
                                                    println!("{}", format!("   ⚠️  Failed to update user list: {}", e).yellow());
                                                }
                                            }
                                            Err(e) => {
                                                println!("{}", format!("   ⚠️  Failed to resolve handle: {}", e).yellow());
                                            }
                                        }
                                    }
                                    
                                    println!();
                                }
                            }
                        }
                    }
                }
            }
            Err(e) => {
                println!("{}", format!("⚠️  Failed to poll comments: {}", e).yellow());
            }
        }
    }
}

async fn get_recent_comments(config: &mut AuthConfig) -> Result<Vec<Value>> {
    let client = reqwest::Client::new();
    let url = format!("{}/xrpc/com.atproto.repo.listRecords?repo={}&collection={}&limit=20",
                     config.admin.pds,
                     urlencoding::encode(&config.admin.did),
                     urlencoding::encode(&config.collections.comment));
    
    if std::env::var("AILOG_DEBUG").is_ok() {
        println!("{}", format!("🌐 API Request URL: {}", url).yellow());
    }
    
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", config.admin.access_jwt))
        .send()
        .await?;
    
    if !response.status().is_success() {
        if std::env::var("AILOG_DEBUG").is_ok() {
            println!("{}", format!("❌ API Error: {} {}", response.status(), response.status().canonical_reason().unwrap_or("Unknown")).red());
        }
        
        if response.status().as_u16() == 401 {
            // Token expired, try to refresh
            if let Ok(_) = super::auth::load_config_with_refresh().await {
                let refreshed_config = super::auth::load_config()?;
                *config = refreshed_config;
                return Box::pin(get_recent_comments(config)).await;
            }
        }
        return Err(anyhow::anyhow!("Failed to fetch comments: {}", response.status()));
    }
    
    let data: Value = response.json().await?;
    
    if std::env::var("AILOG_DEBUG").is_ok() {
        println!("{}", format!("📄 Raw API Response: {}", serde_json::to_string_pretty(&data).unwrap_or_default()).magenta());
    }
    
    let empty_vec = vec![];
    let records = data["records"].as_array().unwrap_or(&empty_vec);
    
    Ok(records.to_vec())
}

fn is_recent_comment(created_at: &str) -> bool {
    use chrono::{DateTime, Utc, Duration};
    
    if let Ok(comment_time) = DateTime::parse_from_rfc3339(created_at) {
        let now = Utc::now();
        let comment_utc = comment_time.with_timezone(&Utc);
        let diff = now.signed_duration_since(comment_utc);
        
        // Consider comments from the last 5 minutes as "recent"
        diff <= Duration::minutes(5) && diff >= Duration::zero()
    } else {
        false
    }
}

fn extract_did_from_uri(uri: &str) -> Option<String> {
    // URI format: at://did:plc:xxx/ai.syui.log/yyy
    if let Some(captures) = uri.strip_prefix("at://") {
        if let Some(end) = captures.find("/") {
            return Some(captures[..end].to_string());
        }
    }
    None
}

pub async fn test_api() -> Result<()> {
    println!("{}", "🧪 Testing API access to comments collection...".cyan().bold());
    
    let mut config = load_config_with_refresh().await?;
    
    println!("👤 Testing as: {}", config.admin.handle.green());
    println!("🌐 PDS: {}", config.admin.pds);
    println!("🆔 DID: {}", config.admin.did);
    println!();
    
    // Test API access
    match get_recent_comments(&mut config).await {
        Ok(comments) => {
            println!("{}", format!("✅ Successfully retrieved {} comments", comments.len()).green());
            
            if comments.is_empty() {
                println!("{}", format!("ℹ️  No comments found in {} collection", config.collections.comment).blue());
                println!("💡 Try posting a comment first using the web interface");
            } else {
                println!("{}", "📝 Comment details:".cyan());
                for (i, comment) in comments.iter().enumerate() {
                    println!("   {}. URI: {}", i + 1, 
                             comment.get("uri").and_then(|v| v.as_str()).unwrap_or("N/A"));
                    
                    if let Some(value) = comment.get("value") {
                        if let Some(created_at) = value.get("createdAt").and_then(|v| v.as_str()) {
                            println!("      Created: {}", created_at);
                        }
                        if let Some(text) = value.get("text").and_then(|v| v.as_str()) {
                            let preview = if text.len() > 50 {
                                format!("{}...", &text[..50])
                            } else {
                                text.to_string()
                            };
                            println!("      Text: {}", preview);
                        }
                    }
                    println!();
                }
            }
        }
        Err(e) => {
            println!("{}", format!("❌ API test failed: {}", e).red());
            return Err(e);
        }
    }
    
    Ok(())
}