mod agent;
mod chat;
#[cfg(target_os = "windows")]
mod rustdesk;
#[cfg(target_os = "windows")]
mod service_client;
mod usb_control;

use agent::{collect_inventory_plain, collect_profile, AgentRuntime, MachineProfile};
use chat::{ChatRuntime, ChatSession, ChatMessagesResponse, SendMessageResponse};
use chrono::Local;
use usb_control::{UsbPolicy, UsbPolicyResult};
use tauri::{Emitter, Listener, Manager, WindowEvent};
use tauri_plugin_store::Builder as StorePluginBuilder;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
#[cfg(target_os = "windows")]
use tauri::menu::{MenuBuilder, MenuItemBuilder};
#[cfg(target_os = "windows")]
use tauri::tray::TrayIconBuilder;
#[cfg(target_os = "windows")]
use winreg::enums::*;
#[cfg(target_os = "windows")]
use winreg::RegKey;

const DEFAULT_CONVEX_URL: &str = "https://convex.esdrasrenan.com.br";

// ============================================================================
// Sistema de Logging para Agente
// ============================================================================

static AGENT_LOG_FILE: OnceLock<std::sync::Mutex<std::fs::File>> = OnceLock::new();

pub fn init_agent_logging() -> Result<(), String> {
    let dir = logs_directory()
        .ok_or("LOCALAPPDATA indisponivel para logging")?;

    std::fs::create_dir_all(&dir)
        .map_err(|e| format!("Falha ao criar diretorio de logs: {e}"))?;

    let path = dir.join("raven-agent.log");
    let file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(&path)
        .map_err(|e| format!("Falha ao abrir raven-agent.log: {e}"))?;

    let _ = AGENT_LOG_FILE.set(std::sync::Mutex::new(file));
    Ok(())
}

pub fn log_agent(level: &str, message: &str) {
    let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
    let line = format!("[{timestamp}] [{level}] {message}\n");

    // Escreve para stderr (util em dev/debug)
    eprint!("{line}");

    // Escreve para arquivo
    if let Some(mutex) = AGENT_LOG_FILE.get() {
        if let Ok(mut file) = mutex.lock() {
            let _ = file.write_all(line.as_bytes());
            let _ = file.flush();
        }
    }
}

#[macro_export]
macro_rules! log_info {
    ($($arg:tt)*) => {
        $crate::log_agent("INFO", format!($($arg)*).as_str())
    };
}

#[macro_export]
macro_rules! log_error {
    ($($arg:tt)*) => {
        $crate::log_agent("ERROR", format!($($arg)*).as_str())
    };
}

#[macro_export]
macro_rules! log_warn {
    ($($arg:tt)*) => {
        $crate::log_agent("WARN", format!($($arg)*).as_str())
    };
}

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

#[tauri::command]
fn collect_machine_profile() -> Result<MachineProfile, String> {
    collect_profile().map_err(|error| error.to_string())
}

#[tauri::command]
fn collect_machine_inventory() -> Result<serde_json::Value, String> {
    Ok(collect_inventory_plain())
}

#[tauri::command]
fn start_machine_agent(
    state: tauri::State<AgentRuntime>,
    base_url: String,
    token: String,
    status: Option<String>,
    interval_seconds: Option<u64>,
) -> Result<(), String> {
    state
        .start_heartbeat(base_url, token, status, interval_seconds)
        .map_err(|error| error.to_string())
}

#[tauri::command]
fn stop_machine_agent(state: tauri::State<AgentRuntime>) -> Result<(), String> {
    state.stop();
    Ok(())
}

#[tauri::command]
fn open_devtools(window: tauri::WebviewWindow) -> Result<(), String> {
    window.open_devtools();
    Ok(())
}

#[tauri::command]
fn log_app_event(message: String) -> Result<(), String> {
    append_app_log(&message)
}

fn append_app_log(message: &str) -> Result<(), String> {
    let Some(dir) = logs_directory() else {
        return Err("LOCALAPPDATA indisponivel para gravar logs".to_string());
    };

    std::fs::create_dir_all(&dir)
        .map_err(|error| format!("Falha ao criar pasta de logs: {error}"))?;

    let path = dir.join("app.log");
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(&path)
        .map_err(|error| format!("Falha ao abrir app.log: {error}"))?;

    let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
    writeln!(file, "[{timestamp}] {message}")
        .map_err(|error| format!("Falha ao escrever log: {error}"))?;

    Ok(())
}

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

#[tauri::command]
async fn ensure_rustdesk_and_emit(
    app: tauri::AppHandle,
    config_string: Option<String>,
    password: Option<String>,
    machine_id: Option<String>,
) -> Result<RustdeskProvisioningResult, String> {
    let result = tauri::async_runtime::spawn_blocking(move || {
        run_rustdesk_ensure(config_string, password, machine_id)
    })
    .await
    .map_err(|error| error.to_string())??;

    if let Err(error) = app.emit("raven://remote-access/provisioned", &result) {
        eprintln!("[rustdesk] falha ao emitir evento raven://remote-access/provisioned: {error}");
    }

    Ok(result)
}

#[cfg(target_os = "windows")]
fn run_rustdesk_ensure(
    config_string: Option<String>,
    password: Option<String>,
    machine_id: Option<String>,
) -> Result<RustdeskProvisioningResult, String> {
    // Tenta usar o servico primeiro (sem UAC)
    if service_client::is_service_available() {
        log_info!("Usando Raven Service para provisionar RustDesk");
        match service_client::provision_rustdesk(
            config_string.as_deref(),
            password.as_deref(),
            machine_id.as_deref(),
        ) {
            Ok(result) => {
                return Ok(RustdeskProvisioningResult {
                    id: result.id,
                    password: result.password,
                    installed_version: result.installed_version,
                    updated: result.updated,
                    last_provisioned_at: result.last_provisioned_at,
                });
            }
            Err(e) => {
                log_warn!("Falha ao usar servico para RustDesk: {e}");
                // Continua para fallback
            }
        }
    }

    // Fallback: chamada direta (pode pedir UAC)
    log_info!("Usando chamada direta para provisionar RustDesk (pode pedir UAC)");
    rustdesk::ensure_rustdesk(
        config_string.as_deref(),
        password.as_deref(),
        machine_id.as_deref(),
    )
    .map_err(|error| error.to_string())
}

#[cfg(not(target_os = "windows"))]
fn run_rustdesk_ensure(
    _config_string: Option<String>,
    _password: Option<String>,
    _machine_id: Option<String>,
) -> Result<RustdeskProvisioningResult, String> {
    Err("Provisionamento automático do RustDesk está disponível apenas no Windows.".to_string())
}

#[tauri::command]
fn apply_usb_policy(policy: String) -> Result<UsbPolicyResult, String> {
    // Valida a politica primeiro
    let _policy_enum = UsbPolicy::from_str(&policy)
        .ok_or_else(|| format!("Politica USB invalida: {}. Use ALLOW, BLOCK_ALL ou READONLY.", policy))?;

    // Tenta usar o servico primeiro (sem UAC)
    #[cfg(target_os = "windows")]
    if service_client::is_service_available() {
        log_info!("Usando Raven Service para aplicar politica USB: {}", policy);
        match service_client::apply_usb_policy(&policy) {
            Ok(result) => {
                return Ok(UsbPolicyResult {
                    success: result.success,
                    policy: result.policy,
                    error: result.error,
                    applied_at: result.applied_at,
                });
            }
            Err(e) => {
                log_warn!("Falha ao usar servico para USB policy: {e}");
                // Continua para fallback
            }
        }
    }

    // Fallback: chamada direta (pode pedir UAC)
    log_info!("Usando chamada direta para aplicar politica USB (pode pedir UAC)");
    usb_control::apply_usb_policy(_policy_enum).map_err(|e| e.to_string())
}

#[tauri::command]
fn get_usb_policy() -> Result<String, String> {
    // Tenta usar o servico primeiro
    #[cfg(target_os = "windows")]
    if service_client::is_service_available() {
        match service_client::get_usb_policy() {
            Ok(policy) => return Ok(policy),
            Err(e) => {
                log_warn!("Falha ao obter USB policy via servico: {e}");
                // Continua para fallback
            }
        }
    }

    // Fallback: leitura direta (nao precisa elevacao para ler)
    usb_control::get_current_policy()
        .map(|p| p.as_str().to_string())
        .map_err(|e| e.to_string())
}

#[tauri::command]
fn refresh_usb_policy() -> Result<(), String> {
    usb_control::refresh_group_policy().map_err(|e| e.to_string())
}

// ============================================================================
// COMANDOS DE CHAT
// ============================================================================

#[tauri::command]
fn start_chat_polling(
    state: tauri::State<ChatRuntime>,
    app: tauri::AppHandle,
    base_url: String,
    convex_url: Option<String>,
    token: String,
) -> Result<(), String> {
    let url = convex_url.unwrap_or_else(|| DEFAULT_CONVEX_URL.to_string());
    state.start_polling(base_url, url, token, app)
}

#[tauri::command]
fn stop_chat_polling(state: tauri::State<ChatRuntime>) -> Result<(), String> {
    state.stop();
    Ok(())
}

#[tauri::command]
fn is_chat_using_realtime(state: tauri::State<ChatRuntime>) -> bool {
    state.is_using_sse()
}

#[tauri::command]
fn get_chat_sessions(state: tauri::State<ChatRuntime>) -> Vec<ChatSession> {
    state.get_sessions()
}

#[tauri::command]
async fn fetch_chat_sessions(base_url: String, token: String) -> Result<Vec<ChatSession>, String> {
    chat::fetch_sessions(&base_url, &token).await
}

#[tauri::command]
async fn fetch_chat_messages(
    base_url: String,
    token: String,
    ticket_id: String,
    since: Option<i64>,
) -> Result<ChatMessagesResponse, String> {
    chat::fetch_messages(&base_url, &token, &ticket_id, since).await
}

#[tauri::command]
async fn send_chat_message(
    base_url: String,
    token: String,
    ticket_id: String,
    body: String,
    attachments: Option<Vec<chat::AttachmentPayload>>,
) -> Result<SendMessageResponse, String> {
    chat::send_message(&base_url, &token, &ticket_id, &body, attachments).await
}

#[tauri::command]
async fn mark_chat_messages_read(
    base_url: String,
    token: String,
    ticket_id: String,
    message_ids: Vec<String>,
) -> Result<(), String> {
    if message_ids.is_empty() {
        return Ok(());
    }
    chat::mark_messages_read(&base_url, &token, &ticket_id, &message_ids).await
}

#[tauri::command]
async fn upload_chat_file(
    base_url: String,
    token: String,
    file_path: String,
) -> Result<chat::AttachmentPayload, String> {
    use std::path::Path;

    // Ler o arquivo
    let path = Path::new(&file_path);
    let file_name = path
        .file_name()
        .and_then(|n| n.to_str())
        .ok_or("Nome de arquivo inválido")?
        .to_string();

    let file_data = std::fs::read(&file_path)
        .map_err(|e| format!("Falha ao ler arquivo: {e}"))?;

    let file_size = file_data.len() as u64;

    // Validar arquivo
    chat::is_allowed_file(&file_name, file_size)?;

    // Obter tipo MIME
    let mime_type = chat::get_mime_type(&file_name);

    // Gerar URL de upload
    let upload_url = chat::generate_upload_url(
        &base_url,
        &token,
        &file_name,
        &mime_type,
        file_size,
    )
    .await?;

    // Fazer upload
    let storage_id = chat::upload_file(&upload_url, file_data, &mime_type).await?;

    Ok(chat::AttachmentPayload {
        storage_id,
        name: file_name,
        size: Some(file_size),
        mime_type: Some(mime_type),
    })
}

#[tauri::command]
fn open_chat_window(app: tauri::AppHandle, ticket_id: String, ticket_ref: u64) -> Result<(), String> {
    log_info!("[CMD] open_chat_window called: ticket_id={}, ticket_ref={}", ticket_id, ticket_ref);
    let result = chat::open_chat_window(&app, &ticket_id, ticket_ref);
    log_info!("[CMD] open_chat_window result: {:?}", result);
    result
}

#[tauri::command]
fn close_chat_window(app: tauri::AppHandle, ticket_id: String) -> Result<(), String> {
    chat::close_chat_window(&app, &ticket_id)
}

#[tauri::command]
fn minimize_chat_window(app: tauri::AppHandle, ticket_id: String) -> Result<(), String> {
    chat::minimize_chat_window(&app, &ticket_id)
}

#[tauri::command]
fn set_chat_minimized(app: tauri::AppHandle, ticket_id: String, minimized: bool) -> Result<(), String> {
    chat::set_chat_minimized(&app, &ticket_id, minimized)
}

#[tauri::command]
fn open_hub_window(app: tauri::AppHandle) -> Result<(), String> {
    chat::open_hub_window(&app)
}

#[tauri::command]
fn close_hub_window(app: tauri::AppHandle) -> Result<(), String> {
    chat::close_hub_window(&app)
}

#[tauri::command]
fn set_hub_minimized(app: tauri::AppHandle, minimized: bool) -> Result<(), String> {
    chat::set_hub_minimized(&app, minimized)
}

// ============================================================================
// Handler de Deep Link (raven://)
// ============================================================================

/// Processa URLs do protocolo raven://
/// Formatos suportados:
/// - raven://ticket/{token} - Abre visualizacao do chamado
/// - raven://chat/{ticketId}?token={token} - Abre chat do chamado
/// - raven://rate/{token} - Abre avaliacao do chamado
fn handle_deep_link(app: &tauri::AppHandle, url: &str) {
    log_info!("Processando deep link: {url}");

    // Remove o prefixo raven://
    let path = url.trim_start_matches("raven://");

    // Parse do path
    let parts: Vec<&str> = path.split('/').collect();

    if parts.is_empty() {
        log_warn!("Deep link invalido: path vazio");
        return;
    }

    match parts[0] {
        "ticket" => {
            if parts.len() > 1 {
                let token = parts[1].split('?').next().unwrap_or(parts[1]);
                log_info!("Abrindo ticket com token: {token}");

                // Mostra a janela principal
                if let Some(window) = app.get_webview_window("main") {
                    let _ = window.show();
                    let _ = window.set_focus();

                    // Emite evento para o frontend navegar para o ticket
                    let _ = app.emit("raven://deep-link/ticket", serde_json::json!({
                        "token": token
                    }));
                }
            }
        }
        "chat" => {
            if parts.len() > 1 {
                let ticket_id = parts[1].split('?').next().unwrap_or(parts[1]);
                log_info!("Abrindo chat do ticket: {ticket_id}");

                // Abre janela de chat (ticket_ref 0 quando vem de deeplink)
                if let Err(e) = chat::open_chat_window(app, ticket_id, 0) {
                    log_error!("Falha ao abrir chat: {e}");
                }
            }
        }
        "rate" => {
            if parts.len() > 1 {
                let token = parts[1].split('?').next().unwrap_or(parts[1]);
                log_info!("Abrindo avaliacao com token: {token}");

                // Mostra a janela principal
                if let Some(window) = app.get_webview_window("main") {
                    let _ = window.show();
                    let _ = window.set_focus();

                    // Emite evento para o frontend navegar para avaliacao
                    let _ = app.emit("raven://deep-link/rate", serde_json::json!({
                        "token": token
                    }));
                }
            }
        }
        _ => {
            log_warn!("Deep link desconhecido: {path}");
        }
    }
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .manage(AgentRuntime::new())
        .manage(ChatRuntime::new())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_opener::init())
        .plugin(StorePluginBuilder::default().build())
        .plugin(tauri_plugin_updater::Builder::new().build())
        .plugin(tauri_plugin_process::init())
        .plugin(tauri_plugin_notification::init())
        .plugin(tauri_plugin_deep_link::init())
        .plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
            // Quando uma segunda instância tenta iniciar, foca a janela existente
            if let Some(window) = app.get_webview_window("main") {
                let _ = window.show();
                let _ = window.unminimize();
                let _ = window.set_focus();
            }
        }))
        .on_window_event(|window, event| {
            if let WindowEvent::CloseRequested { api, .. } = event {
                api.prevent_close();
                let _ = window.hide();
            }
        })
        .setup(|app| {
            // Inicializa sistema de logging primeiro
            if let Err(e) = init_agent_logging() {
                eprintln!("[raven] Falha ao inicializar logging: {e}");
            }

            log_info!("Raven iniciando...");

            // Configura handler de deep link (raven://)
            #[cfg(desktop)]
            {
                let handle = app.handle().clone();
                app.listen("deep-link://new-url", move |event| {
                    let urls = event.payload();
                    log_info!("Deep link recebido: {urls}");
                    handle_deep_link(&handle, urls);
                });
            }

            #[cfg(target_os = "windows")]
            {
                let start_in_background = std::env::args().any(|arg| arg == "--background");
                setup_raven_autostart();
                setup_tray(app.handle())?;
                if start_in_background {
                    if let Some(win) = app.get_webview_window("main") {
                        let _ = win.hide();
                    }
                }

                // Tenta iniciar o agente e chat em background se houver credenciais salvas
                let app_handle = app.handle().clone();
                let agent_runtime = app.state::<AgentRuntime>().inner().clone();
                let chat_runtime = app.state::<ChatRuntime>().inner().clone();
                tauri::async_runtime::spawn(async move {
                    // Aguarda um pouco para o app estabilizar
                    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
                    if let Err(e) = try_start_background_agent(&app_handle, agent_runtime, chat_runtime).await {
                        log_warn!("Agente nao iniciado em background: {e}");
                    }
                });
            }
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            collect_machine_profile,
            collect_machine_inventory,
            start_machine_agent,
            stop_machine_agent,
            open_devtools,
            log_app_event,
            ensure_rustdesk_and_emit,
            apply_usb_policy,
            get_usb_policy,
            refresh_usb_policy,
            // Chat commands
            start_chat_polling,
            stop_chat_polling,
            is_chat_using_realtime,
            get_chat_sessions,
            fetch_chat_sessions,
            fetch_chat_messages,
            send_chat_message,
            mark_chat_messages_read,
            upload_chat_file,
            open_chat_window,
            close_chat_window,
            minimize_chat_window,
            set_chat_minimized,
            // Hub commands
            open_hub_window,
            close_hub_window,
            set_hub_minimized
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

#[cfg(target_os = "windows")]
fn setup_raven_autostart() {
    let exe_path = match std::env::current_exe() {
        Ok(p) => p,
        Err(e) => {
            log_error!("Falha ao obter caminho do executavel: {e}");
            return;
        }
    };

    let path_str = exe_path.display().to_string();
    // Adiciona flag --background para indicar inicio automatico
    let value = format!("\"{}\" --background", path_str);

    let hkcu = RegKey::predef(HKEY_CURRENT_USER);

    let key = match hkcu.create_subkey(r"Software\Microsoft\Windows\CurrentVersion\Run") {
        Ok((key, _)) => key,
        Err(e) => {
            log_error!("Falha ao criar/abrir chave de registro Run: {e}");
            return;
        }
    };

    if let Err(e) = key.set_value("Raven", &value) {
        log_error!("Falha ao definir valor de auto-start no registro: {e}");
        return;
    }

    log_info!("Auto-start configurado: {value}");

    // Valida que foi salvo corretamente
    match key.get_value::<String, _>("Raven") {
        Ok(saved) => {
            if saved == value {
                log_info!("Auto-start validado: entrada existe no registro");
            } else {
                log_warn!("Auto-start: valor difere. Esperado: {value}, Salvo: {saved}");
            }
        }
        Err(e) => {
            log_warn!("Auto-start: nao foi possivel validar entrada: {e}");
        }
    }
}

#[cfg(target_os = "windows")]
fn setup_tray(app: &tauri::AppHandle) -> tauri::Result<()> {
    let show_item = MenuItemBuilder::with_id("show", "Mostrar").build(app)?;
    let chat_item = MenuItemBuilder::with_id("chat", "Abrir Chat").build(app)?;
    let quit_item = MenuItemBuilder::with_id("quit", "Sair").build(app)?;
    let menu = MenuBuilder::new(app)
        .items(&[&show_item, &chat_item, &quit_item])
        .build()?;

    let mut builder = TrayIconBuilder::new()
        .menu(&menu)
        .on_menu_event(|tray, event| {
            match event.id().as_ref() {
                "show" => {
                    if let Some(win) = tray.app_handle().get_webview_window("main") {
                        let _ = win.show();
                        let _ = win.set_focus();
                    }
                    // Reabrir chat se houver sessao ativa
                    if let Some(chat_runtime) = tray.app_handle().try_state::<ChatRuntime>() {
                        let sessions = chat_runtime.get_sessions();
                        if let Some(session) = sessions.first() {
                            let _ = chat::open_chat_window(tray.app_handle(), &session.ticket_id, session.ticket_ref);
                        }
                    }
                }
                "chat" => {
                    // Abrir janela de chat se houver sessao ativa
                    if let Some(chat_runtime) = tray.app_handle().try_state::<ChatRuntime>() {
                        let sessions = chat_runtime.get_sessions();
                        if sessions.len() > 1 {
                            // Multiplas sessoes - abrir hub
                            if let Err(e) = chat::open_hub_window(tray.app_handle()) {
                                log_error!("Falha ao abrir hub de chat: {e}");
                            }
                        } else if let Some(session) = sessions.first() {
                            // Uma sessao - abrir diretamente
                            if let Err(e) = chat::open_chat_window(tray.app_handle(), &session.ticket_id, session.ticket_ref) {
                                log_error!("Falha ao abrir janela de chat: {e}");
                            }
                        }
                    }
                }
                "quit" => {
                    tray.app_handle().exit(0);
                }
                _ => {}
            }
        })
        .on_tray_icon_event(|tray, event| {
            if let tauri::tray::TrayIconEvent::DoubleClick { .. } = event {
                if let Some(win) = tray.app_handle().get_webview_window("main") {
                    let _ = win.show();
                    let _ = win.set_focus();
                }
                // Reabrir chat se houver sessao ativa
                if let Some(chat_runtime) = tray.app_handle().try_state::<ChatRuntime>() {
                    let sessions = chat_runtime.get_sessions();
                    if let Some(session) = sessions.first() {
                        let _ = chat::open_chat_window(tray.app_handle(), &session.ticket_id, session.ticket_ref);
                    }
                }
            }
        });

    if let Some(icon) = app.default_window_icon() {
        builder = builder.icon(icon.clone());
    }

    builder = builder.tooltip("Raven");

    builder.build(app)?;
    Ok(())
}

#[cfg(target_os = "windows")]
async fn try_start_background_agent(
    app: &tauri::AppHandle,
    agent_runtime: AgentRuntime,
    chat_runtime: ChatRuntime,
) -> Result<(), String> {
    log_info!("Verificando credenciais salvas para iniciar agente...");

    let app_data = app
        .path()
        .app_local_data_dir()
        .map_err(|e| format!("Falha ao obter diretorio de dados: {e}"))?;

    let store_path = app_data.join("machine-agent.json");

    if !store_path.exists() {
        return Err("Nenhuma configuracao encontrada".to_string());
    }

    // Ler arquivo JSON diretamente
    let content = std::fs::read_to_string(&store_path)
        .map_err(|e| format!("Falha ao ler machine-agent.json: {e}"))?;

    let data: serde_json::Value = serde_json::from_str(&content)
        .map_err(|e| format!("Falha ao parsear machine-agent.json: {e}"))?;

    let token = data
        .get("token")
        .and_then(|v| v.as_str())
        .filter(|t| !t.is_empty())
        .ok_or("Token nao encontrado ou vazio")?;

    let config = data.get("config");

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

    let convex_url = config
        .and_then(|c| c.get("convexUrl"))
        .and_then(|v| v.as_str())
        .unwrap_or(DEFAULT_CONVEX_URL);

    let interval = config
        .and_then(|c| c.get("heartbeatIntervalSec"))
        .and_then(|v| v.as_u64())
        .unwrap_or(300);

    log_info!(
        "Iniciando agente em background: url={}, interval={}s",
        api_base_url,
        interval
    );

    agent_runtime
        .start_heartbeat(
            api_base_url.to_string(),
            token.to_string(),
            Some("online".to_string()),
            Some(interval),
        )
        .map_err(|e| format!("Falha ao iniciar heartbeat: {e}"))?;

    // Iniciar sistema de chat (WebSocket + fallback HTTP polling)
    if let Err(e) =
        chat_runtime.start_polling(api_base_url.to_string(), convex_url.to_string(), token.to_string(), app.clone())
    {
        log_warn!("Falha ao iniciar chat em background: {e}");
    } else {
        log_info!("Chat iniciado com sucesso (Convex WebSocket)");
    }

    log_info!("Agente iniciado com sucesso em background");

    Ok(())
}
