use std::path::PathBuf;

use anyhow::Result;
use clap::{Parser, Subcommand, ValueEnum};
use multi_brain::config::Paths;
use multi_brain::daemon::{Daemon, request_or_start};
use multi_brain::protocol::{HarnessConfig, HarnessKind, Request, Response};
use tracing_subscriber::EnvFilter;

#[derive(Debug, Parser)]
#[command(version, about)]
struct Cli {
    #[arg(long, env = "MULTI_BRAIN_HOME")]
    home: Option<PathBuf>,
    #[command(subcommand)]
    command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
    Daemon,
    Start,
    Mcp,
    Register(HarnessArgs),
    Spawn {
        name: String,
    },
    Ensure(HarnessArgs),
    List,
    Ask {
        from: String,
        to: String,
        body: String,
    },
    Messages {
        name: String,
    },
    Events {
        name: String,
        #[arg(long, default_value_t = 100)]
        max: usize,
    },
    InjectMcp {
        #[arg(long, default_value = ".")]
        cwd: PathBuf,
        #[arg(long, default_value = "multi-brain")]
        server_name: String,
    },
    Stop {
        name: String,
    },
    Ping,
}

#[derive(Debug, Parser)]
struct HarnessArgs {
    #[arg(long)]
    name: String,
    #[arg(long, value_enum)]
    kind: KindArg,
    #[arg(long)]
    command: String,
    #[arg(long, value_delimiter = ' ')]
    args: Vec<String>,
    #[arg(long, default_value = ".")]
    cwd: PathBuf,
    #[arg(long, default_value = "You are a focused engineering advisor.")]
    personality: String,
    #[arg(long = "env")]
    env: Vec<String>,
}

#[derive(Debug, Clone, ValueEnum)]
enum KindArg {
    Claude,
    Codex,
    OpenClaw,
    Generic,
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(EnvFilter::from_default_env())
        .with_writer(std::io::stderr)
        .init();

    let cli = Cli::parse();
    let paths = match cli.home {
        Some(home) => Paths::from_root(home),
        None => Paths::discover()?,
    };
    paths.ensure().await?;

    match cli.command {
        Command::Daemon => Daemon::new(paths).await?.serve().await?,
        Command::Start => {
            multi_brain::daemon::start_daemon_headless(&paths).await?;
            print_response(Response::Pong);
        }
        Command::Mcp => multi_brain::mcp::serve_stdio(paths).await?,
        Command::Register(args) => print_response(
            request_or_start(
                &paths,
                Request::Register {
                    config: args.into_config()?,
                },
            )
            .await?,
        ),
        Command::Ensure(args) => print_response(
            request_or_start(
                &paths,
                Request::Ensure {
                    config: args.into_config()?,
                },
            )
            .await?,
        ),
        Command::Spawn { name } => {
            print_response(request_or_start(&paths, Request::Spawn { name }).await?)
        }
        Command::List => print_response(request_or_start(&paths, Request::List).await?),
        Command::Ask { from, to, body } => {
            print_response(request_or_start(&paths, Request::Ask { from, to, body }).await?)
        }
        Command::Messages { name } => {
            print_response(request_or_start(&paths, Request::Messages { name }).await?)
        }
        Command::Events { name, max } => {
            print_response(request_or_start(&paths, Request::Events { name, max }).await?)
        }
        Command::InjectMcp { cwd, server_name } => print_response(
            request_or_start(
                &paths,
                Request::InjectMcp {
                    cwd: cwd.canonicalize()?.display().to_string(),
                    server_name,
                },
            )
            .await?,
        ),
        Command::Stop { name } => {
            print_response(request_or_start(&paths, Request::Stop { name }).await?)
        }
        Command::Ping => print_response(request_or_start(&paths, Request::Ping).await?),
    }
    Ok(())
}

impl HarnessArgs {
    fn into_config(self) -> Result<HarnessConfig> {
        let env = self
            .env
            .into_iter()
            .filter_map(|pair| {
                let (k, v) = pair.split_once('=')?;
                Some((k.to_string(), v.to_string()))
            })
            .collect();
        Ok(HarnessConfig {
            name: self.name,
            kind: match self.kind {
                KindArg::Claude => HarnessKind::Claude,
                KindArg::Codex => HarnessKind::Codex,
                KindArg::OpenClaw => HarnessKind::OpenClaw,
                KindArg::Generic => HarnessKind::Generic,
            },
            command: self.command,
            args: self.args,
            cwd: self.cwd.canonicalize()?.display().to_string(),
            personality: self.personality,
            env,
        })
    }
}

fn print_response(response: Response) {
    println!("{}", serde_json::to_string_pretty(&response).unwrap());
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn harness_args_parses_env_and_open_claw_kind() {
        let cwd = tempfile::tempdir().unwrap();
        let config = HarnessArgs {
            name: "open".into(),
            kind: KindArg::OpenClaw,
            command: "open-claw".into(),
            args: vec!["--headless".into()],
            cwd: cwd.path().to_path_buf(),
            personality: "p".into(),
            env: vec!["A=B".into(), "ignored".into()],
        }
        .into_config()
        .unwrap();
        assert_eq!(config.kind, HarnessKind::OpenClaw);
        assert_eq!(config.env, vec![("A".into(), "B".into())]);
    }
}
