//! Command history tracking and management

use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};

/// History entry for tracked actions
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HistoryEntry {
    /// Command or action name
    pub command: String,
    /// Arguments passed to command
    pub args: Vec<String>,
    /// Result: success or error message
    pub result: String,
    /// Whether command succeeded
    pub success: bool,
    /// Timestamp of execution
    pub timestamp: u64,
    /// Execution duration in milliseconds
    pub duration_ms: u64,
}

impl HistoryEntry {
    /// Create successful history entry
    #[must_use]
    pub fn success(command: String, args: Vec<String>, result: String, duration_ms: u64) -> Self {
        Self {
            command,
            args,
            result,
            success: true,
            timestamp: SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_secs(),
            duration_ms,
        }
    }

    /// Create failed history entry
    #[must_use]
    pub fn failure(command: String, args: Vec<String>, error: String, duration_ms: u64) -> Self {
        Self {
            command,
            args,
            result: error,
            success: false,
            timestamp: SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap_or_default()
                .as_secs(),
            duration_ms,
        }
    }

    /// Get display string for history entry
    #[must_use]
    pub fn display(&self) -> String {
        format!(
            "{} {} ({}ms)",
            self.command,
            self.args.join(" "),
            self.duration_ms
        )
    }
}

/// Command history manager
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CommandHistory {
    /// List of history entries
    entries: Vec<HistoryEntry>,
    /// Maximum entries to keep
    max_entries: usize,
    /// Current selected entry index
    selected: usize,
    /// Filter predicate (empty = show all)
    filter: String,
}

impl CommandHistory {
    /// Create new command history with default size (1000 entries)
    #[must_use]
    pub fn new() -> Self {
        Self::with_capacity(1000)
    }

    /// Create with specified capacity
    #[must_use]
    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            entries: Vec::with_capacity(capacity),
            max_entries: capacity,
            selected: 0,
            filter: String::new(),
        }
    }

    /// Add entry to history
    pub fn add_entry(&mut self, entry: HistoryEntry) {
        self.entries.push(entry);

        // Keep history size under limit
        while self.entries.len() > self.max_entries {
            self.entries.remove(0);
        }

        // Update selected index
        if self.selected >= self.entries.len() && !self.entries.is_empty() {
            self.selected = self.entries.len() - 1;
        }
    }

    /// Get all entries (filtered if filter is set)
    #[must_use]
    pub fn entries(&self) -> Vec<&HistoryEntry> {
        if self.filter.is_empty() {
            self.entries.iter().collect()
        } else {
            self.entries
                .iter()
                .filter(|e| {
                    e.command.contains(&self.filter)
                        || e.args.iter().any(|a| a.contains(&self.filter))
                        || e.result.contains(&self.filter)
                })
                .collect()
        }
    }

    /// Get selected entry
    #[must_use]
    pub fn selected_entry(&self) -> Option<&HistoryEntry> {
        self.entries.get(self.selected)
    }

    /// Set filter for entries
    pub fn set_filter(&mut self, filter: String) {
        self.filter = filter;
        self.selected = 0;
    }

    /// Clear filter
    pub fn clear_filter(&mut self) {
        self.filter.clear();
        self.selected = 0;
    }

    /// Select next entry
    pub fn select_next(&mut self) {
        let entries = self.entries();
        if !entries.is_empty() {
            self.selected = (self.selected + 1) % entries.len();
        }
    }

    /// Select previous entry
    pub fn select_previous(&mut self) {
        let entries = self.entries();
        if !entries.is_empty() {
            self.selected = if self.selected == 0 {
                entries.len() - 1
            } else {
                self.selected - 1
            };
        }
    }

    /// Clear all history
    pub fn clear(&mut self) {
        self.entries.clear();
        self.selected = 0;
    }

    /// Get total count
    #[must_use]
    pub const fn count(&self) -> usize {
        self.entries.len()
    }

    /// Get success count
    #[must_use]
    pub fn success_count(&self) -> usize {
        self.entries.iter().filter(|e| e.success).count()
    }

    /// Get failure count
    #[must_use]
    pub fn failure_count(&self) -> usize {
        self.entries.iter().filter(|e| !e.success).count()
    }

    /// Get average execution time
    #[must_use]
    pub fn average_duration_ms(&self) -> u64 {
        if self.entries.is_empty() {
            return 0;
        }
        self.entries.iter().map(|e| e.duration_ms).sum::<u64>() / self.entries.len() as u64
    }

    /// Find entries by command
    #[must_use]
    pub fn find_by_command(&self, command: &str) -> Vec<&HistoryEntry> {
        self.entries
            .iter()
            .filter(|e| e.command == command)
            .collect()
    }
}

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

#[cfg(test)]
mod tests {
    #![allow(
        clippy::cast_sign_loss  // Safe: test loop variable is always positive (0..10)
    )]

    use super::*;

    #[test]
    fn test_history_entry_success() {
        let entry = HistoryEntry::success(
            "list".to_string(),
            vec!["items".to_string()],
            "5 items".to_string(),
            100,
        );

        assert!(entry.success);
        assert_eq!(entry.command, "list");
        assert_eq!(entry.duration_ms, 100);
    }

    #[test]
    fn test_history_entry_failure() {
        let entry = HistoryEntry::failure(
            "delete".to_string(),
            vec!["123".to_string()],
            "Not found".to_string(),
            50,
        );

        assert!(!entry.success);
        assert_eq!(entry.result, "Not found");
    }

    #[test]
    fn test_command_history_new() {
        let history = CommandHistory::new();
        assert_eq!(history.count(), 0);
        assert_eq!(history.max_entries, 1000);
    }

    #[test]
    fn test_add_entry() {
        let mut history = CommandHistory::new();
        let entry = HistoryEntry::success("test".to_string(), vec![], "ok".to_string(), 10);

        history.add_entry(entry);

        assert_eq!(history.count(), 1);
        assert_eq!(history.success_count(), 1);
        assert_eq!(history.failure_count(), 0);
    }

    #[test]
    fn test_add_multiple_entries() {
        let mut history = CommandHistory::new();

        for i in 0..10 {
            let entry =
                HistoryEntry::success(format!("cmd{i}"), vec![], "ok".to_string(), i as u64);
            history.add_entry(entry);
        }

        assert_eq!(history.count(), 10);
    }

    #[test]
    fn test_history_capacity_limit() {
        let mut history = CommandHistory::with_capacity(5);

        for i in 0..10 {
            let entry =
                HistoryEntry::success(format!("cmd{i}"), vec![], "ok".to_string(), i as u64);
            history.add_entry(entry);
        }

        assert_eq!(history.count(), 5);
    }

    #[test]
    fn test_filter_entries() {
        let mut history = CommandHistory::new();
        history.add_entry(HistoryEntry::success(
            "list".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));
        history.add_entry(HistoryEntry::success(
            "create".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));

        history.set_filter("list".to_string());
        assert_eq!(history.entries().len(), 1);
    }

    #[test]
    fn test_clear_filter() {
        let mut history = CommandHistory::new();
        history.add_entry(HistoryEntry::success(
            "test".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));

        history.set_filter("test".to_string());
        history.clear_filter();

        assert_eq!(history.entries().len(), 1);
        assert!(history.filter.is_empty());
    }

    #[test]
    fn test_select_next() {
        let mut history = CommandHistory::new();
        history.add_entry(HistoryEntry::success(
            "cmd1".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));
        history.add_entry(HistoryEntry::success(
            "cmd2".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));

        assert_eq!(history.selected, 0);
        history.select_next();
        assert_eq!(history.selected, 1);
    }

    #[test]
    fn test_success_failure_count() {
        let mut history = CommandHistory::new();
        history.add_entry(HistoryEntry::success(
            "ok1".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));
        history.add_entry(HistoryEntry::failure(
            "fail1".to_string(),
            vec![],
            "error".to_string(),
            10,
        ));
        history.add_entry(HistoryEntry::success(
            "ok2".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));

        assert_eq!(history.success_count(), 2);
        assert_eq!(history.failure_count(), 1);
    }

    #[test]
    fn test_average_duration() {
        let mut history = CommandHistory::new();
        history.add_entry(HistoryEntry::success(
            "cmd1".to_string(),
            vec![],
            "ok".to_string(),
            100,
        ));
        history.add_entry(HistoryEntry::success(
            "cmd2".to_string(),
            vec![],
            "ok".to_string(),
            200,
        ));

        assert_eq!(history.average_duration_ms(), 150);
    }

    #[test]
    fn test_find_by_command() {
        let mut history = CommandHistory::new();
        history.add_entry(HistoryEntry::success(
            "list".to_string(),
            vec![],
            "ok".to_string(),
            10,
        ));
        history.add_entry(HistoryEntry::success(
            "list".to_string(),
            vec![],
            "ok".to_string(),
            20,
        ));
        history.add_entry(HistoryEntry::success(
            "create".to_string(),
            vec![],
            "ok".to_string(),
            30,
        ));

        let list_cmds = history.find_by_command("list");
        assert_eq!(list_cmds.len(), 2);
    }
}
