use super::RpcHandler;
use crate::{node_message, nostr_discovery, peers};
use crate::peers::KnownPeer;
use anyhow::Result;

impl RpcHandler {
    pub(super) async fn handle_node_add_peer(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let onion = params
            .get("onion")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing onion"))?;
        let pubkey = params
            .get("pubkey")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing pubkey"))?;
        let name = params.get("name").and_then(|v| v.as_str()).map(String::from);

        let peer = KnownPeer {
            onion: onion.to_string(),
            pubkey: pubkey.to_string(),
            name,
            added_at: Some(chrono::Utc::now().to_rfc3339()),
        };
        let peers = peers::add_peer(&self.config.data_dir, peer).await?;
        Ok(serde_json::json!({ "peers": peers }))
    }

    pub(super) async fn handle_node_list_peers(&self) -> Result<serde_json::Value> {
        let peers = peers::load_peers(&self.config.data_dir).await?;
        Ok(serde_json::json!({ "peers": peers }))
    }

    pub(super) async fn handle_node_remove_peer(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let pubkey = params
            .get("pubkey")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing pubkey"))?;
        let peers = peers::remove_peer(&self.config.data_dir, pubkey).await?;
        Ok(serde_json::json!({ "peers": peers }))
    }

    pub(super) async fn handle_node_send_message(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let onion = params
            .get("onion")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing onion"))?;
        let message = params
            .get("message")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing message"))?;

        // Validate onion is a known peer to prevent SSRF to arbitrary Tor destinations
        let known_peers = peers::load_peers(&self.config.data_dir).await?;
        let is_known = known_peers.iter().any(|p| {
            p.onion == onion || p.onion == format!("{}.onion", onion)
                || format!("{}.onion", p.onion) == onion
        });
        if !is_known {
            return Err(anyhow::anyhow!(
                "Onion address not in known peers list. Add the peer first."
            ));
        }

        let (data, _) = self.state_manager.get_snapshot().await;
        let pubkey = data.server_info.pubkey.clone();
        node_message::send_to_peer(onion, &pubkey, message).await?;
        Ok(serde_json::json!({ "ok": true, "sent_to": onion }))
    }

    pub(super) async fn handle_node_check_peer(
        &self,
        params: Option<serde_json::Value>,
    ) -> Result<serde_json::Value> {
        let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
        let onion = params
            .get("onion")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing onion"))?;
        let reachable = node_message::check_peer_reachable(onion).await.unwrap_or(false);
        Ok(serde_json::json!({ "onion": onion, "reachable": reachable }))
    }

    pub(super) async fn handle_node_messages_received(&self) -> Result<serde_json::Value> {
        let messages = node_message::get_received();
        Ok(serde_json::json!({ "messages": messages }))
    }

    pub(super) async fn handle_node_nostr_discover(&self) -> Result<serde_json::Value> {
        let identity_dir = self.config.data_dir.join("identity");
        let nodes = nostr_discovery::discover_archipelago_nodes(
            &identity_dir,
            &self.config.nostr_relays,
            self.config.nostr_tor_proxy.as_deref(),
        )
        .await?;
        Ok(serde_json::json!({ "nodes": nodes }))
    }
}
