use std::sync::mpsc;

use axum::{
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use clap::Parser;
use coster_rs::graph::{Graph, RunnableGraph};
use log::info;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // TODO: Proper logging to file
    env_logger::init();
    // TODO: Tracing?
    // tracing_subscriber::fmt::init();

    let agent = Agent::parse();
    agent.run().await?;
    Ok(())
}

#[derive(Parser)]
#[command(name = "ingey-agent")]
#[command(name = "Pivato M. <contact@michaelpivato.devv")]
#[command(version = "0.0.1")]
#[command(about="Simple, fast, efficient data manipulation tool", long_about=None)]
pub struct Agent {
    #[arg(short = 'p', long, value_name = "PORT", default_value_t = 8080)]
    web_server_port: u16,

    #[arg(short = 'b', long, default_value = "localhost")]
    web_server_bind_host: String,
}

impl Agent {
    pub async fn run(&self) -> anyhow::Result<()> {
        // TODO: kind of a want an actual queue so that we can see where in the queue we're up to in another method
        // Maybe what we do instead is add to the queue in the request, then when we finish here we remove from the queue.
        // Can do this with more message passing as well
        let (tx, rx) = mpsc::channel::<RunnableGraph>();
        tokio::spawn(async move {
            // Thread to receive requests to run a task
            for graph in rx {
                let result = graph
                    .run_default_tasks(1, |id, status| {
                        info!("Node with id {} finished with status {:?}", id, status)
                    })
                    .await;
                // If the result is bad, mark this as failed somewhere
            }
        });
        let app = Router::new()
            .route(
                "/run",
                post({
                    let sender = tx.clone();
                    move |body| run_graph(body, sender)
                }),
            )
            .route("/", get(async || "Hello World"));

        let listener = tokio::net::TcpListener::bind(format!(
            "{}:{}",
            self.web_server_bind_host, self.web_server_port
        ))
        .await?;
        info!(
            "Successfully started the web server on address {}:{}",
            self.web_server_bind_host, self.web_server_port
        );
        axum::serve(listener, app).await?;
        info!("Web server has shut down");
        Ok(())
    }
}

async fn run_graph(Json(payload): Json<Graph>, sender: mpsc::Sender<RunnableGraph>) -> StatusCode {
    if let Ok(response) = sender.send(RunnableGraph::from_graph(payload)) {
        StatusCode::ACCEPTED
    } else {
        StatusCode::INTERNAL_SERVER_ERROR
    }
}
