//! Raven Service - Servico Windows para operacoes privilegiadas
//!
//! Este servico roda como LocalSystem e executa operacoes que requerem
//! privilegios de administrador, como:
//! - Aplicar politicas de USB
//! - Provisionar e configurar RustDesk
//! - Modificar chaves de registro em HKEY_LOCAL_MACHINE
//!
//! O app Raven UI comunica com este servico via Named Pipes.

mod ipc;
mod rustdesk;
mod usb_policy;

use std::ffi::OsString;
use std::time::Duration;
use tracing::{error, info};
use windows_service::{
    define_windows_service,
    service::{
        ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
        ServiceType,
    },
    service_control_handler::{self, ServiceControlHandlerResult},
    service_dispatcher,
};

const SERVICE_NAME: &str = "RavenService";
const SERVICE_DISPLAY_NAME: &str = "Raven Desktop Service";
const SERVICE_DESCRIPTION: &str = "Servico do Raven Desktop para operacoes privilegiadas (USB, RustDesk)";
const PIPE_NAME: &str = r"\\.\pipe\RavenService";

define_windows_service!(ffi_service_main, service_main);

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configura logging
    init_logging();

    // Verifica argumentos de linha de comando
    let args: Vec<String> = std::env::args().collect();

    if args.len() > 1 {
        match args[1].as_str() {
            "install" => {
                install_service()?;
                return Ok(());
            }
            "uninstall" => {
                uninstall_service()?;
                return Ok(());
            }
            "run" => {
                // Modo de teste: roda sem registrar como servico
                info!("Executando em modo de teste (nao como servico)");
                run_standalone()?;
                return Ok(());
            }
            _ => {}
        }
    }

    // Inicia como servico Windows
    info!("Iniciando Raven Service...");
    service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
    Ok(())
}

fn init_logging() {
    use tracing_subscriber::{fmt, prelude::*, EnvFilter};

    // Tenta criar diretorio de logs
    let log_dir = std::env::var("PROGRAMDATA")
        .map(|p| std::path::PathBuf::from(p).join("RavenService").join("logs"))
        .unwrap_or_else(|_| std::path::PathBuf::from("C:\\ProgramData\\RavenService\\logs"));

    let _ = std::fs::create_dir_all(&log_dir);

    // Arquivo de log
    let log_file = log_dir.join("service.log");
    let file = std::fs::OpenOptions::new()
        .create(true)
        .append(true)
        .open(&log_file)
        .ok();

    let filter = EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| EnvFilter::new("info"));

    if let Some(file) = file {
        tracing_subscriber::registry()
            .with(filter)
            .with(fmt::layer().with_writer(file).with_ansi(false))
            .init();
    } else {
        tracing_subscriber::registry()
            .with(filter)
            .with(fmt::layer())
            .init();
    }
}

fn service_main(arguments: Vec<OsString>) {
    if let Err(e) = run_service(arguments) {
        error!("Erro ao executar servico: {}", e);
    }
}

fn run_service(_arguments: Vec<OsString>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    info!("Servico iniciando...");

    // Canal para shutdown
    let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
    let shutdown_tx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_tx)));

    // Registra handler de controle do servico
    let shutdown_tx_clone = shutdown_tx.clone();
    let status_handle = service_control_handler::register(SERVICE_NAME, move |control| {
        match control {
            ServiceControl::Stop | ServiceControl::Shutdown => {
                info!("Recebido comando de parada");
                if let Ok(mut guard) = shutdown_tx_clone.lock() {
                    if let Some(tx) = guard.take() {
                        let _ = tx.send(());
                    }
                }
                ServiceControlHandlerResult::NoError
            }
            ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
            _ => ServiceControlHandlerResult::NotImplemented,
        }
    })?;

    // Atualiza status para Running
    status_handle.set_service_status(ServiceStatus {
        service_type: ServiceType::OWN_PROCESS,
        current_state: ServiceState::Running,
        controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN,
        exit_code: ServiceExitCode::Win32(0),
        checkpoint: 0,
        wait_hint: Duration::default(),
        process_id: None,
    })?;

    info!("Servico em execucao, aguardando conexoes...");

    // Cria runtime Tokio
    let runtime = tokio::runtime::Runtime::new()?;

    // Executa servidor IPC
    runtime.block_on(async {
        tokio::select! {
            result = ipc::run_server(PIPE_NAME) => {
                if let Err(e) = result {
                    error!("Erro no servidor IPC: {}", e);
                }
            }
            _ = async {
                let _ = shutdown_rx.await;
            } => {
                info!("Shutdown solicitado");
            }
        }
    });

    // Atualiza status para Stopped
    status_handle.set_service_status(ServiceStatus {
        service_type: ServiceType::OWN_PROCESS,
        current_state: ServiceState::Stopped,
        controls_accepted: ServiceControlAccept::empty(),
        exit_code: ServiceExitCode::Win32(0),
        checkpoint: 0,
        wait_hint: Duration::default(),
        process_id: None,
    })?;

    info!("Servico parado");
    Ok(())
}

fn run_standalone() -> Result<(), Box<dyn std::error::Error>> {
    let runtime = tokio::runtime::Runtime::new()?;

    runtime.block_on(async {
        info!("Servidor IPC iniciando em modo standalone...");

        tokio::select! {
            result = ipc::run_server(PIPE_NAME) => {
                if let Err(e) = result {
                    error!("Erro no servidor IPC: {}", e);
                }
            }
            _ = tokio::signal::ctrl_c() => {
                info!("Ctrl+C recebido, encerrando...");
            }
        }
    });

    Ok(())
}

fn install_service() -> Result<(), Box<dyn std::error::Error>> {
    use windows_service::{
        service::{ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType},
        service_manager::{ServiceManager, ServiceManagerAccess},
    };

    info!("Instalando servico...");

    let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CREATE_SERVICE)?;

    let exe_path = std::env::current_exe()?;

    let service_info = ServiceInfo {
        name: OsString::from(SERVICE_NAME),
        display_name: OsString::from(SERVICE_DISPLAY_NAME),
        service_type: ServiceType::OWN_PROCESS,
        start_type: ServiceStartType::AutoStart,
        error_control: ServiceErrorControl::Normal,
        executable_path: exe_path,
        launch_arguments: vec![],
        dependencies: vec![],
        account_name: None, // LocalSystem
        account_password: None,
    };

    let service = manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;

    // Define descricao
    service.set_description(SERVICE_DESCRIPTION)?;

    info!("Servico instalado com sucesso: {}", SERVICE_NAME);
    println!("Servico '{}' instalado com sucesso!", SERVICE_DISPLAY_NAME);
    println!("Para iniciar: sc start {}", SERVICE_NAME);

    Ok(())
}

fn uninstall_service() -> Result<(), Box<dyn std::error::Error>> {
    use windows_service::{
        service::ServiceAccess,
        service_manager::{ServiceManager, ServiceManagerAccess},
    };

    info!("Desinstalando servico...");

    let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;

    let service = manager.open_service(
        SERVICE_NAME,
        ServiceAccess::STOP | ServiceAccess::DELETE | ServiceAccess::QUERY_STATUS,
    )?;

    // Tenta parar o servico primeiro
    let status = service.query_status()?;
    if status.current_state != ServiceState::Stopped {
        info!("Parando servico...");
        let _ = service.stop();
        std::thread::sleep(Duration::from_secs(2));
    }

    // Remove o servico
    service.delete()?;

    info!("Servico desinstalado com sucesso");
    println!("Servico '{}' removido com sucesso!", SERVICE_DISPLAY_NAME);

    Ok(())
}
