use std::time::Duration;

use anyhow::Result;
use clap::Parser;
use tracing::info;
use tracing_subscriber::EnvFilter;

mod agent;
mod process;
mod watcher;

/// UltraCloud NodeAgent
///
/// - Chainfire 上の `ultracloud/clusters/{cluster_id}/nodes/{node_id}` と
///   `.../instances/*` を watch しつつ、周期 heartbeat/safety reconcile も行う。
/// - `--apply` が指定された場合のみプロセス起動/停止を行う（デフォルトは dry-run）。
#[derive(Parser, Debug)]
#[command(author, version, about)]
struct Cli {
    /// Chainfire API エンドポイント
    #[arg(long, default_value = "http://127.0.0.1:7000")]
    chainfire_endpoint: String,

    /// UltraCloud cluster namespace (default: ultracloud)
    #[arg(long, default_value = "ultracloud")]
    cluster_namespace: String,

    /// UltraCloud Cluster ID
    #[arg(long)]
    cluster_id: String,

    /// このエージェントが管理する Node ID
    #[arg(long)]
    node_id: String,

    /// heartbeat / safety reconcile 間隔（秒）
    #[arg(long, default_value_t = 15)]
    interval_secs: u64,

    /// service status 集約時に利用する heartbeat 許容秒数
    #[arg(long, default_value_t = 300)]
    heartbeat_timeout_secs: u64,

    /// PIDファイル出力ディレクトリ
    #[arg(long, default_value = "/var/run/ultracloud")]
    pid_dir: String,

    /// Desired State を実際に適用する（プロセス起動/停止、ヘルスチェック更新）
    #[arg(long, default_value_t = false)]
    apply: bool,

    /// ローカルの instances.json を Chainfire に upsert する (unsafe; dev only)
    #[arg(long, default_value_t = false)]
    allow_local_instance_upsert: bool,

    /// 単発で 1 回だけ reconcile して終了する
    #[arg(long, default_value_t = false)]
    once: bool,
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(EnvFilter::from_default_env().add_directive("info".parse()?))
        .init();

    let cli = Cli::parse();

    info!(
        cluster_id = %cli.cluster_id,
        node_id = %cli.node_id,
        "starting NodeAgent"
    );

    let mut agent = agent::Agent::new(
        cli.chainfire_endpoint,
        cli.cluster_namespace,
        cli.cluster_id,
        cli.node_id,
        Duration::from_secs(cli.interval_secs),
        cli.heartbeat_timeout_secs,
        cli.apply,
        cli.allow_local_instance_upsert,
        std::path::PathBuf::from(cli.pid_dir),
    );

    if cli.once {
        agent.tick().await?;
    } else {
        agent.run_loop().await?;
    }

    Ok(())
}
