pub mod activity;
pub mod blob;
pub mod connection;
pub mod content;
pub mod crypto;
pub mod http;
pub mod network;
pub mod node;
pub mod protocol;
pub mod storage;
pub mod stun;
pub mod types;
pub mod upnp;
pub mod web;

// Re-export iroh types needed by consumers
pub use iroh::{EndpointAddr, EndpointId};

use types::NodeId;

/// Parse a connect string "nodeid_hex@ip:port" or "nodeid_hex@host:port" or bare "nodeid_hex"
/// into (NodeId, EndpointAddr). Supports DNS hostnames via `ToSocketAddrs`.
/// Shared utility used by CLI, Tauri, and bootstrap.
pub fn parse_connect_string(s: &str) -> anyhow::Result<(NodeId, EndpointAddr)> {
    use std::net::ToSocketAddrs;
    if let Some((id_hex, addr_str)) = s.split_once('@') {
        let nid = parse_node_id_hex(id_hex)?;
        let endpoint_id = EndpointId::from_bytes(&nid)?;
        let all_addrs: Vec<std::net::SocketAddr> = addr_str
            .to_socket_addrs()?
            .collect();
        if all_addrs.is_empty() {
            anyhow::bail!("could not resolve address: {}", addr_str);
        }
        let mut addr = EndpointAddr::from(endpoint_id);
        for sock_addr in all_addrs {
            addr = addr.with_ip_addr(sock_addr);
        }
        Ok((nid, addr))
    } else {
        let nid = parse_node_id_hex(s)?;
        let endpoint_id = EndpointId::from_bytes(&nid)?;
        Ok((nid, EndpointAddr::from(endpoint_id)))
    }
}

/// Parse a hex-encoded node ID string into NodeId bytes.
pub fn parse_node_id_hex(hex_str: &str) -> anyhow::Result<NodeId> {
    let bytes = hex::decode(hex_str)?;
    let id: NodeId = bytes
        .try_into()
        .map_err(|v: Vec<u8>| anyhow::anyhow!("expected 32 bytes, got {}", v.len()))?;
    Ok(id)
}
