//! Scope command handlers for knowledge hierarchy management.

use std::process::ExitCode;

use chrono::{DateTime, Duration, NaiveDate, Utc};

use aphoria::scope::{override_store_dir, OverrideStore, OverrideValue, ScopeId, ScopeOverride};
use aphoria::AphoriaConfig;

use crate::cli::ScopeCommands;

pub async fn handle_scope_command(command: ScopeCommands, config: &AphoriaConfig) -> ExitCode {
    match command {
        ScopeCommands::Status => handle_scope_status(config),
        ScopeCommands::Override { concept_path, value, reason, evidence, expires } => {
            handle_scope_override(config, concept_path, value, reason, evidence, expires)
        }
        ScopeCommands::List { include_inherited, show_expired } => {
            handle_scope_list(config, include_inherited, show_expired)
        }
        ScopeCommands::Remove { concept_path, force } => {
            handle_scope_remove(config, concept_path, force)
        }
    }
}

fn handle_scope_status(config: &AphoriaConfig) -> ExitCode {
    println!("Scope Configuration");
    println!("===================");
    println!();

    // Show configured scope hierarchy
    println!("Configured Hierarchy:");
    if let Some(ref org) = config.scope.organization {
        println!("  Organization: {}", org);
    } else {
        println!("  Organization: (not set)");
    }

    if let Some(ref team) = config.scope.team {
        println!("  Team:         {}", team);
    } else {
        println!("  Team:         (not set)");
    }

    if let Some(ref project) = config.scope.project {
        println!("  Project:      {}", project);
    } else {
        // Try to infer from project config
        let project_name = config.project.name.as_deref().unwrap_or("(auto-detected)");
        println!("  Project:      {}", project_name);
    }

    println!();

    // Show inheritance chain
    let ctx = config.scope.to_context();
    let chain = ctx.inheritance_chain();

    if chain.is_empty() {
        println!("Inheritance Chain: (empty - no scopes configured)");
    } else {
        println!("Inheritance Chain (most specific first):");
        for (i, scope) in chain.iter().enumerate() {
            let arrow = if i == 0 { "  *" } else { "   " };
            println!("{} {} ({})", arrow, scope.name, scope.level);
        }
    }

    // Show override store status
    println!();
    let store_dir = override_store_dir();
    match OverrideStore::new(&store_dir) {
        Ok(store) => {
            let active = store.active_count();
            let expired = store.expired_count();
            println!("Override Store:");
            println!("  Location:     {}", store.path().display());
            println!("  Active:       {}", active);
            println!("  Expired:      {}", expired);
        }
        Err(e) => {
            println!("Override Store: Error - {}", e);
        }
    }

    println!();
    println!("Configure scopes in aphoria.toml:");
    println!();
    println!("  [scope]");
    println!("  project = \"my-project\"");
    println!("  team = \"my-team\"");
    println!("  organization = \"my-org\"");

    ExitCode::SUCCESS
}

fn handle_scope_override(
    config: &AphoriaConfig,
    concept_path: String,
    value: String,
    reason: String,
    evidence: Option<String>,
    expires: Option<String>,
) -> ExitCode {
    // Get current scope
    let ctx = config.scope.to_context();
    let current_scope = match ctx.current_scope() {
        Some(s) => s,
        None => {
            eprintln!("No scope configured. Cannot create override.");
            eprintln!();
            eprintln!("Configure a scope in aphoria.toml first:");
            eprintln!("  [scope]");
            eprintln!("  project = \"my-project\"");
            return ExitCode::from(1);
        }
    };

    // Validate scope name
    if let Err(e) = ScopeId::validate_name(&current_scope.name) {
        eprintln!("Invalid scope name: {}", e);
        return ExitCode::from(1);
    }

    // Parse expiry if provided
    let expires_at = match expires.as_ref() {
        Some(exp_str) => match parse_expiry(exp_str) {
            Ok(dt) => Some(dt),
            Err(e) => {
                eprintln!("Invalid expiry format: {}", e);
                eprintln!();
                eprintln!("Valid formats:");
                eprintln!("  Duration: 90d, 30d, 7d (days from now)");
                eprintln!("  Date: 2026-12-31 (ISO 8601 date)");
                return ExitCode::from(1);
            }
        },
        None => None,
    };

    // Parse the value (infer type from string)
    let parsed_value = OverrideValue::parse(&value);

    // Extract predicate from concept_path (last segment after /)
    let predicate = concept_path.rsplit('/').next().unwrap_or("value").to_string();

    // Create override
    let mut override_record =
        ScopeOverride::new(current_scope.clone(), &concept_path, predicate, parsed_value, &reason);

    if let Some(ref ev) = evidence {
        override_record = override_record.with_evidence(ev);
    }

    if let Some(exp) = expires_at {
        override_record = override_record.with_expires_at(exp);
    }

    // Persist to store
    let store_dir = override_store_dir();
    let mut store = match OverrideStore::new(&store_dir) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("Failed to open override store: {}", e);
            return ExitCode::from(3);
        }
    };

    if let Err(e) = store.add(override_record) {
        eprintln!("Failed to save override: {}", e);
        return ExitCode::from(3);
    }

    println!("Override created successfully.");
    println!();
    println!("  Scope:        {}", current_scope);
    println!("  Concept:      {}", concept_path);
    println!("  Value:        {}", value);
    println!("  Reason:       {}", reason);

    if let Some(ref ev) = evidence {
        println!("  Evidence:     {}", ev);
    }

    if let Some(ref exp) = expires {
        println!("  Expires:      {}", exp);
    }

    println!();
    println!("Stored in: {}", store.path().display());

    ExitCode::SUCCESS
}

fn handle_scope_list(
    config: &AphoriaConfig,
    include_inherited: bool,
    show_expired: bool,
) -> ExitCode {
    let ctx = config.scope.to_context();
    let current_scope = ctx.current_scope();
    let chain = ctx.inheritance_chain();

    println!("Scope Overrides");
    println!("===============");
    println!();

    if let Some(ref scope) = current_scope {
        println!("Current scope: {}", scope);
    } else {
        println!("Current scope: (none configured)");
    }

    if include_inherited {
        println!("Showing: local + inherited overrides");
    } else {
        println!("Showing: local overrides only");
    }

    if show_expired {
        println!("Including: expired overrides");
    }

    println!();

    // Open store
    let store_dir = override_store_dir();
    let store = match OverrideStore::new(&store_dir) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("Failed to open override store: {}", e);
            return ExitCode::from(3);
        }
    };

    // Get overrides
    let overrides = if include_inherited {
        store.list_with_inheritance(&chain, show_expired)
    } else {
        store.list(current_scope.as_ref(), show_expired)
    };

    if overrides.is_empty() {
        println!("No overrides found.");
        println!();
        println!("Create an override with:");
        println!("  aphoria scope override <concept_path> -V <value> -r <reason>");
        return ExitCode::SUCCESS;
    }

    // Print table header
    println!("{:<30} {:<12} {:<20} Reason", "Concept", "Scope", "Value");
    println!("{}", "-".repeat(80));

    for o in &overrides {
        let status = if o.is_expired() { " (expired)" } else { "" };
        let scope_short = format!("{}:{}", o.scope.level, truncate(&o.scope.name, 8));

        println!(
            "{:<30} {:<12} {:<20} {}{}",
            truncate(&o.concept_path, 30),
            scope_short,
            truncate(&o.value.to_string(), 20),
            truncate(&o.reason, 20),
            status
        );

        if let Some(ref ev) = o.evidence {
            println!("    Evidence: {}", ev);
        }

        if let Some(days) = o.days_until_expiration() {
            if days > 0 {
                println!("    Expires in: {} days", days);
            }
        }
    }

    println!();
    println!("Total: {} override(s)", overrides.len());

    ExitCode::SUCCESS
}

fn handle_scope_remove(config: &AphoriaConfig, concept_path: String, force: bool) -> ExitCode {
    let ctx = config.scope.to_context();
    let current_scope = match ctx.current_scope() {
        Some(s) => s,
        None => {
            eprintln!("No scope configured.");
            return ExitCode::from(1);
        }
    };

    if !force {
        println!("Would remove override for '{}' at scope '{}'", concept_path, current_scope);
        println!();
        println!("Use --force to confirm removal.");
        return ExitCode::SUCCESS;
    }

    // Open store
    let store_dir = override_store_dir();
    let mut store = match OverrideStore::new(&store_dir) {
        Ok(s) => s,
        Err(e) => {
            eprintln!("Failed to open override store: {}", e);
            return ExitCode::from(3);
        }
    };

    match store.remove(&current_scope, &concept_path) {
        Ok(true) => {
            println!("Removed override for '{}' at scope '{}'", concept_path, current_scope);
        }
        Ok(false) => {
            println!("No override found for '{}' at scope '{}'", concept_path, current_scope);
        }
        Err(e) => {
            eprintln!("Failed to remove override: {}", e);
            return ExitCode::from(3);
        }
    }

    ExitCode::SUCCESS
}

/// Parse an expiry string into a DateTime.
///
/// Supports:
/// - Duration format: "90d", "30d", "7d" (days from now)
/// - ISO date format: "2026-12-31"
fn parse_expiry(s: &str) -> Result<DateTime<Utc>, String> {
    let s = s.trim();

    // Try duration format (e.g., "90d")
    if let Some(days_str) = s.strip_suffix('d') {
        let days: i64 =
            days_str.parse().map_err(|_| format!("Invalid day count: '{}'", days_str))?;
        if days <= 0 {
            return Err("Days must be positive".to_string());
        }
        return Ok(Utc::now() + Duration::days(days));
    }

    // Try ISO date format (e.g., "2026-12-31")
    if let Ok(date) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
        let datetime = date.and_hms_opt(23, 59, 59).ok_or_else(|| "Invalid date".to_string())?;
        return Ok(DateTime::from_naive_utc_and_offset(datetime, Utc));
    }

    Err(format!("Could not parse '{}'. Use '90d' for duration or '2026-12-31' for date.", s))
}

/// 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.saturating_sub(3)])
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_expiry_duration() {
        let result = parse_expiry("90d").expect("parse 90d");
        let days_from_now = (result - Utc::now()).num_days();
        assert!((89..=90).contains(&days_from_now));

        let result = parse_expiry("7d").expect("parse 7d");
        let days_from_now = (result - Utc::now()).num_days();
        assert!((6..=7).contains(&days_from_now));
    }

    #[test]
    fn test_parse_expiry_date() {
        let result = parse_expiry("2030-12-31").expect("parse date");
        assert_eq!(result.date_naive().to_string(), "2030-12-31");
    }

    #[test]
    fn test_parse_expiry_invalid() {
        assert!(parse_expiry("").is_err());
        assert!(parse_expiry("invalid").is_err());
        assert!(parse_expiry("0d").is_err());
        assert!(parse_expiry("-5d").is_err());
    }
}
