//! Task actions for ecosystem integration

use serde::{Deserialize, Serialize};

/// Type of action a task can trigger in external systems
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ActionType {
    /// GitOps deployment action
    Gitops,

    /// Validation action
    Validate,

    /// Backup operation
    Backup,

    /// Restore operation
    Restore,

    /// Security audit
    Audit,

    /// n8n workflow execution
    N8n,

    /// Custom action
    Custom(String),
}

impl ActionType {
    pub fn as_str(&self) -> &str {
        match self {
            Self::Gitops => "gitops",
            Self::Validate => "validate",
            Self::Backup => "backup",
            Self::Restore => "restore",
            Self::Audit => "audit",
            Self::N8n => "n8n",
            Self::Custom(s) => s,
        }
    }
}

impl std::fmt::Display for ActionType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// When a task action is triggered
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ActionTrigger {
    /// On task creation
    OnCreate,

    /// When task starts
    OnStart,

    /// When task completes
    OnComplete,

    /// When task is blocked
    OnBlock,

    /// Manual trigger
    Manual,

    /// Scheduled trigger
    Scheduled { cron: String },

    /// Triggered by phase transition
    PhaseTransition { from: String, to: String },

    /// Custom trigger
    Custom(String),
}

impl ActionTrigger {
    pub fn is_manual(&self) -> bool {
        matches!(self, Self::Manual)
    }

    pub fn is_automatic(&self) -> bool {
        matches!(
            self,
            Self::OnCreate
                | Self::OnStart
                | Self::OnComplete
                | Self::OnBlock
                | Self::Scheduled { .. }
                | Self::PhaseTransition { .. }
        )
    }
}

/// Task action configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskAction {
    /// Action ID (unique within task)
    pub id: String,

    /// Type of action
    pub action_type: ActionType,

    /// When to trigger
    pub trigger: ActionTrigger,

    /// Action configuration (tool-specific)
    pub config: serde_json::Value,

    /// Whether action is enabled
    #[serde(default = "default_true")]
    pub enabled: bool,

    /// Description of what this action does
    pub description: Option<String>,

    /// Retry on failure
    #[serde(default)]
    pub retry_count: u32,

    /// Timeout in seconds
    pub timeout_secs: Option<u64>,
}

fn default_true() -> bool {
    true
}

impl TaskAction {
    /// Create a new task action
    pub fn new(
        id: String,
        action_type: ActionType,
        trigger: ActionTrigger,
        config: serde_json::Value,
    ) -> Self {
        Self {
            id,
            action_type,
            trigger,
            config,
            enabled: true,
            description: None,
            retry_count: 0,
            timeout_secs: None,
        }
    }

    /// Set description
    pub fn with_description(mut self, desc: String) -> Self {
        self.description = Some(desc);
        self
    }

    /// Set retry count
    pub fn with_retry(mut self, count: u32) -> Self {
        self.retry_count = count;
        self
    }

    /// Set timeout
    pub fn with_timeout(mut self, secs: u64) -> Self {
        self.timeout_secs = Some(secs);
        self
    }

    /// Enable/disable
    pub fn set_enabled(mut self, enabled: bool) -> Self {
        self.enabled = enabled;
        self
    }
}

/// Result of executing an action
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActionResult {
    /// Action ID that was executed
    pub action_id: String,

    /// Whether execution succeeded
    pub success: bool,

    /// Result message
    pub message: String,

    /// Output data from action
    pub output: Option<serde_json::Value>,

    /// Execution duration in ms
    pub duration_ms: u64,

    /// Error details if any
    pub error: Option<String>,
}

impl ActionResult {
    /// Create successful result
    pub fn success(action_id: String, message: String, duration_ms: u64) -> Self {
        Self {
            action_id,
            success: true,
            message,
            output: None,
            duration_ms,
            error: None,
        }
    }

    /// Create failed result
    pub fn failure(action_id: String, message: String, error: String, duration_ms: u64) -> Self {
        Self {
            action_id,
            success: false,
            message,
            output: None,
            duration_ms,
            error: Some(error),
        }
    }

    /// Add output data
    pub fn with_output(mut self, output: serde_json::Value) -> Self {
        self.output = Some(output);
        self
    }
}

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

    #[test]
    fn test_action_type_display() {
        assert_eq!(ActionType::Gitops.to_string(), "gitops");
        assert_eq!(ActionType::Validate.to_string(), "validate");
        assert_eq!(
            ActionType::Custom("my_action".to_string()).to_string(),
            "my_action"
        );
    }

    #[test]
    fn test_action_trigger_classification() {
        assert!(ActionTrigger::Manual.is_manual());
        assert!(!ActionTrigger::Manual.is_automatic());

        assert!(!ActionTrigger::OnComplete.is_manual());
        assert!(ActionTrigger::OnComplete.is_automatic());

        assert!(ActionTrigger::Scheduled {
            cron: "0 0 * * *".to_string()
        }
        .is_automatic());
    }

    #[test]
    fn test_task_action_builder() {
        let action = TaskAction::new(
            "action1".to_string(),
            ActionType::Gitops,
            ActionTrigger::OnComplete,
            serde_json::json!({}),
        )
        .with_description("Deploy to production".to_string())
        .with_retry(3)
        .with_timeout(300);

        assert_eq!(action.id, "action1");
        assert_eq!(action.action_type, ActionType::Gitops);
        assert_eq!(action.description, Some("Deploy to production".to_string()));
        assert_eq!(action.retry_count, 3);
        assert_eq!(action.timeout_secs, Some(300));
        assert!(action.enabled);
    }

    #[test]
    fn test_action_result() {
        let result = ActionResult::success(
            "action1".to_string(),
            "Deployment succeeded".to_string(),
            1500,
        )
        .with_output(serde_json::json!({"deployment_id": "abc123"}));

        assert!(result.success);
        assert_eq!(result.action_id, "action1");
        assert_eq!(result.duration_ms, 1500);
        assert!(result.error.is_none());
        assert!(result.output.is_some());
    }

    #[test]
    fn test_action_result_failure() {
        let result = ActionResult::failure(
            "action1".to_string(),
            "Deployment failed".to_string(),
            "Connection timeout".to_string(),
            2000,
        );

        assert!(!result.success);
        assert_eq!(result.error, Some("Connection timeout".to_string()));
    }
}
