use tokio::{
    net::windows::named_pipe::{
        NamedPipeServer,
        ServerOptions,
    },
    sync::oneshot,
};

use windows::Win32:: {
    Foundation::HANDLE,
    System::Pipes::GetNamedPipeClientProcessId,
};

use std::os::windows::io::AsRawHandle;

use tauri::async_runtime as rt;

use crate::errors::*;


// used by parent module
pub type Stream = NamedPipeServer;


pub struct Server {
    listener: NamedPipeServer,
    app_handle: AppHandle,
}

impl Server {
    pub fn start(app_handle: AppHandle) -> std::io::Result<()> {
        let listener = ServerOptions::new()
            .first_pipe_instance(true)
            .create(r"\\.\pipe\creddy-requests")?;

        let srv = Server {listener, app_handle};
        rt::spawn(srv.serve());
        Ok(())
    }

    async fn serve(mut self) {
        loop {
            if let Err(e) = self.try_serve().await {
                eprintln!("Error accepting connection: {e}");
            }
        }
    }

    async fn try_serve(&mut self) -> Result<(), HandlerError> {
        // connect() just waits for a client to connect, it doesn't return anything
        self.listener.connect().await?;

        // create a new pipe instance to listen for the next client, and swap it in
        let new_listener = ServerOptions::new().create(r"\\.\pipe\creddy-requests")?;
        let mut stream = std::mem::replace(&mut self.listener, new_listener);
        let new_handle = self.app_handle.app_handle();
        let client_pid = get_client_pid(&stream)?;
        rt::spawn(async move {
            super::handle(stream, app_handle)
                .await
                .error_print_prefix("Error responding to request: ");
        });

        Ok(())
    }
}


fn get_client_pid(pipe: &NamedPipeServer) -> Result<u32, ClientInfoError> {
    let raw_handle = pipe.as_raw_handle();
    let mut pid = 0u32;
    let handle = HANDLE(raw_handle as _);
    unsafe { GetNamedPipeClientProcessId(handle, &mut pid as *mut u32)? };
    pid
}
