//! Modulo RustDesk - Provisionamento e gerenciamento do RustDesk
//!
//! Gerencia a instalacao, configuracao e provisionamento do RustDesk.
//! Como o servico roda como LocalSystem, nao precisa de elevacao.

use chrono::Utc;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::env;
use std::ffi::OsStr;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::os::windows::process::CommandExt;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;
use thiserror::Error;
use tracing::{error, info, warn};

const RELEASES_API: &str = "https://api.github.com/repos/rustdesk/rustdesk/releases/latest";
const USER_AGENT: &str = "RavenService/1.0";
const SERVER_HOST: &str = "rust.rever.com.br";
const SERVER_KEY: &str = "0mxocQKmK6GvTZQYKgjrG9tlNkKOqf81gKgqwAmnZuI=";
const DEFAULT_PASSWORD: &str = "FMQ9MA>e73r.FI<b*34Vmx_8P";
const SERVICE_NAME: &str = "RustDesk";
const CACHE_DIR_NAME: &str = "Rever\\RustDeskCache";
const LOCAL_SERVICE_CONFIG: &str = r"C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config";
const LOCAL_SYSTEM_CONFIG: &str = r"C:\Windows\System32\config\systemprofile\AppData\Roaming\RustDesk\config";
const SECURITY_VERIFICATION_VALUE: &str = "use-permanent-password";
const SECURITY_APPROVE_MODE_VALUE: &str = "password";
const CREATE_NO_WINDOW: u32 = 0x08000000;

static PROVISION_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));

#[derive(Debug, Error)]
pub enum RustdeskError {
    #[error("HTTP error: {0}")]
    Http(#[from] reqwest::Error),

    #[error("I/O error: {0}")]
    Io(#[from] io::Error),

    #[error("Release asset nao encontrado para Windows x86_64")]
    AssetMissing,

    #[error("Falha ao executar comando {command}: status {status:?}")]
    CommandFailed { command: String, status: Option<i32> },

    #[error("Falha ao detectar ID do RustDesk")]
    MissingId,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RustdeskResult {
    pub id: String,
    pub password: String,
    pub installed_version: Option<String>,
    pub updated: bool,
    pub last_provisioned_at: i64,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RustdeskStatus {
    pub installed: bool,
    pub running: bool,
    pub id: Option<String>,
    pub version: Option<String>,
}

#[derive(Debug, Deserialize)]
struct ReleaseAsset {
    name: String,
    browser_download_url: String,
}

#[derive(Debug, Deserialize)]
struct ReleaseResponse {
    tag_name: String,
    assets: Vec<ReleaseAsset>,
}

/// Provisiona o RustDesk
pub fn ensure_rustdesk(
    config_string: Option<&str>,
    password_override: Option<&str>,
    machine_id: Option<&str>,
) -> Result<RustdeskResult, RustdeskError> {
    let _guard = PROVISION_MUTEX.lock();
    info!("Iniciando provisionamento do RustDesk");

    // Prepara ACLs dos diretorios de servico
    if let Err(e) = ensure_service_profiles_writable() {
        warn!("Aviso ao preparar ACL: {}", e);
    }

    // Le ID existente antes de qualquer limpeza
    let preserved_remote_id = read_remote_id_from_profiles();
    if let Some(ref id) = preserved_remote_id {
        info!("ID existente preservado: {}", id);
    }

    let exe_path = detect_executable_path();
    let (installed_version, freshly_installed) = ensure_installed(&exe_path)?;

    info!(
        "RustDesk {}: {}",
        if freshly_installed { "instalado" } else { "ja presente" },
        exe_path.display()
    );

    // Para processos existentes
    let _ = stop_rustdesk_processes();

    // Limpa perfis apenas se instalacao fresca
    if freshly_installed {
        let _ = purge_existing_rustdesk_profiles();
    }

    // Aplica configuracao
    if let Some(config) = config_string.filter(|c| !c.trim().is_empty()) {
        if let Err(e) = run_with_args(&exe_path, &["--config", config]) {
            warn!("Falha ao aplicar config inline: {}", e);
        }
    } else {
        let config_path = write_config_files()?;
        if let Err(e) = apply_config(&exe_path, &config_path) {
            warn!("Falha ao aplicar config via CLI: {}", e);
        }
    }

    // Define senha
    let password = password_override
        .map(|v| v.trim().to_string())
        .filter(|v| !v.is_empty())
        .unwrap_or_else(|| DEFAULT_PASSWORD.to_string());

    if let Err(e) = set_password(&exe_path, &password) {
        warn!("Falha ao definir senha: {}", e);
    } else {
        let _ = ensure_password_files(&password);
        let _ = propagate_password_profile();
    }

    // Define ID customizado
    let custom_id = if let Some(ref existing_id) = preserved_remote_id {
        if !freshly_installed {
            Some(existing_id.clone())
        } else {
            define_custom_id(&exe_path, machine_id)
        }
    } else {
        define_custom_id(&exe_path, machine_id)
    };

    // Inicia servico
    if let Err(e) = ensure_service_running(&exe_path) {
        warn!("Falha ao iniciar servico: {}", e);
    }

    // Obtem ID final
    let final_id = match query_id_with_retries(&exe_path, 5) {
        Ok(id) => id,
        Err(_) => {
            read_remote_id_from_profiles()
                .or_else(|| custom_id.clone())
                .ok_or(RustdeskError::MissingId)?
        }
    };

    // Garante ID em todos os arquivos
    ensure_remote_id_files(&final_id);

    let version = query_version(&exe_path).ok().or(installed_version);
    let last_provisioned_at = Utc::now().timestamp_millis();

    info!("Provisionamento concluido. ID: {}, Versao: {:?}", final_id, version);

    Ok(RustdeskResult {
        id: final_id,
        password,
        installed_version: version,
        updated: freshly_installed,
        last_provisioned_at,
    })
}

/// Retorna status do RustDesk
pub fn get_status() -> Result<RustdeskStatus, RustdeskError> {
    let exe_path = detect_executable_path();
    let installed = exe_path.exists();

    let running = if installed {
        query_service_state().map(|s| s == "running").unwrap_or(false)
    } else {
        false
    };

    let id = if installed {
        query_id(&exe_path).ok().or_else(read_remote_id_from_profiles)
    } else {
        None
    };

    let version = if installed {
        query_version(&exe_path).ok()
    } else {
        None
    };

    Ok(RustdeskStatus {
        installed,
        running,
        id,
        version,
    })
}

// =============================================================================
// Funcoes Auxiliares
// =============================================================================

fn detect_executable_path() -> PathBuf {
    let program_files = env::var("PROGRAMFILES").unwrap_or_else(|_| "C:/Program Files".to_string());
    Path::new(&program_files).join("RustDesk").join("rustdesk.exe")
}

fn ensure_installed(exe_path: &Path) -> Result<(Option<String>, bool), RustdeskError> {
    if exe_path.exists() {
        return Ok((None, false));
    }

    let cache_root = PathBuf::from(env::var("PROGRAMDATA").unwrap_or_else(|_| "C:/ProgramData".to_string()))
        .join(CACHE_DIR_NAME);
    fs::create_dir_all(&cache_root)?;

    let (installer_path, version_tag) = download_latest_installer(&cache_root)?;
    run_installer(&installer_path)?;
    thread::sleep(Duration::from_secs(20));

    Ok((Some(version_tag), true))
}

fn download_latest_installer(cache_root: &Path) -> Result<(PathBuf, String), RustdeskError> {
    let client = Client::builder()
        .user_agent(USER_AGENT)
        .timeout(Duration::from_secs(60))
        .build()?;

    let release: ReleaseResponse = client.get(RELEASES_API).send()?.error_for_status()?.json()?;

    let asset = release
        .assets
        .iter()
        .find(|a| a.name.ends_with("x86_64.exe"))
        .ok_or(RustdeskError::AssetMissing)?;

    let target_path = cache_root.join(&asset.name);
    if target_path.exists() {
        return Ok((target_path, release.tag_name));
    }

    info!("Baixando RustDesk: {}", asset.name);
    let mut response = client.get(&asset.browser_download_url).send()?.error_for_status()?;
    let mut output = File::create(&target_path)?;
    response.copy_to(&mut output)?;

    Ok((target_path, release.tag_name))
}

fn run_installer(installer_path: &Path) -> Result<(), RustdeskError> {
    let status = hidden_command(installer_path)
        .arg("--silent-install")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()?;

    if !status.success() {
        return Err(RustdeskError::CommandFailed {
            command: format!("{} --silent-install", installer_path.display()),
            status: status.code(),
        });
    }
    Ok(())
}

fn program_data_config_dir() -> PathBuf {
    PathBuf::from(env::var("PROGRAMDATA").unwrap_or_else(|_| "C:/ProgramData".to_string()))
        .join("RustDesk")
        .join("config")
}

/// Retorna todos os diretorios AppData\Roaming\RustDesk\config de usuarios do sistema
/// Como o servico roda como LocalSystem, precisamos enumerar os profiles de usuarios
fn all_user_appdata_config_dirs() -> Vec<PathBuf> {
    let mut dirs = Vec::new();

    // Enumera C:\Users\*\AppData\Roaming\RustDesk\config
    let users_dir = Path::new("C:\\Users");
    if let Ok(entries) = fs::read_dir(users_dir) {
        for entry in entries.flatten() {
            let path = entry.path();
            // Ignora pastas de sistema
            let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
            if name == "Public" || name == "Default" || name == "Default User" || name == "All Users" {
                continue;
            }
            let rustdesk_config = path.join("AppData").join("Roaming").join("RustDesk").join("config");
            // Verifica se o diretorio pai existe (usuario real)
            if path.join("AppData").join("Roaming").exists() {
                dirs.push(rustdesk_config);
            }
        }
    }

    // Tambem tenta o APPDATA do ambiente (pode ser util em alguns casos)
    if let Ok(appdata) = env::var("APPDATA") {
        let path = Path::new(&appdata).join("RustDesk").join("config");
        if !dirs.contains(&path) {
            dirs.push(path);
        }
    }

    dirs
}

fn service_profile_dirs() -> Vec<PathBuf> {
    vec![
        PathBuf::from(LOCAL_SERVICE_CONFIG),
        PathBuf::from(LOCAL_SYSTEM_CONFIG),
    ]
}

fn remote_id_directories() -> Vec<PathBuf> {
    let mut dirs = Vec::new();
    dirs.push(program_data_config_dir());
    dirs.extend(service_profile_dirs());
    dirs.extend(all_user_appdata_config_dirs());
    dirs
}

fn write_config_files() -> Result<PathBuf, RustdeskError> {
    let config_contents = format!(
        r#"[options]
key = "{key}"
relay-server = "{host}"
custom-rendezvous-server = "{host}"
api-server = "https://{host}"
verification-method = "{verification}"
approve-mode = "{approve}"
"#,
        host = SERVER_HOST,
        key = SERVER_KEY,
        verification = SECURITY_VERIFICATION_VALUE,
        approve = SECURITY_APPROVE_MODE_VALUE,
    );

    let main_path = program_data_config_dir().join("RustDesk2.toml");
    write_file(&main_path, &config_contents)?;

    for service_dir in service_profile_dirs() {
        let service_profile = service_dir.join("RustDesk2.toml");
        let _ = write_file(&service_profile, &config_contents);
    }

    Ok(main_path)
}

fn write_file(path: &Path, contents: &str) -> Result<(), io::Error> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let mut file = OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open(path)?;
    file.write_all(contents.as_bytes())
}

fn apply_config(exe_path: &Path, config_path: &Path) -> Result<(), RustdeskError> {
    run_with_args(exe_path, &["--import-config", &config_path.to_string_lossy()])
}

fn set_password(exe_path: &Path, secret: &str) -> Result<(), RustdeskError> {
    run_with_args(exe_path, &["--password", secret])
}

fn define_custom_id(exe_path: &Path, machine_id: Option<&str>) -> Option<String> {
    let value = machine_id.and_then(|raw| {
        let trimmed = raw.trim();
        if trimmed.is_empty() { None } else { Some(trimmed) }
    })?;

    let custom_id = derive_numeric_id(value);
    if run_with_args(exe_path, &["--set-id", &custom_id]).is_ok() {
        info!("ID deterministico definido: {}", custom_id);
        Some(custom_id)
    } else {
        None
    }
}

fn derive_numeric_id(machine_id: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(machine_id.as_bytes());
    let hash = hasher.finalize();
    let mut bytes = [0u8; 8];
    bytes.copy_from_slice(&hash[..8]);
    let value = u64::from_le_bytes(bytes);
    let num = (value % 900_000_000) + 100_000_000;
    format!("{:09}", num)
}

fn ensure_service_running(exe_path: &Path) -> Result<(), RustdeskError> {
    ensure_service_installed(exe_path)?;
    let _ = run_sc(&["config", SERVICE_NAME, "start=", "auto"]);
    let _ = run_sc(&["start", SERVICE_NAME]);
    remove_rustdesk_autorun_artifacts();
    Ok(())
}

fn ensure_service_installed(exe_path: &Path) -> Result<(), RustdeskError> {
    if run_sc(&["query", SERVICE_NAME]).is_ok() {
        return Ok(());
    }
    run_with_args(exe_path, &["--install-service"])
}

fn stop_rustdesk_processes() -> Result<(), RustdeskError> {
    let _ = run_sc(&["stop", SERVICE_NAME]);
    thread::sleep(Duration::from_secs(2));

    let status = hidden_command("taskkill")
        .args(["/F", "/T", "/IM", "rustdesk.exe"])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()?;

    if status.success() || matches!(status.code(), Some(128)) {
        Ok(())
    } else {
        Err(RustdeskError::CommandFailed {
            command: "taskkill".into(),
            status: status.code(),
        })
    }
}

fn purge_existing_rustdesk_profiles() -> Result<(), String> {
    let files = [
        "RustDesk.toml",
        "RustDesk_local.toml",
        "RustDesk2.toml",
        "password",
        "passwd",
        "passwd.txt",
    ];

    for dir in remote_id_directories() {
        if !dir.exists() {
            continue;
        }
        for name in files {
            let path = dir.join(name);
            if path.exists() {
                let _ = fs::remove_file(&path);
            }
        }
    }
    Ok(())
}

fn ensure_password_files(secret: &str) -> Result<(), String> {
    for dir in remote_id_directories() {
        let password_path = dir.join("RustDesk.toml");
        let _ = write_toml_kv(&password_path, "password", secret);

        let local_path = dir.join("RustDesk_local.toml");
        let _ = write_toml_kv(&local_path, "verification-method", SECURITY_VERIFICATION_VALUE);
        let _ = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE);
    }
    Ok(())
}

fn propagate_password_profile() -> io::Result<bool> {
    // Encontra um diretorio de usuario que tenha arquivos de config
    let user_dirs = all_user_appdata_config_dirs();
    let src_dir = user_dirs.iter().find(|d| d.join("RustDesk.toml").exists());

    let Some(src_dir) = src_dir else {
        // Se nenhum usuario tem config, usa ProgramData como fonte
        let pd = program_data_config_dir();
        if !pd.join("RustDesk.toml").exists() {
            return Ok(false);
        }
        return propagate_from_dir(&pd);
    };

    propagate_from_dir(src_dir)
}

fn propagate_from_dir(src_dir: &Path) -> io::Result<bool> {
    let propagation_files = ["RustDesk.toml", "RustDesk_local.toml", "RustDesk2.toml"];
    let mut propagated = false;

    for filename in propagation_files {
        let src_path = src_dir.join(filename);
        if !src_path.exists() {
            continue;
        }

        for dest_root in remote_id_directories() {
            if dest_root == src_dir {
                continue; // Nao copiar para si mesmo
            }
            let target_path = dest_root.join(filename);
            if copy_overwrite(&src_path, &target_path).is_ok() {
                propagated = true;
            }
        }
    }

    Ok(propagated)
}

fn ensure_remote_id_files(id: &str) {
    for dir in remote_id_directories() {
        let path = dir.join("RustDesk_local.toml");
        let _ = write_remote_id_value(&path, id);
    }
}

fn write_remote_id_value(path: &Path, id: &str) -> io::Result<()> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let replacement = format!("remote_id = '{}'\n", id);
    if let Ok(existing) = fs::read_to_string(path) {
        let mut replaced = false;
        let mut buffer = String::with_capacity(existing.len() + replacement.len());
        for line in existing.lines() {
            if line.trim_start().starts_with("remote_id") {
                buffer.push_str(&replacement);
                replaced = true;
            } else {
                buffer.push_str(line);
                buffer.push('\n');
            }
        }
        if !replaced {
            buffer.push_str(&replacement);
        }
        let mut file = OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(path)?;
        file.write_all(buffer.as_bytes())
    } else {
        let mut file = OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(path)?;
        file.write_all(replacement.as_bytes())
    }
}

fn write_toml_kv(path: &Path, key: &str, value: &str) -> io::Result<()> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let sanitized = value.replace('\\', "\\\\").replace('"', "\\\"");
    let replacement = format!("{key} = \"{sanitized}\"\n");
    let existing = fs::read_to_string(path).unwrap_or_default();
    let mut replaced = false;
    let mut buffer = String::with_capacity(existing.len() + replacement.len());
    for line in existing.lines() {
        let trimmed = line.trim_start();
        if trimmed.starts_with(&format!("{key} ")) || trimmed.starts_with(&format!("{key}=")) {
            buffer.push_str(&replacement);
            replaced = true;
        } else {
            buffer.push_str(line);
            buffer.push('\n');
        }
    }
    if !replaced {
        buffer.push_str(&replacement);
    }
    let mut file = OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open(path)?;
    file.write_all(buffer.as_bytes())
}

fn read_remote_id_from_profiles() -> Option<String> {
    for dir in remote_id_directories() {
        for candidate in [dir.join("RustDesk_local.toml"), dir.join("RustDesk.toml")] {
            if let Some(id) = read_remote_id_file(&candidate) {
                if !id.is_empty() {
                    return Some(id);
                }
            }
        }
    }
    None
}

fn read_remote_id_file(path: &Path) -> Option<String> {
    let content = fs::read_to_string(path).ok()?;
    for line in content.lines() {
        if let Some(value) = parse_assignment(line, "remote_id") {
            return Some(value);
        }
    }
    None
}

fn parse_assignment(line: &str, key: &str) -> Option<String> {
    let trimmed = line.trim();
    if !trimmed.starts_with(key) {
        return None;
    }
    let (_, rhs) = trimmed.split_once('=')?;
    let value = rhs.trim().trim_matches(|c| c == '\'' || c == '"');
    if value.is_empty() {
        None
    } else {
        Some(value.to_string())
    }
}

fn query_id_with_retries(exe_path: &Path, attempts: usize) -> Result<String, RustdeskError> {
    for attempt in 0..attempts {
        match query_id(exe_path) {
            Ok(value) if !value.trim().is_empty() => return Ok(value),
            _ => {}
        }
        if attempt + 1 < attempts {
            thread::sleep(Duration::from_millis(800));
        }
    }
    Err(RustdeskError::MissingId)
}

fn query_id(exe_path: &Path) -> Result<String, RustdeskError> {
    let output = hidden_command(exe_path).arg("--get-id").output()?;
    if !output.status.success() {
        return Err(RustdeskError::CommandFailed {
            command: format!("{} --get-id", exe_path.display()),
            status: output.status.code(),
        });
    }
    let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
    if stdout.is_empty() {
        return Err(RustdeskError::MissingId);
    }
    Ok(stdout)
}

fn query_version(exe_path: &Path) -> Result<String, RustdeskError> {
    let output = hidden_command(exe_path).arg("--version").output()?;
    if !output.status.success() {
        return Err(RustdeskError::CommandFailed {
            command: format!("{} --version", exe_path.display()),
            status: output.status.code(),
        });
    }
    Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}

fn query_service_state() -> Option<String> {
    let output = hidden_command("sc")
        .args(["query", SERVICE_NAME])
        .output()
        .ok()?;

    if !output.status.success() {
        return None;
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    for line in stdout.lines() {
        let lower = line.to_lowercase();
        if lower.contains("running") {
            return Some("running".to_string());
        }
        if lower.contains("stopped") {
            return Some("stopped".to_string());
        }
    }
    None
}

fn run_sc(args: &[&str]) -> Result<(), RustdeskError> {
    let status = hidden_command("sc")
        .args(args)
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()?;
    if !status.success() {
        return Err(RustdeskError::CommandFailed {
            command: format!("sc {}", args.join(" ")),
            status: status.code(),
        });
    }
    Ok(())
}

fn run_with_args(exe_path: &Path, args: &[&str]) -> Result<(), RustdeskError> {
    let status = hidden_command(exe_path)
        .args(args)
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()?;
    if !status.success() {
        return Err(RustdeskError::CommandFailed {
            command: format!("{} {}", exe_path.display(), args.join(" ")),
            status: status.code(),
        });
    }
    Ok(())
}

fn remove_rustdesk_autorun_artifacts() {
    // Remove atalhos de inicializacao automatica
    let mut startup_paths: Vec<PathBuf> = Vec::new();
    if let Ok(appdata) = env::var("APPDATA") {
        startup_paths.push(
            Path::new(&appdata)
                .join("Microsoft\\Windows\\Start Menu\\Programs\\Startup\\RustDesk.lnk"),
        );
    }
    startup_paths.push(PathBuf::from(
        r"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\RustDesk.lnk",
    ));

    for path in startup_paths {
        if path.exists() {
            let _ = fs::remove_file(&path);
        }
    }

    // Remove entradas de registro
    for hive in ["HKCU", "HKLM"] {
        let reg_path = format!(r"{}\Software\Microsoft\Windows\CurrentVersion\Run", hive);
        let _ = hidden_command("reg")
            .args(["delete", &reg_path, "/v", "RustDesk", "/f"])
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .status();
    }
}

fn ensure_service_profiles_writable() -> Result<(), String> {
    for dir in service_profile_dirs() {
        if !can_write_dir(&dir) {
            fix_profile_acl(&dir)?;
        }
    }
    Ok(())
}

fn can_write_dir(dir: &Path) -> bool {
    if fs::create_dir_all(dir).is_err() {
        return false;
    }
    let probe = dir.join(".raven_acl_probe");
    match OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open(&probe)
    {
        Ok(mut file) => {
            if file.write_all(b"ok").is_err() {
                let _ = fs::remove_file(&probe);
                return false;
            }
            let _ = fs::remove_file(&probe);
            true
        }
        Err(_) => false,
    }
}

fn fix_profile_acl(target: &Path) -> Result<(), String> {
    let target_str = target.display().to_string();

    // Como ja estamos rodando como LocalSystem, podemos usar takeown/icacls diretamente
    let _ = hidden_command("takeown")
        .args(["/F", &target_str, "/R", "/D", "Y"])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status();

    let status = hidden_command("icacls")
        .args([
            &target_str,
            "/grant",
            "*S-1-5-32-544:(OI)(CI)F",
            "*S-1-5-19:(OI)(CI)F",
            "*S-1-5-32-545:(OI)(CI)M",
            "/T",
            "/C",
            "/Q",
        ])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .map_err(|e| format!("Erro ao executar icacls: {}", e))?;

    if status.success() {
        Ok(())
    } else {
        Err(format!("icacls retornou codigo {}", status.code().unwrap_or(-1)))
    }
}

fn copy_overwrite(src: &Path, dst: &Path) -> io::Result<()> {
    if let Some(parent) = dst.parent() {
        fs::create_dir_all(parent)?;
    }
    if dst.is_dir() {
        fs::remove_dir_all(dst)?;
    } else if dst.exists() {
        fs::remove_file(dst)?;
    }
    fs::copy(src, dst)?;
    Ok(())
}

fn hidden_command(program: impl AsRef<OsStr>) -> Command {
    let mut cmd = Command::new(program);
    cmd.creation_flags(CREATE_NO_WINDOW);
    cmd
}
