use crate::drivers::DriverManager;
use crate::error::Result;
use crate::ipc::IpcBus;
use crate::kernel::Kernel;
use crate::memory::CommandHistory;
use crate::npc_compiler;
use crate::process::Capabilities;
use crate::scheduler::Scheduler;
use crate::vfs::Vfs;
use std::collections::HashMap;
use std::sync::atomic::AtomicU32;

pub fn boot_kernel(team_dir: &str, db_path: &str) -> Result<Kernel> {
    tracing::info!("kernel: booting from {}", team_dir);

    let team = npc_compiler::load_team_from_directory(team_dir)?;
    let jinxes = team.jinxes.clone();

    tracing::info!(
        "kernel: loaded {} NPCs, {} jinxes",
        team.npcs.len(),
        jinxes.len()
    );

    let drivers = DriverManager::from_env();

    let db_path_expanded = shellexpand::tilde(db_path).to_string();
    let history = CommandHistory::open(&db_path_expanded)?;

    let vfs = Vfs::new(team_dir);

    let mut kernel = Kernel {
        processes: HashMap::new(),
        next_pid: AtomicU32::new(0),
        team,
        jinxes,
        drivers,
        vfs,
        ipc: IpcBus::new(),
        scheduler: Scheduler::new(),
        history,
        python_daemon: None,
        boot_time: chrono::Utc::now(),
    };

    let team_box = Box::new(kernel.team.clone());

    let mut init_npc = kernel.team.lead_npc().cloned().unwrap_or_else(|| {
        crate::npc_compiler::NPC::new("init", "You are the init process. Coordinate the system.")
    });
    init_npc.team = Some(team_box.clone());

    kernel.spawn_init(init_npc);
    tracing::info!("kernel: init process spawned (pid 0)");

    let other_npcs: Vec<_> = kernel
        .team
        .npcs
        .values()
        .filter(|n| Some(&n.name) != kernel.team.forenpc.as_ref())
        .cloned()
        .collect();

    for mut npc in other_npcs {
        npc.team = Some(team_box.clone());
        let name = npc.name.clone();
        let pid = kernel.spawn(npc, 0, Capabilities::root());
        tracing::info!("kernel: spawned daemon {} (pid {})", name, pid);
    }

    tracing::info!("kernel: boot complete");
    Ok(kernel)
}
