use std::time::Instant;

use crate::threat::state::SessionState;
use crate::types::TraceEntry;

#[derive(Debug, Clone, PartialEq)]
pub enum Filter {
    All,
    Blocks,
    Approvals,
}

pub struct App {
    pub session_id: String,
    pub entries: Vec<TraceEntry>,
    pub filtered_indices: Vec<usize>,
    pub selected: usize,
    pub filter: Filter,
    pub expanded: Option<usize>, // index into filtered_indices
    pub session_state: Option<SessionState>,
    pub tailing: bool,
    pub show_help: bool,
    pub last_update: Instant,
    pub start_time: Instant,
}

impl App {
    pub fn new(session_id: String) -> Self {
        App {
            session_id,
            entries: Vec::new(),
            filtered_indices: Vec::new(),
            selected: 0,
            filter: Filter::All,
            expanded: None,
            session_state: None,
            tailing: true,
            show_help: false,
            last_update: Instant::now(),
            start_time: Instant::now(),
        }
    }

    pub fn update_filtered(&mut self) {
        self.filtered_indices = self
            .entries
            .iter()
            .enumerate()
            .filter(|(_, e)| match self.filter {
                Filter::All => true,
                Filter::Blocks => e.decision == "block",
                Filter::Approvals => e.decision == "approve",
            })
            .map(|(i, _)| i)
            .collect();
    }

    pub fn scroll_up(&mut self) {
        if self.selected > 0 {
            self.selected -= 1;
        }
    }

    pub fn scroll_down(&mut self) {
        let max = self.filtered_indices.len().saturating_sub(1);
        if self.selected < max {
            self.selected += 1;
        }
        if self.selected == max {
            self.tailing = true;
        }
    }

    pub fn jump_to_end(&mut self) {
        self.selected = self.filtered_indices.len().saturating_sub(1);
        self.tailing = true;
    }

    pub fn jump_to_start(&mut self) {
        self.selected = 0;
    }

    pub fn toggle_expand(&mut self) {
        if self.expanded == Some(self.selected) {
            self.expanded = None;
        } else {
            self.expanded = Some(self.selected);
        }
    }

    pub fn cycle_filter(&mut self) {
        self.filter = match self.filter {
            Filter::All => Filter::Blocks,
            Filter::Blocks => Filter::Approvals,
            Filter::Approvals => Filter::All,
        };
        self.update_filtered();
        self.selected = self.filtered_indices.len().saturating_sub(1);
        self.expanded = None;
    }

    pub fn selected_entry(&self) -> Option<&TraceEntry> {
        self.filtered_indices
            .get(self.selected)
            .and_then(|&i| self.entries.get(i))
    }

    pub fn count_by_decision(&self, decision: &str) -> usize {
        self.entries.iter().filter(|e| e.decision == decision).count()
    }
}
