use std::pin::Pin;

use fundamentum_edge_proto::com::fundamentum::edge::v1::{
    configuration_server::{Configuration, ConfigurationServer},
    ConfigData, UpdateData,
};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use tokio::sync::mpsc;
use tokio_stream::{wrappers::ReceiverStream, Stream};
use tonic::transport::Server;
use tonic::{Request, Response, Status};

#[derive(Default)]
pub struct ConfigurationService {}

impl ConfigurationService {
    pub fn new() -> Self {
        Self {}
    }
}

#[tonic::async_trait]
impl Configuration for ConfigurationService {
    type UpdateStreamStream =
        Pin<Box<dyn Stream<Item = Result<UpdateData, Status>> + Send + 'static>>;

    async fn get(&self, _: Request<()>) -> Result<Response<ConfigData>, Status> {
        Ok(Response::new(ConfigData {
            payload: "Hello, world!".as_bytes().to_vec(),
        }))
    }

    async fn update_stream(
        &self,
        _: Request<()>,
    ) -> Result<Response<Self::UpdateStreamStream>, Status> {
        let (tx, rx) = mpsc::channel(5);

        tokio::spawn(async move {
            for i in 0..10 {
                tx.send(Ok(UpdateData { payload: vec![i] })).await.unwrap();
            }
        });

        Ok(Response::new(Box::pin(ReceiverStream::new(rx))))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);

    let configuration_svc = ConfigurationService::new();
    let router = Server::builder().add_service(ConfigurationServer::new(configuration_svc));
    router.serve(socket_addr).await?;

    Ok(())
}