use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
use tracing::{debug, info, warn};
use crate::brp_messages::DebugCommand;
use crate::error::Result;
use crate::pattern_learning::{DebugPattern, PatternLearningSystem};
use crate::suggestion_engine::SuggestionEngine;
const MIN_AUTOMATION_OCCURRENCES: usize = 5;
const MIN_SUCCESS_RATE: f64 = 0.8;
const MAX_AUTOMATED_STEPS: usize = 10;
const CHECKPOINT_INTERVAL: usize = 3;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutomatedWorkflow {
pub id: String,
pub name: String,
pub commands: Vec<DebugCommand>,
pub success_rate: f64,
pub execution_count: usize,
pub user_approved: bool,
pub scope: AutomationScope,
pub pattern_id: String,
#[serde(skip)]
pub last_executed: Option<Instant>,
pub checkpoints: Vec<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AutomationScope {
ReadOnly,
SafeCommands,
AllCommands,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionContext {
pub session_id: String,
pub user_preferences: UserPreferences,
pub current_step: usize,
#[serde(skip, default = "Instant::now")]
pub start_time: Instant,
pub checkpoints: Vec<ExecutionCheckpoint>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserPreferences {
pub automation_enabled: bool,
pub preferred_scope: AutomationScope,
pub require_confirmation: bool,
pub auto_rollback: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionCheckpoint {
pub step_index: usize,
pub state_snapshot: String,
#[serde(skip, default = "Instant::now")]
pub timestamp: Instant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExecutionResult {
Success {
steps_executed: usize,
total_time: Duration,
results: Vec<String>,
},
Failed {
failed_step: usize,
error_message: String,
rollback_performed: bool,
},
Interrupted {
step_interrupted: usize,
reason: String,
},
RequiresApproval {
workflow_id: String,
next_step: usize,
},
}
pub struct WorkflowAutomation {
workflows: Arc<RwLock<HashMap<String, AutomatedWorkflow>>>,
active_executions: Arc<RwLock<HashMap<String, ExecutionContext>>>,
pattern_system: Arc<PatternLearningSystem>,
suggestion_engine: Arc<SuggestionEngine>,
default_preferences: UserPreferences,
}
impl WorkflowAutomation {
pub fn new(
pattern_system: Arc<PatternLearningSystem>,
suggestion_engine: Arc<SuggestionEngine>,
) -> Self {
Self {
workflows: Arc::new(RwLock::new(HashMap::new())),
active_executions: Arc::new(RwLock::new(HashMap::new())),
pattern_system,
suggestion_engine,
default_preferences: UserPreferences {
automation_enabled: true,
preferred_scope: AutomationScope::SafeCommands,
require_confirmation: true,
auto_rollback: true,
},
}
}
pub async fn analyze_automation_opportunities(&self) -> Result<Vec<AutomatedWorkflow>> {
let mut opportunities = Vec::new();
let patterns = self.get_frequent_patterns().await?;
for pattern in patterns {
if self.is_automation_candidate(&pattern).await {
let workflow = self.create_workflow_from_pattern(pattern).await?;
opportunities.push(workflow);
}
}
info!("Found {} automation opportunities", opportunities.len());
Ok(opportunities)
}
async fn is_automation_candidate(&self, pattern: &DebugPattern) -> bool {
pattern.frequency >= MIN_AUTOMATION_OCCURRENCES
&& pattern.success_rate >= MIN_SUCCESS_RATE
&& pattern.sequence.len() <= MAX_AUTOMATED_STEPS
&& self.is_safe_for_automation(&pattern.sequence).await
}
async fn is_safe_for_automation(&self, _commands: &[crate::pattern_learning::AnonymizedCommand]) -> bool {
true
}
async fn create_workflow_from_pattern(&self, pattern: DebugPattern) -> Result<AutomatedWorkflow> {
let commands = self.convert_anonymized_to_commands(&pattern.sequence).await?;
let workflow = AutomatedWorkflow {
id: format!("auto_{}", pattern.id),
name: self.generate_workflow_name(&pattern),
commands,
success_rate: pattern.success_rate,
execution_count: 0,
user_approved: false,
scope: AutomationScope::ReadOnly,
pattern_id: pattern.id,
last_executed: None,
checkpoints: self.calculate_checkpoints(&pattern.sequence),
};
Ok(workflow)
}
pub async fn execute_workflow(
&self,
workflow_id: &str,
session_id: String,
preferences: Option<UserPreferences>,
) -> Result<ExecutionResult> {
let workflows = self.workflows.read().await;
let workflow = workflows.get(workflow_id)
.ok_or_else(|| crate::error::Error::Validation(format!("Workflow not found: {}", workflow_id)))?
.clone();
drop(workflows);
let prefs = preferences.unwrap_or_else(|| self.default_preferences.clone());
if !prefs.automation_enabled {
return Ok(ExecutionResult::Failed {
failed_step: 0,
error_message: "Automation disabled by user".to_string(),
rollback_performed: false,
});
}
if !workflow.user_approved && prefs.require_confirmation {
return Ok(ExecutionResult::RequiresApproval {
workflow_id: workflow_id.to_string(),
next_step: 0,
});
}
let context = ExecutionContext {
session_id: session_id.clone(),
user_preferences: prefs,
current_step: 0,
start_time: Instant::now(),
checkpoints: Vec::new(),
};
{
let mut executions = self.active_executions.write().await;
executions.insert(session_id.clone(), context);
}
let result = self.execute_workflow_steps(&workflow, &session_id).await;
{
let mut executions = self.active_executions.write().await;
executions.remove(&session_id);
}
self.update_workflow_stats(&workflow.id, &result).await?;
result
}
async fn execute_workflow_steps(
&self,
workflow: &AutomatedWorkflow,
session_id: &str,
) -> Result<ExecutionResult> {
let mut results = Vec::new();
let start_time = Instant::now();
for (step_index, command) in workflow.commands.iter().enumerate() {
if workflow.checkpoints.contains(&step_index) {
self.create_checkpoint(session_id, step_index).await?;
}
match self.execute_command(command.clone(), session_id).await {
Ok(result) => {
results.push(result);
debug!("Workflow step {} completed successfully", step_index);
}
Err(e) => {
warn!("Workflow step {} failed: {}", step_index, e);
let should_rollback = {
let executions = self.active_executions.read().await;
executions.get(session_id)
.map(|ctx| ctx.user_preferences.auto_rollback)
.unwrap_or(false)
};
if should_rollback {
self.perform_rollback(session_id, step_index).await?;
}
return Ok(ExecutionResult::Failed {
failed_step: step_index,
error_message: e.to_string(),
rollback_performed: should_rollback,
});
}
}
}
Ok(ExecutionResult::Success {
steps_executed: workflow.commands.len(),
total_time: start_time.elapsed(),
results,
})
}
pub async fn approve_workflow(&self, workflow_id: &str) -> Result<()> {
let mut workflows = self.workflows.write().await;
if let Some(workflow) = workflows.get_mut(workflow_id) {
workflow.user_approved = true;
info!("Workflow {} approved for automation", workflow_id);
Ok(())
} else {
Err(crate::error::Error::Validation(format!("Workflow not found: {}", workflow_id)))
}
}
pub async fn get_workflows(&self) -> Vec<AutomatedWorkflow> {
let workflows = self.workflows.read().await;
workflows.values().cloned().collect()
}
async fn get_frequent_patterns(&self) -> Result<Vec<DebugPattern>> {
Ok(Vec::new())
}
async fn convert_anonymized_to_commands(
&self,
_anonymized: &[crate::pattern_learning::AnonymizedCommand],
) -> Result<Vec<DebugCommand>> {
Ok(vec![DebugCommand::GetSystemInfo {
system_name: None,
include_scheduling: None
}])
}
fn generate_workflow_name(&self, pattern: &DebugPattern) -> String {
format!("Auto Workflow ({}% success)", (pattern.success_rate * 100.0) as i32)
}
fn calculate_checkpoints(&self, sequence: &[crate::pattern_learning::AnonymizedCommand]) -> Vec<usize> {
let mut checkpoints = Vec::new();
for i in (0..sequence.len()).step_by(CHECKPOINT_INTERVAL) {
checkpoints.push(i);
}
checkpoints
}
async fn create_checkpoint(&self, session_id: &str, step_index: usize) -> Result<()> {
debug!("Creating checkpoint for session {} at step {}", session_id, step_index);
Ok(())
}
async fn execute_command(&self, _command: DebugCommand, _session_id: &str) -> Result<String> {
Ok("Command executed successfully".to_string())
}
async fn perform_rollback(&self, session_id: &str, _failed_step: usize) -> Result<()> {
debug!("Performing rollback for session {}", session_id);
Ok(())
}
async fn update_workflow_stats(&self, workflow_id: &str, result: &Result<ExecutionResult>) -> Result<()> {
let mut workflows = self.workflows.write().await;
if let Some(workflow) = workflows.get_mut(workflow_id) {
workflow.execution_count += 1;
workflow.last_executed = Some(Instant::now());
let success = matches!(result, Ok(ExecutionResult::Success { .. }));
let old_rate = workflow.success_rate;
let count = workflow.execution_count as f64;
workflow.success_rate = (old_rate * (count - 1.0) + if success { 1.0 } else { 0.0 }) / count;
}
Ok(())
}
}