use crate::RustdeskProvisioningResult;
use chrono::{Local, Utc};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use reqwest::blocking::Client;
use serde::Deserialize;
use serde_json::{Map as JsonMap, Value as JsonValue};
use sha2::{Digest, Sha256};
use std::env;
use std::ffi::OsStr;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;
use thiserror::Error;
use std::os::windows::process::CommandExt;

const RELEASES_API: &str = "https://api.github.com/repos/rustdesk/rustdesk/releases/latest";
const USER_AGENT: &str = "RavenDesktop/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 APP_IDENTIFIER: &str = "br.com.esdrasrenan.sistemadechamados";
const MACHINE_STORE_FILENAME: &str = "machine-agent.json";
#[allow(dead_code)]
const ACL_FLAG_FILENAME: &str = "rustdesk_acl_unlocked.flag";
#[allow(dead_code)]
const RUSTDESK_ACL_STORE_KEY: &str = "rustdeskAclUnlockedAt";
const SECURITY_VERIFICATION_VALUE: &str = "use-permanent-password";
const SECURITY_APPROVE_MODE_VALUE: &str = "password";
const RUSTDESK_CONFIG_FILES: [&str; 6] = [
    "RustDesk.toml",
    "RustDesk_local.toml",
    "RustDesk2.toml",
    "password",
    "passwd",
    "passwd.txt",
];
const PROPAGATION_FILES: [&str; 3] = [
    "RustDesk.toml",
    "RustDesk_local.toml",
    "RustDesk2.toml",
];
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 não 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, Deserialize)]
struct ReleaseAsset {
    name: String,
    browser_download_url: String,
}

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

/// Auxiliar para definir ID customizado baseado no machine_id
fn define_custom_id_from_machine(exe_path: &Path, machine_id: Option<&str>) -> Option<String> {
    if let Some(value) = machine_id.and_then(|raw| {
        let trimmed = raw.trim();
        if trimmed.is_empty() { None } else { Some(trimmed) }
    }) {
        match set_custom_id(exe_path, value) {
            Ok(custom) => {
                log_event(format!("ID determinístico definido: {custom}"));
                Some(custom)
            }
            Err(error) => {
                log_event(format!("Falha ao definir ID determinístico: {error}"));
                None
            }
        }
    } else {
        None
    }
}

pub fn ensure_rustdesk(
    config_string: Option<&str>,
    password_override: Option<&str>,
    machine_id: Option<&str>,
) -> Result<RustdeskProvisioningResult, RustdeskError> {
    let _guard = PROVISION_MUTEX.lock();
    log_event("Iniciando preparo do RustDesk");

    if let Err(error) = ensure_service_profiles_writable_preflight() {
        log_event(format!(
            "Aviso: não foi possível preparar ACL dos perfis do serviço ({error}). Continuando mesmo assim; o serviço pode não aplicar a senha."
        ));
    }

    // IMPORTANTE: Ler o ID existente ANTES de qualquer limpeza
    // Isso preserva o ID quando o Raven é reinstalado mas o RustDesk permanece
    let preserved_remote_id = read_remote_id_from_profiles();
    if let Some(ref id) = preserved_remote_id {
        log_event(format!("ID existente preservado antes da limpeza: {}", id));
    }

    let exe_path = detect_executable_path();
    let (installed_version, freshly_installed) = ensure_installed(&exe_path)?;
    log_event(if freshly_installed {
        "RustDesk instalado a partir do instalador mais recente"
    } else {
        "RustDesk já instalado, usando binário existente"
    });

    match stop_rustdesk_processes() {
        Ok(_) => log_event("Instâncias existentes do RustDesk encerradas"),
        Err(error) => log_event(format!(
            "Aviso: não foi possível parar completamente o RustDesk antes da reprovisionamento ({error})"
        )),
    }

    // So limpa perfis se for instalacao fresca (RustDesk nao existia)
    // Se ja existia, preservamos o ID para manter consistencia
    if freshly_installed {
        match purge_existing_rustdesk_profiles() {
            Ok(_) => log_event("Configurações antigas do RustDesk limpas (instalação fresca)"),
            Err(error) => log_event(format!(
                "Aviso: não foi possível limpar completamente os perfis existentes do RustDesk ({error})"
            )),
        }
    } else {
        log_event("Mantendo perfis existentes do RustDesk (preservando ID)");
    }

    if let Some(value) = config_string.and_then(|raw| {
        let trimmed = raw.trim();
        if trimmed.is_empty() { None } else { Some(trimmed) }
    }) {
        if let Err(error) = run_with_args(&exe_path, &["--config", value]) {
            log_event(format!("Falha ao aplicar configuração inline: {error}"));
        } else {
            log_event("Configuração aplicada via --config");
        }
    } else {
        let config_path = write_config_files()?;
        log_event(format!(
            "Arquivo de configuração atualizado em {}",
            config_path.display()
        ));

        if let Err(error) = apply_config(&exe_path, &config_path) {
            log_event(format!("Falha ao aplicar configuração via CLI: {error}"));
        } else {
            log_event("Configuração aplicada via CLI");
        }
    }

    let password = password_override
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
        .unwrap_or_else(|| DEFAULT_PASSWORD.to_string());

    if let Err(error) = set_password(&exe_path, &password) {
        log_event(format!("Falha ao definir senha padrão: {error}"));
    } else {
        log_event("Senha padrão definida com sucesso");
        log_event("Aplicando senha nos perfis do RustDesk");
        match ensure_password_files(&password) {
            Ok(_) => {
                log_event("Senha e flags de segurança gravadas em todos os perfis do RustDesk");
                log_password_replication(&password);
            }
            Err(error) => log_event(format!("Falha ao persistir senha nos perfis: {error}")),
        }

        match propagate_password_profile() {
            Ok(_) => log_event("Perfil base propagado para ProgramData e perfis de serviço"),
            Err(error) => log_event(format!("Falha ao copiar perfil de senha: {error}")),
        }

        match replicate_password_artifacts() {
            Ok(_) => log_event("Artefatos de senha replicados para o serviço do RustDesk"),
            Err(error) => log_event(format!("Falha ao replicar artefatos de senha: {error}")),
        }

        if let Err(error) = enforce_security_flags() {
            log_event(format!("Falha ao reforçar configuração de senha permanente: {error}"));
        }
    }

    // Se ja existe um ID preservado E o RustDesk nao foi recem-instalado, usa o ID existente
    // Isso garante que reinstalar o Raven nao muda o ID do RustDesk
    let custom_id = if let Some(ref existing_id) = preserved_remote_id {
        if !freshly_installed {
            log_event(format!("Reutilizando ID existente do RustDesk: {}", existing_id));
            Some(existing_id.clone())
        } else {
            // Instalacao fresca - define novo ID baseado no machine_id
            define_custom_id_from_machine(&exe_path, machine_id)
        }
    } else {
        // Sem ID preservado - define novo ID baseado no machine_id
        define_custom_id_from_machine(&exe_path, machine_id)
    };

    if let Err(error) = ensure_service_running(&exe_path) {
        log_event(format!("Falha ao reiniciar serviço do RustDesk: {error}"));
    } else {
        log_event("Serviço RustDesk reiniciado/run ativo");
    }

    let reported_id = match query_id_with_retries(&exe_path, 5) {
        Ok(value) => value,
        Err(error) => {
            log_event(format!("Falha ao obter ID após múltiplas tentativas: {error}"));
            match read_remote_id_from_profiles().or_else(|| custom_id.clone()) {
                Some(value) => {
                    log_event(format!("ID obtido via arquivos de perfil: {value}"));
                    value
                }
                None => return Err(error),
            }
        }
    };

    let mut final_id = reported_id.clone();

    if let Some(expected) = custom_id.as_ref() {
        if expected != &reported_id {
            log_event(format!(
                "ID retornado difere do determinístico ({expected}) -> reaplicando ID determinístico"
            ));

            let mut enforced = false;

            match set_custom_id(&exe_path, expected) {
                Ok(_) => match query_id_with_retries(&exe_path, 3) {
                    Ok(rechecked) => {
                        if &rechecked == expected {
                            log_event(format!("ID determinístico aplicado com sucesso: {rechecked}"));
                            final_id = rechecked;
                            enforced = true;
                        } else {
                            log_event(format!(
                                "ID ainda difere após reaplicação (esperado {expected}, reportado {rechecked}); usando ID reportado"
                            ));
                            final_id = rechecked;
                        }
                    }
                    Err(error) => {
                        log_event(format!(
                            "Falha ao consultar ID após reaplicação: {error}; usando ID reportado ({reported_id})"
                        ));
                        final_id = reported_id.clone();
                    }
                },
                Err(error) => {
                    log_event(format!(
                        "Falha ao reaplicar ID determinístico ({expected}): {error}; usando ID reportado ({reported_id})"
                    ));
                    final_id = reported_id.clone();
                }
            }

            if !enforced && final_id != *expected {
                log_event("Aviso: não foi possível aplicar o ID determinístico; manteremos o ID real fornecido pelo serviço");
            }
        }
    }

    ensure_remote_id_files(&final_id);

    let version = query_version(&exe_path).ok().or(installed_version);

    let last_provisioned_at = Utc::now().timestamp_millis();
    let result = RustdeskProvisioningResult {
        id: final_id.clone(),
        password: password.clone(),
        installed_version: version.clone(),
        updated: freshly_installed,
        last_provisioned_at,
    };

    // Salva os dados do RustDesk diretamente no arquivo machine-agent.json
    // para evitar conflitos com o Tauri Store do TypeScript
    let rustdesk_data = serde_json::json!({
        "id": final_id,
        "password": password,
        "installedVersion": version,
        "updated": freshly_installed,
        "lastProvisionedAt": last_provisioned_at,
        "lastSyncedAt": serde_json::Value::Null,
        "lastError": serde_json::Value::Null
    });
    if let Err(error) = upsert_machine_store_value("rustdesk", rustdesk_data) {
        log_event(format!("Aviso: falha ao salvar dados do RustDesk no store: {error}"));
    } else {
        log_event("Dados do RustDesk salvos no machine-agent.json");
    }

    // Sincroniza com o backend imediatamente apos provisionar
    // O Rust faz o HTTP direto, sem passar pelo CSP do webview
    if let Err(error) = sync_remote_access_with_backend(&result) {
        log_event(format!("Aviso: falha ao sincronizar com backend: {error}"));
    } else {
        log_event("Acesso remoto sincronizado com backend");
        // Atualiza lastSyncedAt no store
        let synced_data = serde_json::json!({
            "id": final_id,
            "password": password,
            "installedVersion": version,
            "updated": freshly_installed,
            "lastProvisionedAt": last_provisioned_at,
            "lastSyncedAt": Utc::now().timestamp_millis(),
            "lastError": serde_json::Value::Null
        });
        if let Err(e) = upsert_machine_store_value("rustdesk", synced_data) {
            log_event(format!("Aviso: falha ao atualizar lastSyncedAt: {e}"));
        } else {
            log_event("lastSyncedAt atualizado com sucesso");
        }
    }

    log_event(format!("Provisionamento concluído. ID final: {final_id}. Versão: {:?}", version));

    Ok(result)
}

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));
    }

    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 write_config_files() -> Result<PathBuf, RustdeskError> {
    let config_contents = build_config_contents();
    let main_path = program_data_config_dir().join("RustDesk2.toml");
    write_file(&main_path, &config_contents)?;
    log_event(format!(
        "Config principal gravada em {}",
        main_path.display()
    ));

    let _ = ensure_service_profiles_writable_preflight();
    for service_dir in service_profile_dirs() {
        let service_profile = service_dir.join("RustDesk2.toml");
        if let Err(error) = write_file(&service_profile, &config_contents) {
            log_event(format!(
                "Falha ao gravar config no perfil do serviço ({}): {error}",
                service_profile.display()
            ));
        }
    }

    if let Some(appdata_path) = user_appdata_config_path("RustDesk2.toml") {
        if let Err(error) = write_file(&appdata_path, &config_contents) {
            log_event(format!(
                "Falha ao atualizar config no AppData do usuário: {error}"
            ));
        }
    }

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

fn user_appdata_config_dir() -> Option<PathBuf> {
    env::var("APPDATA")
        .ok()
        .map(|value| Path::new(&value).join("RustDesk").join("config"))
}

fn user_appdata_config_path(filename: &str) -> Option<PathBuf> {
    user_appdata_config_dir().map(|dir| dir.join(filename))
}

fn build_config_contents() -> String {
    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,
    )
}

fn apply_config(exe_path: &Path, config_path: &Path) -> Result<(), RustdeskError> {
    let status = hidden_command(exe_path)
        .arg("--import-config")
        .arg(config_path)
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()?;
    if !status.success() {
        return Err(RustdeskError::CommandFailed {
            command: format!("{} --import-config {}", exe_path.display(), config_path.display()),
            status: status.code(),
        });
    }
    Ok(())
}

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

fn set_custom_id(exe_path: &Path, machine_id: &str) -> Result<String, RustdeskError> {
    let custom_id = derive_numeric_id(machine_id);
    run_with_args(exe_path, &["--set-id", &custom_id])?;
    Ok(custom_id)
}

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)?;

    if let Err(error) = configure_service_startup() {
        log_event(format!(
            "Aviso: não foi possível reforçar autostart/recuperação do serviço RustDesk: {error}"
        ));
    }

    fn start_sequence() -> Result<(), RustdeskError> {
        let _ = run_sc(&["stop", SERVICE_NAME]);
        thread::sleep(Duration::from_secs(2));
        let _ = run_sc(&["config", SERVICE_NAME, &format!("start= {}", "auto")]);
        run_sc(&["start", SERVICE_NAME])
    }

    let _ = match start_sequence() {
        Ok(_) => Ok(()),
        Err(RustdeskError::CommandFailed { command: _, status: Some(5), .. }) => {
            log_event("SC retornou acesso negado; tentando ajustar ACL dos perfis do serviço...");
            ensure_service_profiles_writable_preflight().map_err(|error| RustdeskError::CommandFailed {
                command: format!("fix_acl ({error})"),
                status: Some(5),
            })?;
            let _ = run_sc(&["stop", SERVICE_NAME]);
            let _ = start_sequence();
            Ok(())
        }
        Err(error) => Err(error),
    };

    remove_rustdesk_autorun_artifacts();

    // Revalida se o serviço realmente subiu; se não, reinstala e tenta novamente.
    match query_service_state() {
        Some(state) if state.eq_ignore_ascii_case("running") => Ok(()),
        _ => {
            log_event("Serviço RustDesk não está em execução após tentativa de start; reaplicando --install-service e start");
            let _ = run_with_args(exe_path, &["--install-service"]);
            let _ = run_sc(&["config", SERVICE_NAME, &format!("start= {}", "auto")]);
            if let Err(error) = start_sequence() {
                log_event(format!(
                    "Falha ao subir o serviço RustDesk mesmo após reinstalação: {error}"
                ));
            }
            Ok(())
        }
    }
}

fn configure_service_startup() -> Result<(), RustdeskError> {
    let start_arg = format!("start= {}", "auto");
    run_sc(&["config", SERVICE_NAME, &start_arg])?;

    let reset_arg = format!("reset= {}", "86400");
    let actions_arg = "actions= restart/5000/restart/5000/restart/5000";
    let failure_actions_applied = run_sc(&["failure", SERVICE_NAME, &reset_arg, actions_arg]).is_ok();
    let _ = run_sc(&["failureflag", SERVICE_NAME, "1"]);

    if failure_actions_applied {
        log_event("Serviço RustDesk configurado para reiniciar automaticamente em caso de falha");
    } else {
        log_event("Aviso: não foi possível configurar recuperação automática do serviço RustDesk");
    }

    Ok(())
}

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() {
        if let Some(pos) = line.find("STATE") {
            // Example: "        STATE              : 4  RUNNING"
            let state = line[pos..].to_string();
            if state.to_lowercase().contains("running") {
                return Some("running".to_string());
            }
            if state.to_lowercase().contains("stopped") {
                return Some("stopped".to_string());
            }
        }
    }
    None
}

fn remove_rustdesk_autorun_artifacts() {
    // Remove atalhos de inicialização automática para evitar abrir GUI a cada boot/login.
    let mut startup_paths: Vec<PathBuf> = Vec::new();
    if let Ok(appdata) = env::var("APPDATA") {
        startup_paths.push(
            Path::new(&appdata)
                .join("Microsoft")
                .join("Windows")
                .join("Start Menu")
                .join("Programs")
                .join("Startup")
                .join("RustDesk.lnk"),
        );
    }
    startup_paths.push(
        Path::new("C:\\ProgramData")
            .join("Microsoft")
            .join("Windows")
            .join("Start Menu")
            .join("Programs")
            .join("Startup")
            .join("RustDesk.lnk"),
    );

    for path in startup_paths {
        if path.exists() {
            match fs::remove_file(&path) {
                Ok(_) => log_event(format!("Atalho de inicialização do RustDesk removido: {}", path.display())),
                Err(error) => log_event(format!(
                    "Falha ao remover atalho de inicialização do RustDesk ({}): {}",
                    path.display(),
                    error
                )),
            }
        }
    }

    for hive in ["HKCU", "HKLM"] {
        let reg_path = format!(r"{}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", hive);
        let status = hidden_command("reg")
            .args(["delete", &reg_path, "/v", "RustDesk", "/f"])
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .status();
        if let Ok(code) = status {
            if code.success() {
                log_event(format!("Entrada de auto-run RustDesk removida de {}", reg_path));
            }
        }
    }
}

fn stop_rustdesk_processes() -> Result<(), RustdeskError> {
    if let Err(error) = try_stop_service() {
        log_event(format!(
            "Não foi possível parar o serviço RustDesk antes da sincronização: {error}"
        ));
    }

    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 /F /T /IM rustdesk.exe".into(),
            status: status.code(),
        })
    }
}

fn try_stop_service() -> Result<(), RustdeskError> {
    match run_sc(&["stop", SERVICE_NAME]) {
        Ok(_) => {
            thread::sleep(Duration::from_secs(2));
            Ok(())
        }
        Err(RustdeskError::CommandFailed { status: Some(code), .. }) if code == 1060 || code == 1062 => Ok(()),
        Err(RustdeskError::CommandFailed { status: Some(5), .. }) => {
            stop_service_elevated().map_err(|error| RustdeskError::CommandFailed {
                command: format!("stop_service_elevated ({error})"),
                status: Some(5),
            })
        }
        Err(error) => Err(error),
    }
}

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 ensure_service_installed(exe_path: &Path) -> Result<(), RustdeskError> {
    if run_sc(&["query", SERVICE_NAME]).is_ok() {
        return Ok(());
    }

    log_event("Serviço RustDesk não encontrado; instalando via CLI");
    run_with_args(exe_path, &["--install-service"])?;
    Ok(())
}

fn query_id_with_retries(exe_path: &Path, attempts: usize) -> Result<String, RustdeskError> {
    let mut last_error: Option<RustdeskError> = None;
    for attempt in 0..attempts {
        match query_id(exe_path) {
            Ok(value) if !value.trim().is_empty() => return Ok(value),
            Ok(_) => {
                last_error = Some(RustdeskError::MissingId);
            }
            Err(error) => {
                last_error = Some(error);
            }
        }
        if attempt + 1 < attempts {
            thread::sleep(Duration::from_millis(800));
        }
    }
    Err(last_error.unwrap_or(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 ensure_remote_id_files(id: &str) {
    for dir in remote_id_directories() {
        let path = dir.join("RustDesk_local.toml");
        match write_remote_id_value(&path, id) {
            Ok(_) => log_event(format!(
                "remote_id atualizado para {} em {}",
                id,
                path.display()
            )),
            Err(error) => log_event(format!(
                "Falha ao atualizar remote_id em {}: {error}",
                path.display()
            )),
        }
    }
}

fn remote_id_directories() -> Vec<PathBuf> {
    let mut dirs = Vec::new();
    dirs.push(program_data_config_dir());
    for profile in service_profile_dirs() {
        dirs.push(profile);
    }
    if let Some(appdir) = user_appdata_config_dir() {
        dirs.push(appdir);
    }
    dirs
}

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

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

fn ensure_password_files(secret: &str) -> Result<(), String> {
    let mut errors = Vec::new();

    for dir in remote_id_directories() {
        let password_path = dir.join("RustDesk.toml");
        if let Err(error) = write_toml_kv(&password_path, "password", secret) {
            errors.push(format!("{} -> {}", password_path.display(), error));
        } else {
            log_event(format!(
                "Senha escrita via fallback em {}",
                password_path.display()
            ));
        }

        let local_path = dir.join("RustDesk_local.toml");
        if let Err(error) = write_toml_kv(&local_path, "verification-method", SECURITY_VERIFICATION_VALUE) {
            log_event(format!(
                "Falha ao ajustar verification-method em {}: {error}",
                local_path.display()
            ));
        } else {
            log_event(format!(
                "verification-method atualizado para {} em {}",
                SECURITY_VERIFICATION_VALUE,
                local_path.display()
            ));
        }

        let rustdesk2_path = dir.join("RustDesk2.toml");
        if let Err(error) = enforce_security_in_rustdesk2(&rustdesk2_path) {
            log_event(format!(
                "Falha ao ajustar flags no RustDesk2.toml em {}: {error}",
                rustdesk2_path.display()
            ));
        }

        if let Err(error) = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE) {
            log_event(format!(
                "Falha ao ajustar approve-mode em {}: {error}",
                local_path.display()
            ));
        } else {
            log_event(format!(
                "approve-mode atualizado para {} em {}",
                SECURITY_APPROVE_MODE_VALUE,
                local_path.display()
            ));
        }
    }

    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors.join(" | "))
    }
}

fn enforce_security_flags() -> Result<(), String> {
    let mut errors = Vec::new();
    for dir in remote_id_directories() {
        let local_path = dir.join("RustDesk_local.toml");
        if let Err(error) = write_toml_kv(&local_path, "verification-method", SECURITY_VERIFICATION_VALUE) {
            errors.push(format!("{} -> {}", local_path.display(), error));
        } else {
            log_event(format!(
                "verification-method atualizado para {} em {}",
                SECURITY_VERIFICATION_VALUE,
                local_path.display()
            ));
        }

        if let Err(error) = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE) {
            errors.push(format!("{} -> {}", local_path.display(), error));
        } else {
            log_event(format!(
                "approve-mode atualizado para {} em {}",
                SECURITY_APPROVE_MODE_VALUE,
                local_path.display()
            ));
        }
    }

    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors.join(" | "))
    }
}

fn enforce_security_in_rustdesk2(path: &Path) -> io::Result<()> {
    write_toml_kv(path, "verification-method", SECURITY_VERIFICATION_VALUE)?;
    write_toml_kv(path, "approve-mode", SECURITY_APPROVE_MODE_VALUE)?;
    Ok(())
}

fn propagate_password_profile() -> io::Result<bool> {
    let Some(src_dir) = user_appdata_config_dir() else {
        log_event("AppData do usuário não disponível para copiar RustDesk.toml (propagação ignorada)");
        return Ok(false);
    };

    let mut propagated = false;

    for filename in PROPAGATION_FILES {
        let src_path = src_dir.join(filename);
        if !src_path.exists() {
            continue;
        }
        log_event(format!(
            "Copiando {} para ProgramData/serviços",
            src_path.display()
        ));

        for dest_root in propagation_destinations() {
            let target_path = dest_root.join(filename);
            copy_overwrite(&src_path, &target_path)?;
            log_event(format!(
                "{} propagado para {}",
                filename,
                target_path.display()
            ));
            propagated = true;
        }
    }

    if !propagated {
        log_event("Nenhum arquivo de perfil encontrado para propagação; aplicando fallback");
    }

    Ok(propagated)
}

fn replicate_password_artifacts() -> io::Result<()> {
    let Some(src) = user_appdata_config_dir() else {
        return Ok(());
    };
    let destinations = propagation_destinations();
    let candidates = ["password", "passwd", "passwd.txt"];

    for dest in destinations {
        fs::create_dir_all(&dest)?;
        for name in candidates {
            let source_path = src.join(name);
            if !source_path.exists() {
                continue;
            }
            let metadata = match fs::metadata(&source_path) {
                Ok(data) => data,
                Err(_) => continue,
            };
            if !metadata.is_file() || metadata.len() == 0 {
                continue;
            }

            let target_path = dest.join(name);
            copy_overwrite(&source_path, &target_path)?;
            log_event(format!(
                "Artefato de senha {name} replicado para {}",
                target_path.display()
            ));
        }
    }

    Ok(())
}

fn purge_existing_rustdesk_profiles() -> Result<(), String> {
    let mut errors = Vec::new();

    for dir in remote_id_directories() {
        match purge_config_dir(&dir) {
            Ok(true) => {
                log_event(format!(
                    "Perfis antigos removidos em {}",
                    dir.display()
                ));
            }
            Ok(false) => {}
            Err(error) => errors.push(format!("{} -> {error}", dir.display())),
        }
    }

    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors.join(" | "))
    }
}

fn purge_config_dir(dir: &Path) -> Result<bool, io::Error> {
    if !dir.exists() {
        return Ok(false);
    }

    let mut removed = false;
    fs::create_dir_all(dir)?;

    for name in RUSTDESK_CONFIG_FILES {
        let path = dir.join(name);
        if path.is_dir() {
            fs::remove_dir_all(&path)?;
            removed = true;
            continue;
        }
        if path.exists() {
            fs::remove_file(&path)?;
            removed = true;
        }
    }

    Ok(removed)
}

#[allow(dead_code)]
fn run_powershell_elevated(script: &str) -> Result<(), String> {
    let temp_dir = env::temp_dir();
    let payload = temp_dir.join("raven_payload.ps1");
    fs::write(&payload, script).map_err(|error| format!("write payload: {error}"))?;

    let launcher = temp_dir.join("raven_launcher.ps1");
    let launcher_body = format!(
        r#"
$ErrorActionPreference='Stop'
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName  = 'powershell.exe'
$psi.Arguments = '-NoProfile -ExecutionPolicy Bypass -File "{payload}"'
$psi.Verb      = 'runas'
$psi.WindowStyle = 'Hidden'
$process = [System.Diagnostics.Process]::Start($psi)
$process.WaitForExit()
exit $process.ExitCode
"#,
        payload = payload.display()
    );
    fs::write(&launcher, launcher_body).map_err(|error| format!("write launcher: {error}"))?;

    let status = Command::new("powershell")
        .creation_flags(CREATE_NO_WINDOW)
        .args([
            "-NoProfile",
            "-ExecutionPolicy",
            "Bypass",
            "-File",
            &launcher.to_string_lossy(),
        ])
        .status()
        .map_err(|error| format!("spawn ps: {error}"))?;

    let _ = fs::remove_file(&launcher);
    let _ = fs::remove_file(&payload);

    if let Some(code) = status.code() {
        if code == 0 || code == 1 {
            return Ok(());
        }
    } else if status.success() {
        return Ok(());
    }
    Err(format!("elevated ps exit {:?}", status.code()))
}

#[allow(dead_code)]
fn fix_profile_acl(target: &Path) -> Result<(), String> {
    let target_str = target.display().to_string();
    let transcript = env::temp_dir().join("raven_acl_ps.log");
    let log_str = transcript.display().to_string();
    let script = format!(
        r#"
$ErrorActionPreference='Stop'
Start-Transcript -Path '{log}' -Force
try {{
  if (-not (Test-Path '{target}')) {{ New-Item -ItemType Directory -Force -Path '{target}' | Out-Null }}

  & takeown /F '{target}' /R /D Y
  $takeCode = $LASTEXITCODE

  & icacls '{target}' /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
  $icaCode = $LASTEXITCODE

  if (($takeCode -eq 0) -and ($icaCode -in 0,1)) {{ exit 0 }}
  if ($icaCode -ne 0) {{ exit $icaCode }}
  exit $takeCode
}} catch {{
  Write-Host ("exception: " + ($_.Exception.Message))
  exit 1
}} finally {{
  try {{ Stop-Transcript | Out-Null }} catch {{ }}
}}
"#,
        target = target_str,
        log = log_str
    );

    let result = run_powershell_elevated(&script);
    if result.is_err() {
        if let Ok(content) = fs::read_to_string(&transcript) {
            log_event(format!(
                "ACL transcript para {}:\n{}",
                target.display(), content
            ));
        }
    }
    let _ = fs::remove_file(&transcript);
    result
}

fn ensure_service_profiles_writable_preflight() -> Result<(), String> {
    // Verificamos se os diretorios de perfil sao graváveis
    // Se nao forem, apenas logamos aviso - o Raven Service deve lidar com isso
    // Nao usamos elevacao para evitar UAC adicional
    let mut blocked_dirs = Vec::new();
    for dir in service_profile_dirs() {
        if !can_write_dir(&dir) {
            blocked_dirs.push(dir);
        }
    }

    if blocked_dirs.is_empty() {
        return Ok(());
    }

    // Apenas logamos aviso - o serviço RavenService deve lidar com permissões
    log_event(format!(
        "Aviso: alguns perfis de serviço não são graváveis: {:?}. O Raven Service deve configurar permissões.",
        blocked_dirs.iter().map(|d| d.display().to_string()).collect::<Vec<_>>()
    ));

    // Retornamos Ok para não bloquear o fluxo
    // O Raven Service, rodando como LocalSystem, pode gravar nesses diretórios
    Ok(())
}

fn stop_service_elevated() -> Result<(), String> {
    // Tentamos parar o serviço RustDesk sem elevação
    // Se falhar, apenas logamos aviso - o Raven Service pode lidar com isso
    // Não usamos elevação para evitar UAC adicional
    let output = Command::new("sc")
        .args(["stop", "RustDesk"])
        .output();

    match output {
        Ok(result) => {
            if result.status.success() {
                // Aguarda um pouco para o serviço parar
                std::thread::sleep(std::time::Duration::from_secs(2));
                Ok(())
            } else {
                let stderr = String::from_utf8_lossy(&result.stderr);
                log_event(format!(
                    "Aviso: não foi possível parar o serviço RustDesk sem elevação: {}",
                    stderr.trim()
                ));
                // Retornamos Ok para não bloquear - o serviço pode estar já parado
                Ok(())
            }
        }
        Err(e) => {
            log_event(format!("Aviso: falha ao executar sc stop RustDesk: {e}"));
            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 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)?;
    }
    if path.is_dir() {
        fs::remove_dir_all(path)?;
    }
    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 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 log_password_replication(secret: &str) {
    for dir in remote_id_directories() {
        let primary = dir.join("RustDesk.toml");
        log_password_match(&primary, secret);

        let local_path = dir.join("RustDesk_local.toml");
        log_password_match(&local_path, secret);
    }
}

fn log_password_match(path: &Path, secret: &str) {
    match read_password_from_file(path) {
        Some(value) if value == secret => {
            log_event(format!(
                "Senha confirmada em {} ({})",
                path.display(),
                mask_secret(&value)
            ));
        }
        Some(value) => {
            log_event(format!(
                "Aviso: senha divergente ({}) em {}",
                mask_secret(&value),
                path.display()
            ));
        }
        None => {
            log_event(format!(
                "Aviso: chave 'password' não encontrada em {}",
                path.display()
            ));
        }
    }
}

fn read_password_from_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, "password") {
            return Some(value);
        }
    }
    None
}

fn mask_secret(secret: &str) -> String {
    if secret.is_empty() {
        return "<vazio>".to_string();
    }
    let chars: Vec<char> = secret.chars().collect();
    if chars.len() <= 4 {
        return "*".repeat(chars.len());
    }
    let prefix: String = chars.iter().take(2).copied().collect();
    let suffix: String = chars
        .iter()
        .rev()
        .take(2)
        .copied()
        .collect::<Vec<char>>()
        .into_iter()
        .rev()
        .collect();
    format!("{}***{}", prefix, suffix)
}

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

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 log_event(message: impl AsRef<str>) {
    if let Some(dir) = logs_directory() {
        if let Err(error) = append_log(dir, message.as_ref()) {
            eprintln!("[rustdesk][log] Falha ao registrar log: {error}");
        }
    }
}

fn logs_directory() -> Option<PathBuf> {
    let base = env::var("LOCALAPPDATA").ok()?;
    Some(
        Path::new(&base)
            .join("br.com.esdrasrenan.sistemadechamados")
            .join("logs"),
    )
}

fn append_log(dir: PathBuf, message: &str) -> io::Result<()> {
    fs::create_dir_all(&dir)?;
    let log_path = dir.join("rustdesk.log");
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(log_path)?;
    let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
    writeln!(file, "[{timestamp}] {message}")?;
    Ok(())
}

fn raven_appdata_root() -> Option<PathBuf> {
    env::var("LOCALAPPDATA")
        .ok()
        .map(|value| Path::new(&value).join(APP_IDENTIFIER))
}

fn machine_store_path() -> Option<PathBuf> {
    raven_appdata_root().map(|dir| dir.join(MACHINE_STORE_FILENAME))
}

fn read_machine_store_object() -> Option<JsonMap<String, JsonValue>> {
    let path = machine_store_path()?;
    let contents = fs::read_to_string(path).ok()?;
    let value: JsonValue = serde_json::from_str(&contents).ok()?;
    value.as_object().cloned()
}

fn write_machine_store_object(map: JsonMap<String, JsonValue>) -> Result<(), String> {
    let path = machine_store_path().ok_or_else(|| "LOCALAPPDATA não disponível".to_string())?;
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent).map_err(|error| format!("mkdir AppData: {error}"))?;
    }
    let serialized = serde_json::to_vec_pretty(&JsonValue::Object(map))
        .map_err(|error| format!("serialize machine-agent: {error}"))?;
    fs::write(&path, serialized).map_err(|error| format!("write machine-agent: {error}"))?;
    Ok(())
}

fn upsert_machine_store_value(key: &str, value: JsonValue) -> Result<(), String> {
    let mut map = read_machine_store_object().unwrap_or_default();
    map.insert(key.to_string(), value);
    write_machine_store_object(map)
}

#[allow(dead_code)]
fn machine_store_key_exists(key: &str) -> bool {
    read_machine_store_object()
        .map(|map| map.contains_key(key))
        .unwrap_or(false)
}

#[allow(dead_code)]
fn acl_flag_file_path() -> Option<PathBuf> {
    raven_appdata_root().map(|dir| dir.join(ACL_FLAG_FILENAME))
}

#[allow(dead_code)]
fn has_acl_unlock_flag() -> bool {
    if let Some(flag) = acl_flag_file_path() {
        if flag.exists() {
            return true;
        }
    }
    machine_store_key_exists(RUSTDESK_ACL_STORE_KEY)
}

#[allow(dead_code)]
fn mark_acl_unlock_flag() {
    let timestamp = Utc::now().timestamp_millis();
    if let Some(flag_path) = acl_flag_file_path() {
        if let Some(parent) = flag_path.parent() {
            let _ = fs::create_dir_all(parent);
        }
        if let Err(error) = fs::write(&flag_path, timestamp.to_string()) {
            log_event(format!(
                "Falha ao gravar flag de ACL em {}: {error}",
                flag_path.display()
            ));
        }
    }

    if let Err(error) = upsert_machine_store_value(RUSTDESK_ACL_STORE_KEY, JsonValue::from(timestamp)) {
        log_event(format!(
            "Falha ao registrar flag de ACL no machine-agent: {error}"
        ));
    }
}

fn get_machine_store_path() -> Result<PathBuf, RustdeskError> {
    let base = env::var("LOCALAPPDATA")
        .map_err(|_| RustdeskError::MissingId)?;
    Ok(Path::new(&base)
        .join(APP_IDENTIFIER)
        .join(MACHINE_STORE_FILENAME))
}

fn sync_remote_access_with_backend(result: &crate::RustdeskProvisioningResult) -> Result<(), RustdeskError> {
    log_event("Iniciando sincronizacao com backend...");

    // Le token e config do store
    let store_path = get_machine_store_path()?;
    let store_content = fs::read_to_string(&store_path)
        .map_err(RustdeskError::Io)?;
    let store: serde_json::Value = serde_json::from_str(&store_content)
        .map_err(|_| RustdeskError::MissingId)?;

    let token = store.get("token")
        .and_then(|v| v.as_str())
        .ok_or(RustdeskError::MissingId)?;

    let config = store.get("config")
        .ok_or(RustdeskError::MissingId)?;

    let machine_id = config.get("machineId")
        .and_then(|v| v.as_str())
        .ok_or(RustdeskError::MissingId)?;

    let api_base_url = config.get("apiBaseUrl")
        .and_then(|v| v.as_str())
        .unwrap_or("https://tickets.esdrasrenan.com.br");

    log_event(format!("Sincronizando com backend: {} (machineId: {})", api_base_url, machine_id));

    // Monta payload conforme schema esperado pelo backend
    // Schema: { machineToken, provider, identifier, password?, url?, username?, notes? }
    let payload = serde_json::json!({
        "machineToken": token,
        "provider": "RustDesk",
        "identifier": result.id,
        "password": result.password,
        "notes": format!("Versao: {}. Provisionado em: {}",
            result.installed_version.as_deref().unwrap_or("desconhecida"),
            result.last_provisioned_at)
    });

    // Faz POST para /api/machines/remote-access
    let client = Client::builder()
        .user_agent(USER_AGENT)
        .timeout(Duration::from_secs(30))
        .build()?;

    let url = format!("{}/api/machines/remote-access", api_base_url);
    let response = client.post(&url)
        .header("Content-Type", "application/json")
        .header("Idempotency-Key", format!("{}:RustDesk:{}", machine_id, result.id))
        .body(payload.to_string())
        .send()?;

    if response.status().is_success() {
        log_event(format!("Sync com backend OK: status {}", response.status()));
        Ok(())
    } else {
        let status = response.status();
        let body = response.text().unwrap_or_default();
        let body_preview = if body.len() > 200 { &body[..200] } else { &body };
        log_event(format!("Sync com backend falhou: {} - {}", status, body_preview));
        Err(RustdeskError::CommandFailed {
            command: "sync_remote_access".to_string(),
            status: Some(status.as_u16() as i32)
        })
    }
}
