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

use crate::{
    discovery::{DiscoveryCandidate, matcher::match_candidate_multi},
    persistence::{markov::Markov, persistable::Persistable},
};

#[derive(Debug, Serialize, Deserialize)]
pub struct HistoryEntry {
    pub path: PathBuf,
    pub visits: usize,
    pub last_visited: u64,
}

#[derive(Debug, Serialize, Deserialize, Default)]
pub struct History {
    pub entries: Vec<HistoryEntry>,
    pub chain: Markov,
}

impl History {
    /// Record a visit in memory.
    pub fn record_visit(&mut self, path: &PathBuf) {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();

        if let Some(entry) = self.entries.iter_mut().find(|e| e.path == *path) {
            entry.visits += 1;
            entry.last_visited = now;
        } else {
            self.entries.push(HistoryEntry {
                path: path.clone(),
                visits: 1,
                last_visited: now,
            });
        }
    }

    /// Collect all candidate entries from history.
    pub fn history_candidates(&self, tokens: &[&str]) -> Vec<DiscoveryCandidate> {
        self.entries
            .iter()
            .filter(|entry| entry.path.is_dir())
            .filter_map(|entry| match_candidate_multi(&entry.path, tokens))
            .collect()
    }

    pub fn load_or_new() -> Self {
        match Self::load() {
            Ok(history) => history,
            Err(e) => {
                if Self::path().exists() {
                    eprintln!("Warning: could not load {}: {e}", Self::FILE);
                }
                let mut new_history = Self::default();
                if let Err(e) = new_history.save() {
                    eprintln!("Error saving {}: {e}", Self::FILE);
                }
                new_history
            }
        }
    }

    pub fn visit_count(&self, path: &PathBuf) -> usize {
        self.entries
            .iter()
            .find(|e| e.path == *path)
            .map(|e| e.visits)
            .unwrap_or(0)
    }

    pub fn seconds_since_last_visit_at(&self, path: &PathBuf, now: u64) -> Option<u64> {
        self.entries
            .iter()
            .find(|e| e.path == *path)
            .map(|e| now.saturating_sub(e.last_visited))
    }

    pub fn seconds_since_last_visit(&self, path: &PathBuf) -> Option<u64> {
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
        self.seconds_since_last_visit_at(path, now)
    }

    pub fn transition_count(&self) -> usize {
        self.chain.transition_count()
    }
}

impl Persistable for History {
    const FILE: &'static str = "history.json";
}
