//! Modulo IPC - Servidor de Named Pipes
//!
//! Implementa comunicacao entre o Raven UI e o Raven Service
//! usando Named Pipes do Windows com protocolo JSON-RPC simplificado.

use crate::{rustdesk, usb_policy};
use serde::{Deserialize, Serialize};
use std::io::{BufRead, BufReader, Write};
use thiserror::Error;
use tracing::{debug, info, warn};

#[derive(Debug, Error)]
pub enum IpcError {
    #[error("Erro de IO: {0}")]
    Io(#[from] std::io::Error),

    #[error("Erro de serializacao: {0}")]
    Json(#[from] serde_json::Error),
}

/// Requisicao JSON-RPC simplificada
#[derive(Debug, Deserialize)]
pub struct Request {
    pub id: String,
    pub method: String,
    #[serde(default)]
    pub params: serde_json::Value,
}

/// Resposta JSON-RPC simplificada
#[derive(Debug, Serialize)]
pub struct Response {
    pub id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub result: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<ErrorResponse>,
}

#[derive(Debug, Serialize)]
pub struct ErrorResponse {
    pub code: i32,
    pub message: String,
}

impl Response {
    pub fn success(id: String, result: serde_json::Value) -> Self {
        Self {
            id,
            result: Some(result),
            error: None,
        }
    }

    pub fn error(id: String, code: i32, message: String) -> Self {
        Self {
            id,
            result: None,
            error: Some(ErrorResponse { code, message }),
        }
    }
}

/// Inicia o servidor de Named Pipes
pub async fn run_server(pipe_name: &str) -> Result<(), IpcError> {
    info!("Iniciando servidor IPC em: {}", pipe_name);

    loop {
        match accept_connection(pipe_name).await {
            Ok(()) => {
                debug!("Conexao processada com sucesso");
            }
            Err(e) => {
                warn!("Erro ao processar conexao: {}", e);
            }
        }
    }
}

/// Aceita uma conexao e processa requisicoes
async fn accept_connection(pipe_name: &str) -> Result<(), IpcError> {
    use windows::Win32::Foundation::INVALID_HANDLE_VALUE;
    use windows::Win32::Security::{
        InitializeSecurityDescriptor, SetSecurityDescriptorDacl,
        PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR,
    };
    use windows::Win32::Storage::FileSystem::PIPE_ACCESS_DUPLEX;
    use windows::Win32::System::Pipes::{
        ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe,
        PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT,
    };
    use windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION;
    use windows::core::PCWSTR;

    // Cria o named pipe com seguranca que permite acesso a todos os usuarios
    let pipe_name_wide: Vec<u16> = pipe_name.encode_utf16().chain(std::iter::once(0)).collect();

    // Cria security descriptor com DACL nulo (permite acesso a todos)
    let mut sd = SECURITY_DESCRIPTOR::default();
    unsafe {
        let sd_ptr = PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _);
        let _ = InitializeSecurityDescriptor(sd_ptr, SECURITY_DESCRIPTOR_REVISION);
        // DACL nulo = acesso irrestrito
        let _ = SetSecurityDescriptorDacl(sd_ptr, true, None, false);
    }

    let sa = SECURITY_ATTRIBUTES {
        nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
        lpSecurityDescriptor: &mut sd as *mut _ as *mut _,
        bInheritHandle: false.into(),
    };

    let pipe_handle = unsafe {
        CreateNamedPipeW(
            PCWSTR::from_raw(pipe_name_wide.as_ptr()),
            PIPE_ACCESS_DUPLEX,
            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
            PIPE_UNLIMITED_INSTANCES,
            4096, // out buffer
            4096, // in buffer
            0,    // default timeout
            Some(&sa), // seguranca permissiva
        )
    };

    // Verifica se o handle e valido
    if pipe_handle == INVALID_HANDLE_VALUE {
        return Err(IpcError::Io(std::io::Error::last_os_error()));
    }

    // Aguarda conexao de um cliente
    info!("Aguardando conexao de cliente...");
    let connect_result = unsafe {
        ConnectNamedPipe(pipe_handle, None)
    };

    if let Err(e) = connect_result {
        // ERROR_PIPE_CONNECTED (535) significa que o cliente ja estava conectado
        // o que e aceitavel
        let error_code = e.code().0 as u32;
        if error_code != 535 {
            warn!("Erro ao aguardar conexao: {:?}", e);
        }
    }

    info!("Cliente conectado");

    // Processa requisicoes do cliente
    let result = process_client(pipe_handle);

    // Desconecta o cliente
    unsafe {
        let _ = DisconnectNamedPipe(pipe_handle);
    }

    result
}

/// Processa requisicoes de um cliente conectado
fn process_client(pipe_handle: windows::Win32::Foundation::HANDLE) -> Result<(), IpcError> {
    use std::os::windows::io::{FromRawHandle, RawHandle};
    use std::fs::File;

    // Cria File handle a partir do pipe
    let raw_handle = pipe_handle.0 as RawHandle;
    let file = unsafe { File::from_raw_handle(raw_handle) };

    let reader = BufReader::new(file.try_clone()?);
    let mut writer = file;

    // Le linhas (cada linha e uma requisicao JSON)
    for line in reader.lines() {
        let line = match line {
            Ok(l) => l,
            Err(e) => {
                if e.kind() == std::io::ErrorKind::BrokenPipe {
                    info!("Cliente desconectou");
                    break;
                }
                return Err(e.into());
            }
        };

        if line.is_empty() {
            continue;
        }

        debug!("Requisicao recebida: {}", line);

        // Parse da requisicao
        let response = match serde_json::from_str::<Request>(&line) {
            Ok(request) => handle_request(request),
            Err(e) => Response::error(
                "unknown".to_string(),
                -32700,
                format!("Parse error: {}", e),
            ),
        };

        // Serializa e envia resposta
        let response_json = serde_json::to_string(&response)?;
        debug!("Resposta: {}", response_json);

        writeln!(writer, "{}", response_json)?;
        writer.flush()?;
    }

    // IMPORTANTE: Nao fechar o handle aqui, pois DisconnectNamedPipe precisa dele
    std::mem::forget(writer);

    Ok(())
}

/// Processa uma requisicao e retorna a resposta
fn handle_request(request: Request) -> Response {
    info!("Processando metodo: {}", request.method);

    match request.method.as_str() {
        "health_check" => handle_health_check(request.id),
        "apply_usb_policy" => handle_apply_usb_policy(request.id, request.params),
        "get_usb_policy" => handle_get_usb_policy(request.id),
        "provision_rustdesk" => handle_provision_rustdesk(request.id, request.params),
        "get_rustdesk_status" => handle_get_rustdesk_status(request.id),
        _ => Response::error(
            request.id,
            -32601,
            format!("Metodo nao encontrado: {}", request.method),
        ),
    }
}

// =============================================================================
// Handlers de Requisicoes
// =============================================================================

fn handle_health_check(id: String) -> Response {
    Response::success(
        id,
        serde_json::json!({
            "status": "ok",
            "service": "RavenService",
            "version": env!("CARGO_PKG_VERSION"),
            "timestamp": chrono::Utc::now().timestamp_millis()
        }),
    )
}

fn handle_apply_usb_policy(id: String, params: serde_json::Value) -> Response {
    let policy = match params.get("policy").and_then(|p| p.as_str()) {
        Some(p) => p,
        None => {
            return Response::error(id, -32602, "Parametro 'policy' e obrigatorio".to_string())
        }
    };

    match usb_policy::apply_policy(policy) {
        Ok(result) => Response::success(id, serde_json::to_value(result).unwrap()),
        Err(e) => Response::error(id, -32000, format!("Erro ao aplicar politica: {}", e)),
    }
}

fn handle_get_usb_policy(id: String) -> Response {
    match usb_policy::get_current_policy() {
        Ok(policy) => Response::success(
            id,
            serde_json::json!({
                "policy": policy
            }),
        ),
        Err(e) => Response::error(id, -32000, format!("Erro ao obter politica: {}", e)),
    }
}

fn handle_provision_rustdesk(id: String, params: serde_json::Value) -> Response {
    let config_string = params.get("config").and_then(|c| c.as_str()).map(String::from);
    let password = params.get("password").and_then(|p| p.as_str()).map(String::from);
    let machine_id = params.get("machineId").and_then(|m| m.as_str()).map(String::from);

    match rustdesk::ensure_rustdesk(config_string.as_deref(), password.as_deref(), machine_id.as_deref()) {
        Ok(result) => Response::success(id, serde_json::to_value(result).unwrap()),
        Err(e) => Response::error(id, -32000, format!("Erro ao provisionar RustDesk: {}", e)),
    }
}

fn handle_get_rustdesk_status(id: String) -> Response {
    match rustdesk::get_status() {
        Ok(status) => Response::success(id, serde_json::to_value(status).unwrap()),
        Err(e) => Response::error(id, -32000, format!("Erro ao obter status: {}", e)),
    }
}
