use clap::{Parser, Subcommand};
use shcortex::Shcortex;
use std::time::Instant;

#[derive(Parser)]
#[command(name = "shcortex")]
#[command(version = "0.2.0")]
#[command(about = "SHCortex – Artificial Hippocampus for LLM context compression")]
struct Cli {
    #[command(subcommand)]
    command: Commands,
    #[arg(short, long, global = true)]
    db: Option<String>,
}

#[derive(Subcommand)]
enum Commands {
    Compress { text: String },
    Sleep { ticks: usize },
    Prompt { question: String },
    Retrieve { question: String },
    Status,
    Selftest,
}

fn main() {
    let cli = Cli::parse();
    let mut sh = Shcortex::new(cli.db);

    match cli.command {
        Commands::Compress { text } => {
            let start = Instant::now();
            sh.compress(&text);
            let latency = start.elapsed();
            println!("compressed: {} memcons, {} atinks | latency: {:?}",
                sh.memcons().len(), sh.atinks().len(), latency);
        }
        Commands::Sleep { ticks } => {
            let start = Instant::now();
            for _ in 0..ticks { sh.malla_mut().step(); }
            let items = sh.malla_mut().consolidate_with_activity(0.7);
            let memcons: Vec<String> = items.iter().map(|(id, _)| format!("{id:08x}")).collect();
            let complexities: Vec<f32> = items.iter().map(|(_, c)| *c).collect();
            sh.malla_mut().create_atinks(5);
            let atinks: Vec<(String, String)> = sh.malla_mut().atinks.iter()
                .map(|a| (format!("{:08x}", a.source), format!("{:08x}", a.target)))
                .collect();
            sh.set_memcons(memcons);
            sh.set_complexities(complexities);
            sh.set_atinks(atinks);
            let latency = start.elapsed();
            println!("slept: {} ticks | latency: {:?}", ticks, latency);
        }
        Commands::Prompt { question } => {
            let (system, user) = sh.build_prompt(&question);
            if let Some(s) = system {
                println!("--- system ---\n{s}\n");
            }
            println!("--- user ---\n{user}");
        }
        Commands::Retrieve { question } => {
            let start = Instant::now();
            let context = sh.retrieve(&question);
            let latency = start.elapsed();
            println!("context ({} results, {:?}):", context.len(), latency);
            for (i, c) in context.iter().enumerate() {
                println!("  {}. {}", i + 1, c);
            }
        }
        Commands::Status => {
            let s = sh.status();
            println!("text_tokens: {}", s.text_tokens);
            println!("memcons (RAM): {}", s.ram_memcons);
            println!("atinks (RAM): {}", s.ram_atinks);
            println!("\n--- Persistent Memory (SQLite) ---");
            println!("memcons: {}", s.db_memcons);
            println!("atinks: {}", s.db_atinks);
            println!("hectores: {}", s.db_hectores);
            println!("db_size_kb: {}", s.db_size_kb);
        }
        Commands::Selftest => {
            assert_eq!(lnhash::detect_lang("El perro come pan"), 0);
            assert_eq!(lnhash::detect_lang("The dog eats bread"), 1);
            let togens = lnhash::text_to_togens("Hello world 123 and more");
            assert!(togens.len() > 0);

            let mut m = shcortex::malla_multi::MallaMulti::new(3, 64, 0.15, 0.015, 0.07);
            m.inject(42, 0);
            m.step();
            let _ = m.consolidate(0.7);

            let b = bimoh::BimoH::new(0.1, 0.01, 0.05);
            assert!(b.should_consolidate(0.2));
            assert!(!b.should_consolidate(0.05));

            let engine = shcortex::prompt_engine::PromptEngine::new(500);
            let user = engine.build_user_prompt("short text more words", &["short".into(), "text".into()], &[], "?");
            assert!(user.contains("short text"));
            assert!(!user.contains("000000"));

            assert!(shcortex::prompt_engine::PromptEngine::build_system_context(&["ctx1".into()]).is_some());
            println!("selftest ok");
        }
    }
}
