//! Named Pipe IPC server for communication with GUI

use anyhow::Result;
use std::io::{BufRead, BufReader, Write};
use std::sync::{Arc, Mutex};
use tokio::task;

use crate::ServiceState;

const PIPE_NAME: &str = r"\\.\pipe\ostp-daemon";

pub async fn start_ipc_server(state: Arc<Mutex<ServiceState>>) -> Result<()> {
    task::spawn_blocking(move || {
        ipc_server_loop(state)
    });
    
    Ok(())
}

#[cfg(windows)]
fn ipc_server_loop(state: Arc<Mutex<ServiceState>>) -> Result<()> {
    use std::ptr;
    use winapi::um::namedpipeapi::*;
    use winapi::um::winbase::*;
    use winapi::um::fileapi::*;
    use winapi::um::handleapi::*;
    use winapi::shared::minwindef::*;
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;

    loop {
        // Create Named Pipe instance
        let pipe_name_wide: Vec<u16> = OsStr::new(PIPE_NAME)
            .encode_wide()
            .chain(std::iter::once(0))
            .collect();

        let pipe_handle = unsafe {
            CreateNamedPipeW(
                pipe_name_wide.as_ptr(),
                PIPE_ACCESS_DUPLEX,
                PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                PIPE_UNLIMITED_INSTANCES,
                512,
                512,
                0,
                ptr::null_mut(),
            )
        };

        if pipe_handle == INVALID_HANDLE_VALUE {
            tracing::error!("Failed to create named pipe");
            std::thread::sleep(std::time::Duration::from_secs(1));
            continue;
        }

        // Wait for client connection
        let connected = unsafe { ConnectNamedPipe(pipe_handle, ptr::null_mut()) };

        if connected == 0 {
            unsafe { CloseHandle(pipe_handle) };
            continue;
        }

        // Handle client communication
        handle_client(pipe_handle, state.clone());

        unsafe { CloseHandle(pipe_handle) };
    }
}

#[cfg(windows)]
fn handle_client(pipe_handle: winapi::um::winnt::HANDLE, state: Arc<Mutex<ServiceState>>) {
    use std::fs::File;
    use std::os::windows::io::FromRawHandle;

    let mut pipe_file = unsafe { File::from_raw_handle(pipe_handle as *mut _) };
    let mut reader = BufReader::new(pipe_file.try_clone().unwrap());

    let mut line = String::new();
    if reader.read_line(&mut line).is_ok() {
        let command = line.trim();
        tracing::info!("Received IPC command: {}", command);

        let response = match command {
            "CONNECT" => {
                state.lock().unwrap().is_connected = true;
                "OK:CONNECTED".to_string()
            }
            "DISCONNECT" => {
                state.lock().unwrap().is_connected = false;
                "OK:DISCONNECTED".to_string()
            }
            "STATUS" => {
                let state_lock = state.lock().unwrap();
                format!("{{\"connected\":{},\"upload_speed\":0,\"download_speed\":0,\"ping\":0}}", 
                    state_lock.is_connected)
            }
            _ => "ERROR:UNKNOWN_COMMAND".to_string(),
        };

        let _ = pipe_file.write_all(response.as_bytes());
        let _ = pipe_file.write_all(b"\n");
    }
}

#[cfg(not(windows))]
fn ipc_server_loop(_state: Arc<Mutex<ServiceState>>) -> Result<()> {
    anyhow::bail!("Named pipes are only supported on Windows");
}
