//! Mental model commands for managing user-curated summaries.

use anyhow::Result;

use crate::api::ApiClient;
use crate::output::{self, OutputFormat};
use crate::ui;

use hindsight_client::types;

/// List mental models for a bank
pub fn list(
    client: &ApiClient,
    bank_id: &str,
    verbose: bool,
    output_format: OutputFormat,
) -> Result<()> {
    let spinner = if output_format == OutputFormat::Pretty {
        Some(ui::create_spinner("Fetching mental models..."))
    } else {
        None
    };

    let response = client.list_mental_models(bank_id, verbose);

    if let Some(mut sp) = spinner {
        sp.finish();
    }

    match response {
        Ok(result) => {
            if output_format == OutputFormat::Pretty {
                ui::print_section_header(&format!("Mental Models: {}", bank_id));

                if result.items.is_empty() {
                    println!("  {}", ui::dim("No mental models found."));
                } else {
                    for mental_model in &result.items {
                        println!(
                            "  {} {}",
                            ui::gradient_start(&mental_model.id),
                            mental_model.name
                        );

                        // Show content preview
                        if let Some(ref content) = mental_model.content {
                            let preview: String = content.chars().take(80).collect();
                            let ellipsis = if content.len() > 80 { "..." } else { "" };
                            println!("    {}{}", ui::dim(&preview), ellipsis);
                        }

                        println!();
                    }
                }
            } else {
                output::print_output(&result, output_format)?;
            }
            Ok(())
        }
        Err(e) => Err(e),
    }
}

/// Get a specific mental model
pub fn get(
    client: &ApiClient,
    bank_id: &str,
    mental_model_id: &str,
    verbose: bool,
    output_format: OutputFormat,
) -> Result<()> {
    let spinner = if output_format == OutputFormat::Pretty {
        Some(ui::create_spinner("Fetching mental model..."))
    } else {
        None
    };

    let response = client.get_mental_model(bank_id, mental_model_id, verbose);

    if let Some(mut sp) = spinner {
        sp.finish();
    }

    match response {
        Ok(mental_model) => {
            if output_format == OutputFormat::Pretty {
                print_mental_model_detail(&mental_model);
            } else {
                output::print_output(&mental_model, output_format)?;
            }
            Ok(())
        }
        Err(e) => Err(e),
    }
}

/// Create a new mental model
#[allow(clippy::too_many_arguments)]
pub fn create(
    client: &ApiClient,
    bank_id: &str,
    name: &str,
    source_query: &str,
    id: Option<&str>,
    tags: Vec<String>,
    max_tokens: i64,
    trigger_refresh_after_consolidation: bool,
    verbose: bool,
    output_format: OutputFormat,
) -> Result<()> {
    let spinner = if output_format == OutputFormat::Pretty {
        Some(ui::create_spinner("Creating mental model..."))
    } else {
        None
    };

    // Only send a trigger when the user opted in, so the server's default
    // behaviour is preserved otherwise.
    let trigger = if trigger_refresh_after_consolidation {
        Some(types::MentalModelTriggerInput {
            mode: types::Mode::Full,
            refresh_after_consolidation: true,
            exclude_mental_models: false,
            exclude_mental_model_ids: None,
            fact_types: None,
            tag_groups: None,
            tags_match: None,
            include_chunks: None,
            recall_max_tokens: None,
            recall_chunks_max_tokens: None,
        })
    } else {
        None
    };

    let request = types::CreateMentalModelRequest {
        id: id.map(|s| s.to_string()),
        name: name.to_string(),
        source_query: source_query.to_string(),
        max_tokens,
        tags,
        trigger,
    };

    let response = client.create_mental_model(bank_id, &request, verbose);

    if let Some(mut sp) = spinner {
        sp.finish();
    }

    match response {
        Ok(result) => {
            if output_format == OutputFormat::Pretty {
                ui::print_success(&format!("Mental model created, operation_id: {}", result.operation_id));
            } else {
                output::print_output(&result, output_format)?;
            }
            Ok(())
        }
        Err(e) => Err(e),
    }
}

/// Update a mental model
#[allow(clippy::too_many_arguments)]
pub fn update(
    client: &ApiClient,
    bank_id: &str,
    mental_model_id: &str,
    name: Option<String>,
    source_query: Option<String>,
    max_tokens: Option<i64>,
    tags: Option<Vec<String>>,
    trigger_refresh_after_consolidation: Option<bool>,
    verbose: bool,
    output_format: OutputFormat,
) -> Result<()> {
    if name.is_none()
        && source_query.is_none()
        && max_tokens.is_none()
        && tags.is_none()
        && trigger_refresh_after_consolidation.is_none()
    {
        anyhow::bail!(
            "At least one of --name, --source-query, --max-tokens, --tags, or \
             --trigger-refresh-after-consolidation must be provided"
        );
    }

    let spinner = if output_format == OutputFormat::Pretty {
        Some(ui::create_spinner("Updating mental model..."))
    } else {
        None
    };

    // Only build a trigger override when the user actually passed the flag;
    // sending None leaves the existing trigger config untouched on the server.
    let trigger = trigger_refresh_after_consolidation.map(|refresh| types::MentalModelTriggerInput {
        mode: types::Mode::Full,
        refresh_after_consolidation: refresh,
        exclude_mental_models: false,
        exclude_mental_model_ids: None,
        fact_types: None,
        tag_groups: None,
        tags_match: None,
        include_chunks: None,
        recall_max_tokens: None,
        recall_chunks_max_tokens: None,
    });

    let request = types::UpdateMentalModelRequest {
        name,
        source_query,
        max_tokens,
        tags,
        trigger,
    };

    let response = client.update_mental_model(bank_id, mental_model_id, &request, verbose);

    if let Some(mut sp) = spinner {
        sp.finish();
    }

    match response {
        Ok(mental_model) => {
            if output_format == OutputFormat::Pretty {
                ui::print_success(&format!("Mental model '{}' updated successfully", mental_model_id));
                println!();
                print_mental_model_detail(&mental_model);
            } else {
                output::print_output(&mental_model, output_format)?;
            }
            Ok(())
        }
        Err(e) => Err(e),
    }
}

/// Delete a mental model
pub fn delete(
    client: &ApiClient,
    bank_id: &str,
    mental_model_id: &str,
    yes: bool,
    verbose: bool,
    output_format: OutputFormat,
) -> Result<()> {
    // Confirmation prompt unless -y flag is used
    if !yes && output_format == OutputFormat::Pretty {
        let message = format!(
            "Are you sure you want to delete mental model '{}'? This cannot be undone.",
            mental_model_id
        );

        let confirmed = ui::prompt_confirmation(&message)?;

        if !confirmed {
            ui::print_info("Operation cancelled");
            return Ok(());
        }
    }

    let spinner = if output_format == OutputFormat::Pretty {
        Some(ui::create_spinner("Deleting mental model..."))
    } else {
        None
    };

    let response = client.delete_mental_model(bank_id, mental_model_id, verbose);

    if let Some(mut sp) = spinner {
        sp.finish();
    }

    match response {
        Ok(_) => {
            if output_format == OutputFormat::Pretty {
                ui::print_success(&format!("Mental model '{}' deleted successfully", mental_model_id));
            } else {
                println!("{{\"success\": true}}");
            }
            Ok(())
        }
        Err(e) => Err(e),
    }
}

/// Refresh a mental model
pub fn refresh(
    client: &ApiClient,
    bank_id: &str,
    mental_model_id: &str,
    verbose: bool,
    output_format: OutputFormat,
) -> Result<()> {
    let spinner = if output_format == OutputFormat::Pretty {
        Some(ui::create_spinner("Submitting mental model refresh..."))
    } else {
        None
    };

    let response = client.refresh_mental_model(bank_id, mental_model_id, verbose);

    if let Some(mut sp) = spinner {
        sp.finish();
    }

    match response {
        Ok(operation) => {
            if output_format == OutputFormat::Pretty {
                ui::print_success(&format!(
                    "Mental model refresh submitted. Operation ID: {}",
                    operation.operation_id
                ));
                println!("  {} {}", ui::dim("Status:"), operation.status);
                println!();
                println!("{}", ui::dim("Use 'hindsight operations get' to check the operation status."));
            } else {
                output::print_output(&operation, output_format)?;
            }
            Ok(())
        }
        Err(e) => Err(e),
    }
}

/// Get the change history of a mental model
pub fn history(
    client: &ApiClient,
    bank_id: &str,
    mental_model_id: &str,
    verbose: bool,
    output_format: OutputFormat,
) -> Result<()> {
    let spinner = if output_format == OutputFormat::Pretty {
        Some(ui::create_spinner("Fetching mental model history..."))
    } else {
        None
    };

    let response = client.get_mental_model_history(bank_id, mental_model_id, verbose);

    if let Some(mut sp) = spinner {
        sp.finish();
    }

    match response {
        Ok(history) => {
            if output_format == OutputFormat::Pretty {
                ui::print_section_header(&format!("History: {}", mental_model_id));

                if let Some(entries) = history.as_array() {
                    if entries.is_empty() {
                        println!("  {}", ui::dim("No history entries found."));
                    } else {
                        for entry in entries {
                            let changed_at = entry.get("changed_at").and_then(|v| v.as_str()).unwrap_or("unknown");
                            let previous = entry.get("previous_content").and_then(|v| v.as_str()).unwrap_or("(none)");
                            println!("  {} {}", ui::dim("Changed at:"), changed_at);
                            let preview: String = previous.chars().take(80).collect();
                            let ellipsis = if previous.len() > 80 { "..." } else { "" };
                            println!("  {} {}{}", ui::dim("Previous:"), ui::dim(&preview), ellipsis);
                            println!();
                        }
                    }
                }
            } else {
                output::print_output(&history, output_format)?;
            }
            Ok(())
        }
        Err(e) => Err(e),
    }
}

// Helper function to print mental model details
fn print_mental_model_detail(mental_model: &types::MentalModelResponse) {
    ui::print_section_header(&mental_model.name);

    println!("  {} {}", ui::dim("ID:"), ui::gradient_start(&mental_model.id));
    if let Some(ref source_query) = mental_model.source_query {
        println!("  {} {}", ui::dim("Source Query:"), source_query);
    }

    if let Some(ref content) = mental_model.content {
        println!();
        println!("{}", ui::gradient_text("─── Content ───"));
        println!();
        println!("{}", content);
        println!();
    }
}
