use std::io::ErrorKind;
use tokio::net::{UnixListener, UnixStream};
use tauri::{
    AppHandle,
    Manager,
    async_runtime as rt,
};

use crate::errors::*;


pub type Stream = UnixStream;


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

impl Server {
    pub fn start(app_handle: AppHandle) -> std::io::Result<()> {
        match std::fs::remove_file("/tmp/creddy.sock") {
            Ok(_) => (),
            Err(e) if e.kind() == ErrorKind::NotFound => (),
            Err(e) => return Err(e),
        }

        let listener = UnixListener::bind("/tmp/creddy.sock")?;
        let srv = Server { listener, app_handle };
        rt::spawn(srv.serve());
        Ok(())
    }

    async fn serve(self) {
        loop {
            self.try_serve()
                .await
                .error_print_prefix("Error accepting request: ");
        }
    }

    async fn try_serve(&self) -> Result<(), HandlerError> {
        let (stream, _addr) = self.listener.accept().await?;
        let new_handle = self.app_handle.app_handle();
        let client_pid = get_client_pid(&stream)?;
        rt::spawn(async move {
            super::handle(stream, new_handle, client_pid)
                .await
                .error_print_prefix("Error responding to request: ");
        });
        Ok(())
    }
}


fn get_client_pid(stream: &UnixStream) -> std::io::Result<u32> {
    let cred = stream.peer_cred()?;
    Ok(cred.pid().unwrap() as u32)
}
