use anyhow::Result;
use comfy_table::{Table, ContentArrangement};
use std::collections::HashMap;
use crate::types::{Node, Nodes};

pub async fn parse_nodes(nodes: &[String], verbose: bool) -> Result<Nodes> {
    if verbose { println!("\ngathering node data\n"); }
    let mut parsed = HashMap::new();
    for s in nodes {
        let node = parse_node(s).await?;
        parsed.insert(node.name.clone(), node);
    }
    if verbose { print_nodes_table(&parsed); }
    Ok(parsed)
}

async fn parse_node(node: &str) -> Result<Node> {
    let prefixes = ["http://", "https://"];
    let has_name = node.contains('=') && !prefixes.iter().any(|p| node.starts_with(p));
    let (name, mut url) = if has_name {
        let p: Vec<&str> = node.splitn(2, '=').collect();
        (p[0].to_string(), p[1].to_string())
    } else {
        (node.split('?').next().unwrap_or(node).to_string(), node.to_string())
    };

    if !prefixes.iter().any(|p| url.starts_with(p)) {
        let host = url.split(':').next().unwrap_or("");
        if host.starts_with("localhost") || host.split('.').all(|p| p.parse::<u8>().is_ok()) {
            url = format!("http://{}", url);
        } else {
            url = format!("https://{}", url);
        }
    }

    let (client_version, network) = (get_version(&url).await, detect_network(&url).await);
    Ok(Node { name, url, remote: None, client_version, network })
}

async fn get_version(url: &str) -> Option<String> {
    let payload = serde_json::json!({"jsonrpc": "2.0", "id": 1, "method": "getVersion", "params": []});
    let resp = reqwest::Client::new().post(url).json(&payload).send().await.ok()?;
    let json: serde_json::Value = resp.json().await.ok()?;
    json.get("result")?.get("solana-core")?.as_str().map(|s| format!("solana/{}", s))
}

async fn detect_network(url: &str) -> Option<String> {
    if url.contains("mainnet") { return Some("mainnet-beta".to_string()); }
    if url.contains("testnet") { return Some("testnet".to_string()); }
    if url.contains("devnet") { return Some("devnet".to_string()); }

    let payload = serde_json::json!({"jsonrpc": "2.0", "id": 1, "method": "getGenesisHash", "params": []});
    let resp = reqwest::Client::new().post(url).json(&payload).send().await.ok()?;
    let json: serde_json::Value = resp.json().await.ok()?;
    match json.get("result")?.as_str()? {
        "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d" => Some("mainnet-beta".to_string()),
        "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY" => Some("testnet".to_string()),
        "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG" => Some("devnet".to_string()),
        _ => None,
    }
}

pub fn print_nodes_table(nodes: &Nodes) {
    let mut table = Table::new();
    table.set_content_arrangement(ContentArrangement::Dynamic);
    table.set_header(vec!["Node", "URL", "Version", "Network"]);
    for node in nodes.values() {
        table.add_row(vec![
            &node.name,
            &node.url,
            node.client_version.as_deref().unwrap_or("-"),
            node.network.as_deref().unwrap_or("-"),
        ]);
    }
    println!("{table}");
}
