//! Demo CLI for Aurora Semantic - Tests indexing and searching on the codebase
//!
//! This demo automatically indexes the aurora-semantic source code using the
//! jina-model in the project root, then runs interactive searches.

use std::io::{self, Write};
use std::path::PathBuf;
use std::time::Instant;

use colored::*;

use aurora_semantic::{
    Embedder, Engine, EngineConfig, IndexPhase, ModelConfig, SearchMode, SearchQuery, WorkspaceConfig,
};

const DEMO_INDEX_DIR: &str = "E:\\aurora-semantic-demo-index";
const MODEL_DIR: &str = "E:\\aurora-semantic-model\\jina-code-embeddings-1.5b-ONNX";

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

    println!("{}", "╔════════════════════════════════════════════════════════╗".cyan());
    println!("{}", "║      Aurora Semantic - Demo CLI                        ║".cyan());
    println!("{}", "║      Local Semantic Code Search Engine                 ║".cyan());
    println!("{}", "╚════════════════════════════════════════════════════════╝".cyan());
    println!();

    // Check for model
    let model_path = PathBuf::from(MODEL_DIR);
    if !model_path.exists() {
        eprintln!("{} Model directory '{}' not found!", "Error:".red().bold(), MODEL_DIR);
        eprintln!();
        eprintln!("Please download the jina-embeddings model and place it in ./{}:", MODEL_DIR);
        eprintln!("  - model.onnx (or model-w-mean-pooling.onnx)");
        eprintln!("  - tokenizer.json");
        std::process::exit(1);
    }

    // Check for tokenizer
    let tokenizer_path = model_path.join("tokenizer.json");
    if !tokenizer_path.exists() {
        eprintln!("{} tokenizer.json not found in {}", "Error:".red().bold(), MODEL_DIR);
        std::process::exit(1);
    }

    println!("{} {}", "Model directory:".cyan(), model_path.display());

    // Load model
    println!("{}", "Loading ONNX embedding model...".yellow());
    let start = Instant::now();

    let model_config = ModelConfig::from_directory(&model_path).with_max_length(512);
    let embedder = match model_config.load() {
        Ok(e) => e,
        Err(err) => {
            eprintln!("{} Failed to load model: {}", "Error:".red().bold(), err);
            std::process::exit(1);
        }
    };

    // Show execution provider info
    let provider = embedder.execution_provider();
    let provider_display = if provider.is_gpu {
        format!("{} {}", "GPU".green().bold(), provider.name.cyan())
    } else {
        format!("{} {}", "CPU".yellow(), provider.name)
    };

    println!(
        "{} Model loaded in {:.2}s (dimension: {}, device: {})",
        "OK".green().bold(),
        start.elapsed().as_secs_f64(),
        embedder.dimension(),
        provider_display
    );
    
    if provider.is_gpu {
        println!("  {} GPU acceleration enabled", ">>".green());
        if let Some(details) = &provider.details {
            println!("  {} {}", ">>".green(), details);
        }
    } else {
        println!("  {} Running on CPU (build with --features cuda/directml/coreml for GPU)", ">>".yellow());
    }
    println!();

    // Create engine
    let index_dir = PathBuf::from(DEMO_INDEX_DIR);
    let config = EngineConfig::new(index_dir.clone());
    let engine = Engine::with_embedder(config, embedder)?;

    // Get workspace path (Instagram app directory)
    let workspace_path = PathBuf::from("E:\\INSTAGRAM-KICKBOT-MIXED\\QUANTUM-GRAM-APP-FINAL");

    // Check if already indexed
    let workspaces = engine.list_workspaces()?;
    let workspace_id = if workspaces.is_empty() {
        println!("{} {}", "Indexing workspace:".cyan(), workspace_path.display());
        println!("{}", "This may take a moment...".yellow());
        println!();

        let start = Instant::now();

        // Progress callback
        let progress_callback = Box::new(|progress: aurora_semantic::IndexProgress| {
            let indicator = match progress.phase {
                IndexPhase::Scanning => "📂",
                IndexPhase::Parsing => "📝",
                IndexPhase::Embedding => "🧠",
                IndexPhase::Indexing => "🔍",
                IndexPhase::Persisting => "💾",
                IndexPhase::Complete => "✅",
            };

            print!(
                "\r{} {:?}: {}/{} ({:.0}%)   ",
                indicator,
                progress.phase,
                progress.processed,
                progress.total,
                progress.percentage()
            );
            io::stdout().flush().ok();
        });

        let ws_config = WorkspaceConfig::new(workspace_path.clone());
        let ws_id = engine.index_workspace(ws_config, Some(progress_callback)).await?;

        println!();
        let stats = engine.get_workspace_stats(&ws_id)?;
        println!();
        println!("{}", "=== Indexing Complete ===".green().bold());
        println!("  Documents: {}", stats.document_count);
        println!("  Chunks:    {}", stats.chunk_count);
        println!("  Time:      {:.2}s", start.elapsed().as_secs_f64());
        println!();

        ws_id
    } else {
        let ws_id = workspaces[0].clone();
        engine.load_workspace(&ws_id)?;

        let stats = engine.get_workspace_stats(&ws_id)?;
        println!(
            "{} Using existing index ({} docs, {} chunks)",
            "✓".green().bold(),
            stats.document_count,
            stats.chunk_count
        );
        println!("  To reindex, delete the {} directory and run again.", DEMO_INDEX_DIR);
        println!();

        ws_id
    };

    // Interactive search loop
    println!("{}", "=== Interactive Search ===".cyan().bold());
    println!("Commands:");
    println!("  {}       - Search all modes", "<query>".yellow());
    println!("  {}  - Lexical search only", "l:<query>".yellow());
    println!("  {}  - Semantic search only", "s:<query>".yellow());
    println!("  {}  - Hybrid search", "h:<query>".yellow());
    println!("  {}       - Exit", "quit".yellow());
    println!();

    loop {
        print!("{} ", "search>".green().bold());
        io::stdout().flush()?;

        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        let input = input.trim();

        if input.is_empty() {
            continue;
        }

        if input == "quit" || input == "exit" || input == "q" {
            println!("{}", "Goodbye!".cyan());
            break;
        }

        // Parse mode prefix
        let (mode, query_text) = if input.starts_with("l:") {
            (SearchMode::Lexical, &input[2..])
        } else if input.starts_with("s:") {
            (SearchMode::Semantic, &input[2..])
        } else if input.starts_with("h:") {
            (SearchMode::Hybrid, &input[2..])
        } else {
            (SearchMode::Hybrid, input)
        };

        let query_text = query_text.trim();
        if query_text.is_empty() {
            println!("{}", "Please enter a search query.".yellow());
            continue;
        }

        // Perform search
        let query = SearchQuery::new(query_text)
            .mode(mode)
            .limit(5)
            .min_score(0.1);

        let start = Instant::now();
        let results = engine.search(&workspace_id, query)?;
        let elapsed = start.elapsed();

        if results.is_empty() {
            println!("{}", "No results found.".yellow());
            println!();
            continue;
        }

        println!(
            "\n{} {} results ({:?}) in {:.2}ms\n",
            "Found".green().bold(),
            results.len(),
            mode,
            elapsed.as_secs_f64() * 1000.0
        );

        for (i, result) in results.iter().enumerate() {
            let score_color = if result.score > 0.7 {
                format!("{:.3}", result.score).green()
            } else if result.score > 0.4 {
                format!("{:.3}", result.score).yellow()
            } else {
                format!("{:.3}", result.score).red()
            };

            println!(
                "{}. {} [{}] {:?}",
                (i + 1).to_string().bold(),
                result.document.relative_path.display().to_string().cyan(),
                score_color,
                result.match_type
            );

            if let Some(ref symbol) = result.chunk.symbol_name {
                println!(
                    "   {} {:?} '{}'",
                    "Symbol:".dimmed(),
                    result.chunk.chunk_type,
                    symbol.yellow()
                );
            }

            println!(
                "   {} lines {}-{}",
                "Lines:".dimmed(),
                result.chunk.start_line,
                result.chunk.end_line
            );

            // Show preview
            let preview: String = result
                .chunk
                .content
                .chars()
                .take(120)
                .collect::<String>()
                .replace('\n', " ")
                .replace('\r', "");
            println!("   {}", preview.dimmed());
            println!();
        }
    }

    Ok(())
}
