use std::{
    process::Command,
    sync::Arc,
};

#[cfg(target_os = "macos")]
use log::warn;
use log::{
    debug,
    error,
    info,
};
use tokio::sync::Mutex;

use crate::error::HelperError;

pub struct NetworkConfigManager {
    lock: Arc<Mutex<()>>,
}

impl NetworkConfigManager {
    pub fn new() -> Result<Self, HelperError> {
        Ok(Self {
            lock: Arc::new(Mutex::new(())),
        })
    }

    pub async fn add_loopback_address(&self, address: &str) -> Result<(), HelperError> {
        debug!("Starting add_loopback_address for: {address}");

        let _guard = self.lock.lock().await;

        if let Err(e) = self.validate_loopback_address(address) {
            error!("Address validation failed: {e}");
            return Err(e);
        }

        debug!("Address validation successful for: {address}");

        #[cfg(target_os = "macos")]
        {
            debug!("Using macOS-specific implementation");
            let result = self.add_loopback_macos(address).await;
            debug!(
                "macOS implementation completed with result: {:?}",
                result.is_ok()
            );
            result
        }

        #[cfg(target_os = "linux")]
        {
            debug!("Using Linux-specific implementation");
            self.add_loopback_linux(address).await
        }

        #[cfg(target_os = "windows")]
        {
            debug!("Using Windows-specific implementation");
            self.add_loopback_windows(address).await
        }

        #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
        {
            error!("Unsupported platform");
            Err(HelperError::UnsupportedPlatform)
        }
    }

    pub async fn remove_loopback_address(&self, address: &str) -> Result<(), HelperError> {
        let _guard = self.lock.lock().await;

        self.validate_loopback_address(address)?;

        if !self.is_address_configured(address).await? {
            info!("Address already removed, returning success: {address}");
            return Ok(());
        }

        debug!("Address is configured, proceeding with removal: {address}");

        #[cfg(target_os = "macos")]
        {
            self.remove_loopback_macos(address).await
        }

        #[cfg(target_os = "linux")]
        {
            self.remove_loopback_linux(address).await
        }

        #[cfg(target_os = "windows")]
        {
            self.remove_loopback_windows(address).await
        }

        #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
        {
            Err(HelperError::UnsupportedPlatform)
        }
    }

    pub async fn list_loopback_addresses(&self) -> Result<Vec<String>, HelperError> {
        let _guard = self.lock.lock().await;

        #[cfg(target_os = "macos")]
        {
            self.list_loopback_macos().await
        }

        #[cfg(target_os = "linux")]
        {
            self.list_loopback_linux().await
        }

        #[cfg(target_os = "windows")]
        {
            self.list_loopback_windows().await
        }

        #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
        {
            Err(HelperError::UnsupportedPlatform)
        }
    }

    async fn is_address_configured(&self, address: &str) -> Result<bool, HelperError> {
        #[cfg(target_os = "macos")]
        {
            debug!("Checking if address is configured on macOS: {address}");

            let address_owned = address.to_string();
            let result = tokio::task::spawn_blocking(move || {
                debug!("Executing ifconfig command to check if address exists");

                let output = Command::new("/sbin/ifconfig").arg("lo0").output();

                match output {
                    Ok(output) => {
                        let output_str = String::from_utf8_lossy(&output.stdout);
                        let mut found = false;

                        for line in output_str.lines() {
                            if line.contains("inet ") && line.contains(&address_owned) {
                                found = true;
                                break;
                            }
                        }

                        debug!("Address {address_owned} found status: {found}");
                        Ok(found)
                    }
                    Err(e) => {
                        error!("Error checking interface: {e}");
                        Err(HelperError::NetworkConfig(format!(
                            "Failed to check if address is configured: {e}"
                        )))
                    }
                }
            })
            .await;

            match result {
                Ok(inner_result) => inner_result,
                Err(e) => {
                    error!("Task execution error: {e}");
                    Err(HelperError::NetworkConfig(format!(
                        "Task execution error: {e}"
                    )))
                }
            }
        }

        #[cfg(target_os = "linux")]
        {
            let addresses = self.list_loopback_addresses().await?;
            Ok(addresses.contains(&address.to_string()))
        }

        #[cfg(target_os = "windows")]
        {
            let addresses = self.list_loopback_addresses().await?;
            Ok(addresses.contains(&address.to_string()))
        }

        #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
        {
            Err(HelperError::UnsupportedPlatform)
        }
    }

    fn validate_loopback_address(&self, address: &str) -> Result<(), HelperError> {
        if !address.starts_with("127.") {
            return Err(HelperError::NetworkConfig(format!(
                "Invalid loopback address: {address}"
            )));
        }

        let parts: Vec<&str> = address.split('.').collect();
        if parts.len() != 4 {
            return Err(HelperError::NetworkConfig(format!(
                "Invalid IP address format: {address}"
            )));
        }

        for part in parts {
            if let Ok(_num) = part.parse::<u8>() {
                if part.starts_with('0') && part.len() > 1 {
                    return Err(HelperError::NetworkConfig(format!(
                        "Invalid IP address format (leading zeros): {address}"
                    )));
                }
            } else {
                return Err(HelperError::NetworkConfig(format!(
                    "Invalid IP address format (non-numeric): {address}"
                )));
            }
        }

        Ok(())
    }

    #[cfg(target_os = "macos")]
    async fn add_loopback_macos(&self, address: &str) -> Result<(), HelperError> {
        debug!("Adding loopback address on macOS: {address}");

        let address_owned = address.to_string();
        let result = tokio::task::spawn_blocking(move || {
            debug!("Executing ifconfig command to add loopback address");

            let output = Command::new("/sbin/ifconfig")
                .args(["lo0", "alias", &address_owned])
                .output();

            match output {
                Ok(output) => {
                    if output.status.success() {
                        info!("Successfully added loopback address: {address_owned}");
                        Ok(())
                    } else {
                        let error = String::from_utf8_lossy(&output.stderr);
                        error!("Failed to add loopback address: {error}");
                        Err(HelperError::NetworkConfig(format!(
                            "Failed to add loopback address: {error}"
                        )))
                    }
                }
                Err(e) => {
                    error!("Error executing ifconfig command: {e}");
                    Err(HelperError::NetworkConfig(format!(
                        "Failed to add loopback address: {e}"
                    )))
                }
            }
        })
        .await;

        match result {
            Ok(inner_result) => inner_result,
            Err(e) => {
                error!("Task execution error: {e}");
                Err(HelperError::NetworkConfig(format!(
                    "Task execution error: {e}"
                )))
            }
        }
    }

    #[cfg(target_os = "macos")]
    async fn remove_loopback_macos(&self, address: &str) -> Result<(), HelperError> {
        debug!("Removing loopback address on macOS: {address}");
        debug!("Thread ID: {:?}", std::thread::current().id());

        let address_owned = address.to_string();
        let result = tokio::task::spawn_blocking(move || {
            debug!("Executing ifconfig command to remove loopback address: {address_owned}");
            debug!("Blocking task thread ID: {:?}", std::thread::current().id());

            debug!("Directly calling ifconfig to remove loopback address");
            let output = Command::new("/sbin/ifconfig")
                .args(["lo0", "-alias", &address_owned])
                .output();

            match output {
                Ok(output) => {
                    let stdout = String::from_utf8_lossy(&output.stdout);
                    let stderr = String::from_utf8_lossy(&output.stderr);

                    debug!(
                        "ifconfig -alias result: success={}, stdout={}, stderr={}",
                        output.status.success(),
                        stdout,
                        stderr
                    );

                    if output.status.success() {
                        info!("Successfully removed loopback address: {address_owned}");
                        Ok(())
                    } else if stderr.contains("not found") || stderr.contains("No such process") {
                        info!("Address already removed, considering operation successful");
                        Ok(())
                    } else {
                        error!("Failed to remove loopback address, error: {stderr}");
                        Err(HelperError::NetworkConfig(format!(
                            "Failed to remove loopback address: {stderr}"
                        )))
                    }
                }
                Err(e) => {
                    error!("Error executing ifconfig command: {e}");
                    Err(HelperError::NetworkConfig(format!(
                        "Failed to remove loopback address: {e}"
                    )))
                }
            }
        })
        .await;

        match result {
            Ok(inner_result) => {
                debug!(
                    "Address removal task completed with result: {:?}",
                    inner_result.is_ok()
                );

                let verify_result = self.is_address_configured(address).await;
                match verify_result {
                    Ok(still_exists) => {
                        if !still_exists {
                            info!("Verified address was successfully removed: {address}");
                        } else {
                            warn!("Warning: Address still exists after removal attempt: {address}");
                        }
                    }
                    Err(e) => {
                        error!("Error verifying address removal: {e}");
                    }
                }

                inner_result
            }
            Err(e) => {
                error!("Task execution error: {e}");
                Err(HelperError::NetworkConfig(format!(
                    "Task execution error: {e}"
                )))
            }
        }
    }

    #[cfg(target_os = "macos")]
    async fn list_loopback_macos(&self) -> Result<Vec<String>, HelperError> {
        debug!("Listing loopback addresses on macOS");

        let result = tokio::task::spawn_blocking(move || {
            debug!("Executing ifconfig command to list loopback addresses");

            let output = Command::new("/sbin/ifconfig").arg("lo0").output();

            match output {
                Ok(output) => {
                    if output.status.success() {
                        let output_str = String::from_utf8_lossy(&output.stdout);
                        let mut addresses = Vec::new();

                        for line in output_str.lines() {
                            if line.contains("inet ") {
                                let parts: Vec<&str> = line.split_whitespace().collect();
                                if parts.len() >= 2 {
                                    let addr = parts[1];
                                    if addr.starts_with("127.") {
                                        addresses.push(addr.to_string());
                                    }
                                }
                            }
                        }

                        debug!("Found {} loopback addresses", addresses.len());
                        Ok(addresses)
                    } else {
                        let error = String::from_utf8_lossy(&output.stderr);
                        error!("Failed to list loopback addresses: {error}");
                        Err(HelperError::NetworkConfig(format!(
                            "Failed to list loopback addresses: {error}"
                        )))
                    }
                }
                Err(e) => {
                    error!("Error executing ifconfig command: {e}");
                    Err(HelperError::NetworkConfig(format!(
                        "Failed to list loopback addresses: {e}"
                    )))
                }
            }
        })
        .await;

        match result {
            Ok(inner_result) => inner_result,
            Err(e) => {
                error!("Task execution error: {e}");
                Err(HelperError::NetworkConfig(format!(
                    "Task execution error: {e}"
                )))
            }
        }
    }

    #[cfg(target_os = "linux")]
    async fn add_loopback_linux(&self, address: &str) -> Result<(), HelperError> {
        let output = Command::new("/sbin/ip")
            .args(["addr", "add", &format!("{}/32", address), "dev", "lo"])
            .output()
            .map_err(|e| {
                HelperError::NetworkConfig(format!("Failed to add loopback address: {}", e))
            })?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::NetworkConfig(format!(
                "Failed to add loopback address: {}",
                error
            )));
        }

        Ok(())
    }

    #[cfg(target_os = "linux")]
    async fn remove_loopback_linux(&self, address: &str) -> Result<(), HelperError> {
        let output = Command::new("/sbin/ip")
            .args(["addr", "del", &format!("{}/32", address), "dev", "lo"])
            .output()
            .map_err(|e| {
                HelperError::NetworkConfig(format!("Failed to remove loopback address: {}", e))
            })?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::NetworkConfig(format!(
                "Failed to remove loopback address: {}",
                error
            )));
        }

        Ok(())
    }

    #[cfg(target_os = "linux")]
    async fn list_loopback_linux(&self) -> Result<Vec<String>, HelperError> {
        let output = Command::new("/sbin/ip")
            .args(["addr", "show", "dev", "lo"])
            .output()
            .map_err(|e| {
                HelperError::NetworkConfig(format!("Failed to list loopback addresses: {}", e))
            })?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::NetworkConfig(format!(
                "Failed to list loopback addresses: {}",
                error
            )));
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        let mut addresses = Vec::new();

        for line in output_str.lines() {
            if line.contains("inet ") {
                let parts: Vec<&str> = line.split_whitespace().collect();
                if parts.len() >= 2 {
                    let addr_with_prefix = parts[1];
                    let addr = addr_with_prefix.split('/').next().unwrap_or("");
                    if addr.starts_with("127.") {
                        addresses.push(addr.to_string());
                    }
                }
            }
        }

        Ok(addresses)
    }

    #[cfg(target_os = "windows")]
    async fn add_loopback_windows(&self, address: &str) -> Result<(), HelperError> {
        let output = Command::new("netsh")
            .args([
                "interface",
                "ipv4",
                "add",
                "address",
                "Loopback",
                address,
                "255.0.0.0",
            ])
            .output()
            .map_err(|e| {
                HelperError::NetworkConfig(format!("Failed to add loopback address: {}", e))
            })?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::NetworkConfig(format!(
                "Failed to add loopback address: {}",
                error
            )));
        }

        Ok(())
    }

    #[cfg(target_os = "windows")]
    async fn remove_loopback_windows(&self, address: &str) -> Result<(), HelperError> {
        let output = Command::new("netsh")
            .args([
                "interface",
                "ipv4",
                "delete",
                "address",
                "Loopback",
                address,
            ])
            .output()
            .map_err(|e| {
                HelperError::NetworkConfig(format!("Failed to remove loopback address: {}", e))
            })?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::NetworkConfig(format!(
                "Failed to remove loopback address: {}",
                error
            )));
        }

        Ok(())
    }

    #[cfg(target_os = "windows")]
    async fn list_loopback_windows(&self) -> Result<Vec<String>, HelperError> {
        let output = Command::new("netsh")
            .args(["interface", "ipv4", "show", "addresses", "Loopback"])
            .output()
            .map_err(|e| {
                HelperError::NetworkConfig(format!("Failed to list loopback addresses: {}", e))
            })?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::NetworkConfig(format!(
                "Failed to list loopback addresses: {}",
                error
            )));
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        let mut addresses = Vec::new();

        for line in output_str.lines() {
            if line.contains("IP Address:") {
                let parts: Vec<&str> = line.split(':').collect();
                if parts.len() >= 2 {
                    let addr = parts[1].trim();
                    if addr.starts_with("127.") {
                        addresses.push(addr.to_string());
                    }
                }
            }
        }

        Ok(addresses)
    }
}
