mod protocol;
mod server;
mod tools;

use std::time::{Duration, Instant};

fn main() {
    tracing_subscriber::fmt()
        .with_writer(std::io::stderr)
        .with_env_filter(
            tracing_subscriber::EnvFilter::from_default_env()
                .add_directive("second_brain=info".parse().unwrap()),
        )
        .init();

    tracing::info!("second-brain MCP server starting (HTTP client mode)");

    let api_base =
        std::env::var("SECOND_BRAIN_API").unwrap_or_else(|_| "http://127.0.0.1:7200".to_string());

    let client = reqwest::blocking::Client::builder()
        .timeout(Duration::from_secs(120))
        .build()
        .expect("build http client");

    ensure_daemon_running(&client, &api_base);

    if let Err(e) = server::run_stdio(client, api_base) {
        tracing::error!("server error: {e}");
        std::process::exit(1);
    }
}

fn ensure_daemon_running(client: &reqwest::blocking::Client, api_base: &str) {
    let health_url = format!("{api_base}/health");

    if client
        .get(&health_url)
        .timeout(Duration::from_secs(2))
        .send()
        .is_ok_and(|r| r.status().is_success())
    {
        tracing::info!("daemon running at {api_base}");
        return;
    }

    let daemon_bin =
        std::env::var("SECOND_BRAIN_DAEMON_BIN").unwrap_or_else(|_| locate_daemon_binary());

    tracing::info!("starting daemon: {daemon_bin}");

    match std::process::Command::new(&daemon_bin)
        .stdin(std::process::Stdio::null())
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::inherit())
        .spawn()
    {
        Ok(_) => {}
        Err(e) => {
            tracing::error!("could not spawn daemon ({daemon_bin}): {e}");
            tracing::error!("install and start second-brain-api, or set SECOND_BRAIN_DAEMON_BIN");
            std::process::exit(1);
        }
    }

    let mut delay = Duration::from_millis(200);
    let deadline = Instant::now() + Duration::from_secs(30);

    while Instant::now() < deadline {
        std::thread::sleep(delay);
        if client
            .get(&health_url)
            .timeout(Duration::from_secs(2))
            .send()
            .is_ok_and(|r| r.status().is_success())
        {
            tracing::info!("daemon is ready");
            return;
        }
        delay = (delay * 2).min(Duration::from_secs(3));
    }

    tracing::error!("daemon did not become ready within 30s");
    std::process::exit(1);
}

fn locate_daemon_binary() -> String {
    if let Ok(exe) = std::env::current_exe() {
        let candidate = exe.with_file_name("second-brain-api");
        if candidate.exists() {
            return candidate.to_string_lossy().into_owned();
        }
    }
    "second-brain-api".to_string()
}
