use std::io::{self, Write};

use clap::{Args, Parser, Subcommand};

use crate::agent::Agent;
use crate::error::{Error, Result};
use crate::loader::script_source_from_path;
use crate::request::RunRequest;

#[derive(Debug, Parser)]
#[command(name = "sandlet-agent")]
struct Cli {
    #[command(subcommand)]
    command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
    Run(RunArgs),
    Serve(ServeArgs),
}

#[derive(Debug, Args)]
#[command(trailing_var_arg = true)]
struct RunArgs {
    #[arg(long)]
    script: String,

    #[arg(value_name = "ARG")]
    args: Vec<String>,
}

#[derive(Debug, Args)]
struct ServeArgs {
    #[arg(long, default_value = "127.0.0.1:7000")]
    listen: String,
}

pub fn run(agent: &Agent) -> Result<()> {
    let cli = Cli::try_parse().map_err(|err| Error::invalid_request(err.to_string()))?;

    match cli.command {
        Command::Run(args) => {
            let mut parameters = std::collections::HashMap::new();
            for module in &agent.metadata().modules {
                for param in &module.parameters {
                    if let Ok(val) = std::env::var(&param.name) {
                        parameters.insert(param.name.clone(), val);
                    }
                }
            }

            let request = RunRequest {
                script: script_source_from_path(args.script),
                entrypoint: String::from("main"),
                args: args.args,
                parameters,
                limits: Default::default(),
            };
            let result = agent.run_once(request)?;
            let stdout = io::stdout();
            let mut handle = stdout.lock();
            writeln!(handle, "{}", result.output)?;
            Ok(())
        }
        Command::Serve(args) => agent.serve(&args.listen),
    }
}
