memplace 0.1.0

command and snippet manager
use crate::storage::Post;
use ratatui::{
    DefaultTerminal, Frame,
    crossterm::event::{self, Event, KeyCode},
    widgets::{Block, Borders, Paragraph},
};
use std::io;

struct App<'a> {
    results: Vec<String>,
    selected_index: usize,
    selected_value: Option<String>,
    exit: bool,
    needle: String,
    raw_posts: &'a [&'a Post],
}

impl<'a> App<'a> {
    fn new(posts: &'a [&'a Post], needle: &str) -> Self {
        let max_cmd_len = posts.iter().map(|p| p.command.len()).max().unwrap_or(10);

        Self {
            raw_posts: posts,

            results: posts
                .iter()
                .map(|p| {
                    format!(
                        "{:<width$} |Desc| {}",
                        p.command,
                        p.comment,
                        width = max_cmd_len
                    )
                })
                .collect(),
            selected_index: 0,
            selected_value: None,
            exit: false,
            needle: format!("Sorted by the distance to the search phrase:{}", needle),
        }
    }
    pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<Option<String>> {
        while !self.exit {
            terminal.draw(|frame| self.draw(frame))?;
            self.handle_events()?;
        }
        Ok(self.selected_value.clone())
    }

    fn draw(&self, frame: &mut Frame) {
        // We manually create a layout string
        let mut list_text = String::new();

        for (i, item) in self.results.iter().enumerate() {
            if i == self.selected_index {
                // Add a ">" pointer for the selected item
                list_text.push_str(&format!("> {}\n", item));
            } else {
                list_text.push_str(&format!("  {}\n", item));
            }
        }
        let p = Paragraph::new(list_text).block(
            Block::default()
                .borders(Borders::ALL)
                .title(self.needle.as_str()),
        );

        frame.render_widget(p, frame.area());
    }

    fn handle_events(&mut self) -> io::Result<()> {
        if let Event::Key(key) = event::read()? {
            match key.code {
                KeyCode::Up => {
                    if self.selected_index > 0 {
                        self.selected_index -= 1;
                    }
                }
                KeyCode::Down => {
                    if self.selected_index < self.results.len() - 1 {
                        self.selected_index += 1;
                    }
                }
                KeyCode::Enter => {
                    self.selected_value = Some(self.raw_posts[self.selected_index].command.clone());
                    self.exit = true; // Close the TUI
                }
                KeyCode::Esc | KeyCode::Char('q') => {
                    self.exit = true;
                }

                KeyCode::Char('k') => {
                    if self.selected_index > 0 {
                        self.selected_index -= 1;
                    }
                }
                KeyCode::Char('j') => {
                    if self.selected_index < self.results.len() - 1 {
                        self.selected_index += 1;
                    }
                }
                _ => {}
            }
        }
        Ok(())
    }
}

pub fn check(matches: &Vec<&Post>, needle: &str) -> color_eyre::Result<()> {
    color_eyre::install()?;
    let mut terminal = ratatui::init();

    let mut app = App::new(matches, needle);
    let result = app.run(&mut terminal)?;

    ratatui::restore();

    if let Some(selection) = result {
        println!("{}", selection);
    } else {
        eprintln!("No item selected.");
    }

    Ok(())
}