// r8r: rust port of n8n.io
// Initial author: Michael Cochran <mcochran@cochranblock.org>
// Built for: Tanner Woodrum, intended cofounder
// License: Unlicense (public domain dedication, see UNLICENSE)

use chrono::{DateTime, Utc};
use crate::core::types::*;
use std::collections::{HashMap, VecDeque};

#[derive(Debug, Clone)]
pub struct ExecuteFrame {
    pub node: NodeName,
    pub input: Vec<NodeItem>,
    pub run_index: RunIndex,
}

#[derive(Debug, Clone)]
pub struct TaskRun {
    pub start_time: DateTime<Utc>,
    pub execution_time_ms: u64,
    pub status: TaskStatus,
    pub items: Vec<NodeItem>,
    pub error: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TaskStatus {
    Success,
    Error,
    Waiting,
}

#[derive(Default)]
pub struct WaitingState {
    pub by_input: HashMap<u8, Vec<NodeItem>>,
    pub expected_inputs: u8,
}

impl WaitingState {
    pub fn is_complete(&self) -> bool {
        self.by_input.len() as u8 >= self.expected_inputs
    }

    pub fn drain(&mut self) -> Vec<NodeItem> {
        let mut all = Vec::new();
        let mut keys: Vec<_> = self.by_input.keys().copied().collect();
        keys.sort();
        for k in keys {
            if let Some(items) = self.by_input.remove(&k) {
                all.extend(items);
            }
        }
        all
    }
}

pub struct RunState {
    pub stack: VecDeque<ExecuteFrame>,
    pub waiting: HashMap<(NodeName, RunIndex), WaitingState>,
    pub run_data: HashMap<NodeName, Vec<TaskRun>>,
    pub run_items: HashMap<NodeName, Vec<NodeItem>>,
    pub last_node_executed: Option<NodeName>,
    pub error: Option<String>,
    pub started_at: DateTime<Utc>,
}

impl Default for RunState {
    fn default() -> Self { Self::new() }
}

impl RunState {
    pub fn new() -> Self {
        Self {
            stack: VecDeque::new(),
            waiting: HashMap::new(),
            run_data: HashMap::new(),
            run_items: HashMap::new(),
            last_node_executed: None,
            error: None,
            started_at: Utc::now(),
        }
    }

    pub fn record(&mut self, node: &str, task: TaskRun) {
        self.run_items.insert(node.to_string(), task.items.clone());
        self.run_data.entry(node.to_string()).or_default().push(task);
        self.last_node_executed = Some(node.to_string());
    }
}
