//! Rhai wrappers for network module functions
//!
//! This module provides Rhai wrappers for network connectivity functions.
//! All functions are prefixed with `net_` to avoid naming conflicts.
//!
//! ## TCP Functions
//! - `net_tcp_check(host, port)` - Check if TCP port is open
//! - `net_tcp_ping(host)` - Ping a host using ICMP
//! - `port_check_available(port)` - Check if a port is available for binding
//!
//! ## HTTP Functions
//! - `net_http_check(url)` - Check if HTTP URL is reachable
//! - `net_http_status(url)` - Get HTTP status code
//! - `http_ping(url)` - Check if URL is reachable (returns bool)
//! - `http_wait(url, timeout)` - Wait for URL to become available
//!
//! For SSH functions, see the [`crate::ssh::rhai`] module.

use super::{http, tcp};
use rhai::{Engine, EvalAltResult, Position};

/// Convert HTTP and TCP results to Rhai errors
fn convert_anyhow_error(e: anyhow::Error) -> Box<EvalAltResult> {
    Box::new(EvalAltResult::ErrorRuntime(
        e.to_string().into(),
        Position::NONE,
    ))
}

/// Register network module functions with the Rhai engine
pub fn register_net_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    // TCP functions (prefixed with net_)
    engine.register_fn("net_tcp_check", net_tcp_check);
    engine.register_fn("net_tcp_ping", net_tcp_ping);

    // HTTP functions (prefixed with net_)
    engine.register_fn("net_http_check", net_http_check);
    engine.register_fn("net_http_status", net_http_status);

    // New HTTP functions
    engine.register_fn("http_ping", http_ping);
    engine.register_fn("http_wait", http_wait);

    // New TCP function
    engine.register_fn("port_check_available", port_check_available);

    Ok(())
}

/// Check if a TCP port is open
pub fn net_tcp_check(host: &str, port: i64) -> bool {
    // Use std::net::TcpStream for synchronous connection test
    use std::net::{SocketAddr, TcpStream};
    use std::time::Duration;

    // Parse the address
    let addr_str = format!("{}:{}", host, port);
    if let Ok(socket_addr) = addr_str.parse::<SocketAddr>() {
        // Try to connect with a timeout
        TcpStream::connect_timeout(&socket_addr, Duration::from_secs(5)).is_ok()
    } else {
        // Try to resolve hostname first
        match std::net::ToSocketAddrs::to_socket_addrs(&addr_str) {
            Ok(mut addrs) => {
                if let Some(addr) = addrs.next() {
                    TcpStream::connect_timeout(&addr, Duration::from_secs(5)).is_ok()
                } else {
                    false
                }
            }
            Err(_) => false,
        }
    }
}

/// Ping a host using ICMP (cross-platform)
pub fn net_tcp_ping(host: &str) -> bool {
    // Use system ping command for synchronous operation
    use std::process::Command;

    // Cross-platform ping implementation
    let mut cmd = Command::new("ping");

    #[cfg(target_os = "windows")]
    {
        cmd.arg("-n").arg("1").arg("-w").arg("5000"); // Windows: -n count, -w timeout in ms
    }

    #[cfg(not(target_os = "windows"))]
    {
        cmd.arg("-c").arg("1").arg("-W").arg("5"); // Unix: -c count, -W timeout in seconds
    }

    cmd.arg(host);

    match cmd.output() {
        Ok(output) => output.status.success(),
        Err(_) => false,
    }
}

/// Check if an HTTP URL is reachable
pub fn net_http_check(url: &str) -> bool {
    use std::time::Duration;

    let agent = ureq::Agent::new_with_config(
        ureq::Agent::config_builder()
            .timeout_global(Some(Duration::from_secs(10)))
            .build(),
    );

    match agent.head(url).call() {
        Ok(response) => response.status().is_success(),
        Err(_) => false,
    }
}

/// Get HTTP status code from a URL
pub fn net_http_status(url: &str) -> i64 {
    use std::time::Duration;

    let agent = ureq::Agent::new_with_config(
        ureq::Agent::config_builder()
            .timeout_global(Some(Duration::from_secs(10)))
            .build(),
    );

    match agent.head(url).call() {
        Ok(response) => response.status().as_u16() as i64,
        Err(_) => -1,
    }
}

//
// HTTP Utility Function Wrappers
//

/// Wrapper for http::http_ping
///
/// Check if an HTTP URL is reachable with a single GET request.
/// Returns true if the HTTP status is 2xx, false otherwise.
pub fn http_ping(url: &str) -> Result<bool, Box<EvalAltResult>> {
    http::http_ping(url).map_err(convert_anyhow_error)
}

/// Wrapper for http::http_wait
///
/// Wait for an HTTP endpoint to become available with retries.
/// Waits up to the specified timeout in seconds.
pub fn http_wait(url: &str, timeout_seconds: i64) -> Result<(), Box<EvalAltResult>> {
    http::http_wait(url, timeout_seconds).map_err(convert_anyhow_error)
}

//
// TCP Utility Function Wrappers
//

/// Wrapper for tcp::port_check_available
///
/// Check if a port is available for binding on localhost.
/// Returns true if the port is available, false if in use.
pub fn port_check_available(port: i64) -> Result<bool, Box<EvalAltResult>> {
    tcp::port_check_available(port).map_err(convert_anyhow_error)
}
