//! Windows Service management for OSTP Client
//!
//! Installs, configures, and manages the OSTP client as a Windows Service.
//! Uses sc.exe for broader compatibility.

use anyhow::{Context, Result};
use std::path::PathBuf;
use std::process::Command;

/// Service name
const SERVICE_NAME: &str = "OstpClient";

/// Service display name  
const SERVICE_DISPLAY_NAME: &str = "Ospab Network OSTP Client";

/// Service description
const SERVICE_DESCRIPTION: &str = 
    "Provides secure, stealth VPN connectivity through the OSTP protocol.";

/// Install OSTP client as a Windows Service using sc.exe
pub async fn install_service(install_path: &PathBuf) -> Result<()> {
    #[cfg(windows)]
    {
        let executable_path = install_path.join("ostp-client.exe");
        
        if !executable_path.exists() {
            anyhow::bail!("ostp-client.exe not found at {:?}", executable_path);
        }

        let config_path = install_path.join("ostp-client.json");
        let bin_path = format!(
            "\"{}\" --service --config \"{}\"",
            executable_path.display(),
            config_path.display()
        );

        // Create service
        let output = Command::new("sc.exe")
            .args([
                "create",
                SERVICE_NAME,
                &format!("binPath= {}", bin_path),
                &format!("DisplayName= {}", SERVICE_DISPLAY_NAME),
                "start= auto",
                "depend= Tcpip/Dnscache",
            ])
            .output()
            .context("Failed to run sc.exe create")?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            if !stderr.contains("1073") { // Service already exists
                anyhow::bail!("Failed to create service: {}", stderr);
            }
        }

        // Set description
        let _ = Command::new("sc.exe")
            .args(["description", SERVICE_NAME, SERVICE_DESCRIPTION])
            .output();

        // Configure recovery options (restart on failure)
        let _ = Command::new("sc.exe")
            .args([
                "failure",
                SERVICE_NAME,
                "reset= 86400",
                "actions= restart/60000/restart/120000/restart/300000",
            ])
            .output();

        tracing::info!("Service {} installed successfully", SERVICE_NAME);
    }

    #[cfg(not(windows))]
    {
        let _ = install_path;
        anyhow::bail!("Windows Service installation is only supported on Windows");
    }

    Ok(())
}

/// Start the OSTP service
pub async fn start_service() -> Result<()> {
    #[cfg(windows)]
    {
        let output = Command::new("sc.exe")
            .args(["start", SERVICE_NAME])
            .output()
            .context("Failed to run sc.exe start")?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            if !stderr.contains("1056") { // Service already running
                anyhow::bail!("Failed to start service: {}", stderr);
            }
        }

        tracing::info!("Service {} started", SERVICE_NAME);
    }

    Ok(())
}

/// Stop the OSTP service
pub async fn stop_service() -> Result<()> {
    #[cfg(windows)]
    {
        let output = Command::new("sc.exe")
            .args(["stop", SERVICE_NAME])
            .output()
            .context("Failed to run sc.exe stop")?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            if !stderr.contains("1062") { // Service not running
                anyhow::bail!("Failed to stop service: {}", stderr);
            }
        }

        // Wait for service to stop
        for _ in 0..30 {
            tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
            if !is_running() {
                break;
            }
        }

        tracing::info!("Service {} stopped", SERVICE_NAME);
    }

    Ok(())
}

/// Uninstall the OSTP service
pub async fn uninstall_service() -> Result<()> {
    // Stop the service first
    stop_service().await.ok();

    #[cfg(windows)]
    {
        let output = Command::new("sc.exe")
            .args(["delete", SERVICE_NAME])
            .output()
            .context("Failed to run sc.exe delete")?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            if !stderr.contains("1060") { // Service doesn't exist
                anyhow::bail!("Failed to delete service: {}", stderr);
            }
        }

        tracing::info!("Service {} uninstalled", SERVICE_NAME);
    }

    Ok(())
}

/// Enable service to start at system startup
pub async fn enable_autostart() -> Result<()> {
    #[cfg(windows)]
    {
        Command::new("sc.exe")
            .args(["config", SERVICE_NAME, "start= auto"])
            .output()
            .context("Failed to enable autostart")?;

        tracing::info!("Autostart enabled for {}", SERVICE_NAME);
    }

    Ok(())
}

/// Disable service autostart
#[allow(dead_code)]
pub async fn disable_autostart() -> Result<()> {
    #[cfg(windows)]
    {
        Command::new("sc.exe")
            .args(["config", SERVICE_NAME, "start= demand"])
            .output()
            .context("Failed to disable autostart")?;

        tracing::info!("Autostart disabled for {}", SERVICE_NAME);
    }

    Ok(())
}

/// Check if service is installed
#[allow(dead_code)]
pub fn is_installed() -> bool {
    #[cfg(windows)]
    {
        Command::new("sc.exe")
            .args(["query", SERVICE_NAME])
            .output()
            .map(|o| o.status.success())
            .unwrap_or(false)
    }

    #[cfg(not(windows))]
    false
}

/// Check if service is running
pub fn is_running() -> bool {
    #[cfg(windows)]
    {
        Command::new("sc.exe")
            .args(["query", SERVICE_NAME])
            .output()
            .map(|o| {
                let stdout = String::from_utf8_lossy(&o.stdout);
                stdout.contains("RUNNING")
            })
            .unwrap_or(false)
    }

    #[cfg(not(windows))]
    false
}

/// Get service status
#[allow(dead_code)]
pub fn get_status() -> Option<String> {
    #[cfg(windows)]
    {
        Command::new("sc.exe")
            .args(["query", SERVICE_NAME])
            .output()
            .ok()
            .and_then(|o| {
                let stdout = String::from_utf8_lossy(&o.stdout);
                if stdout.contains("RUNNING") {
                    Some("Running".to_string())
                } else if stdout.contains("STOPPED") {
                    Some("Stopped".to_string())
                } else if stdout.contains("PENDING") {
                    Some("Pending".to_string())
                } else {
                    Some("Unknown".to_string())
                }
            })
    }

    #[cfg(not(windows))]
    None
}
