//! Shadow mode testing command handlers

use std::io::{self, Write};
use std::path::PathBuf;
use std::process::ExitCode;

use aphoria::{
    AphoriaConfig, FeedbackCollector, GraduationManager, MatchFeedback, ShadowExtractorRegistry,
    ShadowStatus,
};
use uuid::Uuid;

/// Handle shadow-status command
pub fn handle_shadow_status(config: &AphoriaConfig, verbose: bool) -> ExitCode {
    // Create registry
    let registry =
        match ShadowExtractorRegistry::new(&config.shadow, &config.learning.promotion.output_dir) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("Failed to open shadow registry: {e}");
                return ExitCode::from(3);
            }
        };

    // Get all tests
    let tests = match registry.list_all_tests() {
        Ok(t) => t,
        Err(e) => {
            eprintln!("Failed to list shadow tests: {e}");
            return ExitCode::from(3);
        }
    };

    if tests.is_empty() {
        println!("No shadow tests found.");
        println!();
        println!("Shadow tests are created when patterns are auto-promoted.");
        println!("To enable shadow mode, add this to your aphoria.toml:");
        println!();
        println!("  [shadow]");
        println!("  enabled = true");
        return ExitCode::SUCCESS;
    }

    println!("Shadow Mode Testing Status");
    println!("==========================");
    println!();
    println!("Configuration:");
    println!("  Min scans for graduation: {}", config.shadow.min_scans);
    println!("  Max FP rate for graduation: {:.1}%", config.shadow.max_fp_rate * 100.0);
    println!("  Rollback threshold: {:.1}%", config.shadow.rollback_threshold * 100.0);
    println!();

    // Group by status
    let active: Vec<_> = tests.iter().filter(|t| t.status == ShadowStatus::Active).collect();
    let graduated: Vec<_> = tests.iter().filter(|t| t.status == ShadowStatus::Graduated).collect();
    let rolled_back: Vec<_> =
        tests.iter().filter(|t| t.status == ShadowStatus::RolledBack).collect();

    // Active tests
    if !active.is_empty() {
        println!("Active Shadow Tests ({}):", active.len());
        println!(
            "{:<30} {:>8} {:>8} {:>8} {:>8} {:>6}",
            "Name", "Scans", "TP", "FP", "FP%", "Ready?"
        );
        println!("{}", "-".repeat(80));

        for test in &active {
            let fp_rate = test.metrics.fp_rate() * 100.0;
            let is_ready = test.meets_graduation_criteria(&config.shadow);

            println!(
                "{:<30} {:>8} {:>8} {:>8} {:>7.1}% {}",
                truncate(&test.extractor_name, 30),
                test.metrics.total_scans,
                test.metrics.true_positives,
                test.metrics.false_positives,
                fp_rate,
                if is_ready { "YES" } else { "no" }
            );

            if verbose {
                println!("    ID: {}", test.id);
                println!("    Pending review: {}", test.metrics.pending_review);
                println!("    Created: {}", test.created_at.format("%Y-%m-%d %H:%M"));
                println!();
            }
        }
        println!();
    }

    // Graduated tests
    if !graduated.is_empty() {
        println!("Graduated ({}):", graduated.len());
        for test in &graduated {
            println!(
                "  {} - graduated {}",
                test.extractor_name,
                test.graduated_at
                    .map_or("unknown".to_string(), |t| t.format("%Y-%m-%d").to_string())
            );
        }
        println!();
    }

    // Rolled back tests
    if !rolled_back.is_empty() {
        println!("Rolled Back ({}):", rolled_back.len());
        for test in &rolled_back {
            println!(
                "  {} - {}",
                test.extractor_name,
                test.rollback_reason.as_deref().unwrap_or("unknown reason")
            );
        }
        println!();
    }

    // Summary
    println!("Summary:");
    println!("  Active:      {}", active.len());
    println!("  Graduated:   {}", graduated.len());
    println!("  Rolled back: {}", rolled_back.len());

    ExitCode::SUCCESS
}

/// Get the production directory from config
fn get_production_dir(config: &AphoriaConfig) -> PathBuf {
    // Navigate up from output_dir (learned/) to sibling (production/)
    // e.g., ~/.aphoria/learned/ -> ~/.aphoria/production/
    config.learning.promotion.output_dir.parent().map(|p| p.join("production")).unwrap_or_else(
        || {
            // Fallback: use data_dir/production if output_dir has no parent
            tracing::warn!(
                "Cannot determine production directory from output_dir, using data_dir fallback"
            );
            config.episteme.data_dir.join("production")
        },
    )
}

/// Handle feedback command
pub fn handle_shadow_feedback(config: &AphoriaConfig, test_name: &str, limit: usize) -> ExitCode {
    // Create registry
    let registry =
        match ShadowExtractorRegistry::new(&config.shadow, &config.learning.promotion.output_dir) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("Failed to open shadow registry: {e}");
                return ExitCode::from(3);
            }
        };

    let production_dir = get_production_dir(config);
    let collector = FeedbackCollector::new(&registry, &config.shadow, production_dir);

    // Try to parse as UUID first, then fall back to name lookup
    let test = if let Ok(id) = Uuid::parse_str(test_name) {
        match collector.get_test_state(&id) {
            Ok(Some(t)) => t,
            Ok(None) => {
                eprintln!("Shadow test '{}' not found", test_name);
                return ExitCode::from(3);
            }
            Err(e) => {
                eprintln!("Failed to get shadow test: {e}");
                return ExitCode::from(3);
            }
        }
    } else {
        match collector.get_test_state_by_name(test_name) {
            Ok(Some(t)) => t,
            Ok(None) => {
                eprintln!("Shadow test '{}' not found", test_name);
                return ExitCode::from(3);
            }
            Err(e) => {
                eprintln!("Failed to get shadow test: {e}");
                return ExitCode::from(3);
            }
        }
    };

    // Get pending matches
    let pending = match collector.get_pending(&test.id) {
        Ok(p) => p,
        Err(e) => {
            eprintln!("Failed to get pending matches: {e}");
            return ExitCode::from(3);
        }
    };

    if pending.is_empty() {
        println!("No pending matches for '{}'.", test.extractor_name);
        println!();
        println!("Current metrics:");
        println!("  Total scans:      {}", test.metrics.total_scans);
        println!("  True positives:   {}", test.metrics.true_positives);
        println!("  False positives:  {}", test.metrics.false_positives);
        println!("  FP rate:          {:.1}%", test.metrics.fp_rate() * 100.0);
        return ExitCode::SUCCESS;
    }

    println!("Shadow Feedback Session: {}", test.extractor_name);
    println!("========================{}", "=".repeat(test.extractor_name.len()));
    println!();
    println!("For each match, enter:");
    println!("  t/tp - True positive (correct detection)");
    println!("  f/fp - False positive (incorrect detection)");
    println!("  s/skip - Skip this match");
    println!("  q/quit - End session");
    println!();

    let matches_to_review: Vec<_> = pending.into_iter().take(limit).collect();
    let mut tp_count = 0;
    let mut fp_count = 0;
    let mut skipped = 0;

    for (idx, m) in matches_to_review.iter().enumerate() {
        println!("Match {}/{}", idx + 1, matches_to_review.len());
        println!("File: {}:{}", m.file_path.display(), m.line_number);
        println!("Matched: {}", m.matched_text);
        println!("Context:");
        for (i, line) in m.context.lines().enumerate() {
            let marker = if i == m.context.lines().count() / 2 { ">>>" } else { "   " };
            println!("{} {}", marker, line);
        }
        println!();

        print!("Feedback [t/f/s/q]: ");
        let _ = io::stdout().flush();

        let mut input = String::new();
        if io::stdin().read_line(&mut input).is_err() {
            eprintln!("Failed to read input");
            break;
        }

        match input.trim().to_lowercase().as_str() {
            "t" | "tp" | "true" | "true_positive" => {
                match collector.record_feedback(&test.id, &m.id, MatchFeedback::TruePositive) {
                    Ok(_) => {
                        tp_count += 1;
                        println!("Marked as TRUE POSITIVE");
                    }
                    Err(e) => {
                        eprintln!("Failed to record feedback: {e}");
                    }
                }
            }
            "f" | "fp" | "false" | "false_positive" => {
                match collector.record_feedback(&test.id, &m.id, MatchFeedback::FalsePositive) {
                    Ok(result) => {
                        fp_count += 1;
                        println!("Marked as FALSE POSITIVE");
                        if let Some(rollback) = result.auto_rollback {
                            if rollback.rolled_back > 0 {
                                println!();
                                println!(
                                    "⚠️  AUTO-ROLLBACK TRIGGERED: {}",
                                    rollback.rolled_back_names.join(", ")
                                );
                                println!("Session ended due to auto-rollback.");
                                break;
                            }
                        }
                    }
                    Err(e) => {
                        eprintln!("Failed to record feedback: {e}");
                    }
                }
            }
            "s" | "skip" => {
                skipped += 1;
                println!("Skipped");
            }
            "q" | "quit" | "exit" => {
                println!("Session ended.");
                break;
            }
            _ => {
                println!("Unknown input. Use t/f/s/q.");
                skipped += 1;
            }
        }
        println!();
    }

    println!();
    println!("Session Summary:");
    println!("  True positives:  {}", tp_count);
    println!("  False positives: {}", fp_count);
    println!("  Skipped:         {}", skipped);

    // Get updated test state
    if let Ok(Some(updated)) = collector.get_test_state(&test.id) {
        println!();
        println!("Updated metrics for '{}':", updated.extractor_name);
        println!("  Total scans:      {}", updated.metrics.total_scans);
        println!("  True positives:   {}", updated.metrics.true_positives);
        println!("  False positives:  {}", updated.metrics.false_positives);
        println!("  FP rate:          {:.1}%", updated.metrics.fp_rate() * 100.0);
        println!(
            "  Ready for graduation: {}",
            if updated.meets_graduation_criteria(&config.shadow) { "YES" } else { "no" }
        );
    }

    ExitCode::SUCCESS
}

/// Handle graduate command
pub fn handle_shadow_graduate(config: &AphoriaConfig, test_name: &str, force: bool) -> ExitCode {
    // Create registry
    let registry =
        match ShadowExtractorRegistry::new(&config.shadow, &config.learning.promotion.output_dir) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("Failed to open shadow registry: {e}");
                return ExitCode::from(3);
            }
        };

    let production_dir = get_production_dir(config);
    let manager = GraduationManager::new(&registry, &config.shadow, &production_dir);

    // Check readiness first
    let is_ready = match manager.is_ready_by_name(test_name) {
        Ok(r) => r,
        Err(e) => {
            eprintln!("Failed to check graduation readiness: {e}");
            return ExitCode::from(3);
        }
    };

    if !is_ready && !force {
        eprintln!("Shadow test '{}' is not ready for graduation.", test_name);
        eprintln!();
        eprintln!("Requirements:");
        eprintln!("  - At least {} scans", config.shadow.min_scans);
        eprintln!("  - FP rate <= {:.1}%", config.shadow.max_fp_rate * 100.0);
        eprintln!("  - At least some feedback");
        eprintln!();
        eprintln!("Use --force to override (not recommended).");
        return ExitCode::from(1);
    }

    // Graduate
    match manager.graduate_by_name(test_name) {
        Ok(result) => {
            if result.success {
                println!("{}", result.message);
                if let Some(path) = result.extractor_path {
                    println!("Production extractor: {}", path.display());
                }
                ExitCode::SUCCESS
            } else {
                eprintln!("{}", result.message);
                ExitCode::from(1)
            }
        }
        Err(e) => {
            eprintln!("Graduation failed: {e}");
            ExitCode::from(3)
        }
    }
}

/// Handle rollback command
pub fn handle_shadow_rollback(config: &AphoriaConfig, test_name: &str, reason: &str) -> ExitCode {
    // Create registry
    let registry =
        match ShadowExtractorRegistry::new(&config.shadow, &config.learning.promotion.output_dir) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("Failed to open shadow registry: {e}");
                return ExitCode::from(3);
            }
        };

    let production_dir = get_production_dir(config);
    let manager = GraduationManager::new(&registry, &config.shadow, &production_dir);

    // Rollback
    match manager.rollback_by_name(test_name, reason.to_string()) {
        Ok(result) => {
            if result.success {
                println!("{}", result.message);
                ExitCode::SUCCESS
            } else {
                eprintln!("{}", result.message);
                ExitCode::from(1)
            }
        }
        Err(e) => {
            eprintln!("Rollback failed: {e}");
            ExitCode::from(3)
        }
    }
}

/// Handle auto-check command - scan all active tests and rollback if needed
pub fn handle_shadow_auto_check(config: &AphoriaConfig) -> ExitCode {
    // Create registry
    let registry =
        match ShadowExtractorRegistry::new(&config.shadow, &config.learning.promotion.output_dir) {
            Ok(r) => r,
            Err(e) => {
                eprintln!("Failed to open shadow registry: {e}");
                return ExitCode::from(3);
            }
        };

    let production_dir = get_production_dir(config);
    let manager = GraduationManager::new(&registry, &config.shadow, &production_dir);

    match manager.check_auto_rollback() {
        Ok(result) => {
            if result.checked == 0 {
                println!("No active shadow tests to check.");
            } else if result.rolled_back == 0 {
                println!(
                    "Checked {} shadow test(s). All within threshold ({:.1}% max FP rate).",
                    result.checked,
                    config.shadow.rollback_threshold * 100.0
                );
            } else {
                println!(
                    "⚠️  Auto-rolled back {} of {} shadow test(s):",
                    result.rolled_back, result.checked
                );
                for name in &result.rolled_back_names {
                    println!("  - {}", name);
                }
            }

            if !result.errors.is_empty() {
                println!();
                println!("Errors encountered:");
                for err in &result.errors {
                    println!("  - {}", err);
                }
            }

            ExitCode::SUCCESS
        }
        Err(e) => {
            eprintln!("Auto-check failed: {e}");
            ExitCode::from(3)
        }
    }
}

/// Truncate a string for display
fn truncate(s: &str, max_len: usize) -> String {
    if s.len() <= max_len {
        s.to_string()
    } else {
        format!("{}...", &s[..max_len - 3])
    }
}
