use crate::commands::describe::describe_changes_plain;
use crate::commands::explain::{self, ChangeStatus, ResourceContext};
use super::ResourceDiff;
pub(super) async fn generate_ai_narrative(
diffs: &[ResourceDiff],
command_context: &str,
total_unchanged: usize,
) -> Option<String> {
let changed: Vec<&ResourceDiff> = diffs.iter().filter(|d| !d.result.is_equal).collect();
if changed.is_empty() {
return None;
}
eprintln!("Generating AI explanation...");
let labels = Some(("locally", "on the server"));
let contexts: Vec<ResourceContext> = changed
.iter()
.map(|d| {
let status = if d.result.changes.len() == 1 && d.result.changes[0].path == "." {
ChangeStatus::New
} else {
ChangeStatus::Modified
};
let descriptions =
describe_changes_plain(&d.result.changes, d.kind, &d.resource_name, labels);
ResourceContext {
kind: d.kind,
name: d.resource_name.clone(),
status,
local_content: d.local_content.clone(),
remote_content: d.remote_content.clone(),
descriptions,
}
})
.collect();
match explain::explain_all_changes(&contexts, command_context, total_unchanged).await {
Ok(narrative) => Some(narrative),
Err(e) => {
eprintln!("Warning: AI explanation failed: {}", e);
None
}
}
}
pub(super) async fn generate_ai_summaries(
diffs: &[ResourceDiff],
) -> std::collections::HashMap<String, String> {
let mut summaries = std::collections::HashMap::new();
let changed: Vec<&ResourceDiff> = diffs.iter().filter(|d| !d.result.is_equal).collect();
if changed.is_empty() {
return summaries;
}
eprintln!("Generating AI explanations...");
let labels = Some(("locally", "on the server"));
let futures: Vec<_> = changed
.iter()
.map(|d| {
let display_id = d.display_id.clone();
let resource_type = d.kind.display_name().to_string();
let resource_name = d.resource_name.clone();
let changes = d.result.changes.clone();
let descriptions =
describe_changes_plain(&d.result.changes, d.kind, &d.resource_name, labels);
async move {
let result = crate::commands::explain::explain_resource_changes(
&resource_type,
&resource_name,
&changes,
&descriptions,
"diff",
)
.await;
(display_id, result)
}
})
.collect();
let results = futures::future::join_all(futures).await;
for (display_id, result) in results {
match result {
Ok(summary) => {
summaries.insert(display_id, summary);
}
Err(e) => {
eprintln!("Warning: AI explanation failed for {}: {}", display_id, e);
}
}
}
summaries
}