//! Outbox for side effects
//!
//! The outbox pattern allows the executor to record side effects (like task creation
//! or timer scheduling) without actually performing them. The external orchestrator
//! is responsible for processing the outbox after execution.

use super::types::Val;
use chrono::{DateTime, Utc};
use std::collections::HashMap;

/// A task creation side effect
///
/// Represents a request to create a new task. This is added to the outbox
/// when Task.run() is called, and the external orchestrator will batch-create
/// these tasks.
#[derive(Debug, Clone, PartialEq)]
pub struct TaskCreation {
    /// The unique ID for this task (UUID generated by Task.run)
    pub task_id: String,

    /// The name of the workflow/task to run
    pub task_name: String,

    /// The inputs to pass to the task
    pub inputs: HashMap<String, Val>,
}

impl TaskCreation {
    /// Create a new task creation side effect
    pub fn new(task_id: String, task_name: String, inputs: HashMap<String, Val>) -> Self {
        Self {
            task_id,
            task_name,
            inputs,
        }
    }
}

/// A timer scheduling side effect
///
/// Represents a request to schedule a timer. This is added to the outbox
/// when Timer.delay() is called, and the external orchestrator will create
/// a scheduled_queue entry.
#[derive(Debug, Clone, PartialEq)]
pub struct TimerSchedule {
    /// The absolute time when the timer should fire
    pub fire_at: DateTime<Utc>,
}

impl TimerSchedule {
    /// Create a new timer schedule side effect
    pub fn new(fire_at: DateTime<Utc>) -> Self {
        Self { fire_at }
    }
}

/// A signal request recorded during workflow execution
///
/// Represents a request to wait for a signal. The claim_id uniquely identifies
/// this request for idempotent matching with incoming signals.
#[derive(Debug, Clone, PartialEq)]
pub struct SignalRequest {
    /// Unique identifier linking request to signal
    pub claim_id: String,
    /// The signal channel name
    pub signal_name: String,
    /// Set when matched to an unclaimed signal (during pre-loop or commit)
    pub signal_id: Option<String>,
}

impl SignalRequest {
    /// Create a new signal request
    pub fn new(claim_id: String, signal_name: String) -> Self {
        Self {
            claim_id,
            signal_name,
            signal_id: None,
        }
    }
}

/// Outbox - collection of side effects
#[derive(Debug, Clone, Default)]
pub struct Outbox {
    /// Task creation side effects
    pub tasks: Vec<TaskCreation>,
    /// Timer scheduling side effects
    pub timers: Vec<TimerSchedule>,
    /// Signal request side effects
    pub signals: Vec<SignalRequest>,
}

impl Outbox {
    /// Create a new empty outbox
    pub fn new() -> Self {
        Self {
            tasks: Vec::new(),
            timers: Vec::new(),
            signals: Vec::new(),
        }
    }

    /// Add a task creation side effect
    pub fn push_task(&mut self, task: TaskCreation) {
        self.tasks.push(task);
    }

    /// Add a timer schedule side effect
    pub fn push_timer(&mut self, timer: TimerSchedule) {
        self.timers.push(timer);
    }

    /// Check if a task with the given ID is in the outbox
    pub fn has_task(&self, task_id: &str) -> bool {
        self.tasks.iter().any(|t| t.task_id == task_id)
    }

    /// Add a signal request side effect
    pub fn push_signal(&mut self, signal: SignalRequest) {
        self.signals.push(signal);
    }

    /// Find a signal request by claim_id
    pub fn get_signal(&self, claim_id: &str) -> Option<&SignalRequest> {
        self.signals.iter().find(|s| s.claim_id == claim_id)
    }
}
