use std::{io, rc::Rc, sync::RwLock};

use anyhow::Result;
use editor::ACLEditor;
use ratatui::{
    buffer::Buffer,
    crossterm::event::{self, Event, KeyCode},
    layout::{Constraint, Direction, Layout, Rect},
    widgets::Widget,
    DefaultTerminal, Frame,
};
use tree::Tree;

mod editor;
mod tree;

pub struct App {
    exit: bool,
    tree: Tree,
    editor: Option<ACLEditor>,
}

impl Default for App {
    fn default() -> Self {
        let initial_path = if let Some(path) = std::env::args().nth(1) {
            std::path::PathBuf::from(path)
        } else {
            std::env::current_dir().unwrap()
        };

        Self {
            exit: false,
            tree: Tree::new(initial_path),
            editor: None,
        }
    }
}

impl App {
    pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
        let mut app_event = AppEventHost::default();
        while !self.exit {
            terminal.draw(|frame| self.render(frame, app_event))?;
            app_event = self.handle_events()?;
        }
        Ok(())
    }

    pub fn handle_events(&mut self) -> io::Result<AppEventHost> {
        // Handle events here
        // For example, you can check for key presses and update the state accordingly

        let evt = event::read()?;
        if let Event::Key(key) = evt {
            match key.code {
                KeyCode::Char('q') => {
                    self.exit = true;
                }
                _ => {}
            }
        }

        Ok(AppEventHost::new(Some(evt)))
    }

    pub fn render(&mut self, frame: &mut Frame, event: AppEventHost) {
        if let Some(evt) = &event.event {
            match evt {
                Event::Key(key) => match key.code {
                    KeyCode::Char('q') => {
                        self.exit = true;
                    }
                    KeyCode::Enter | KeyCode::Char('e') => {
                        self.editor = Some(ACLEditor::new(self.tree.get_selected()));
                    }
                    KeyCode::Esc | KeyCode::Char('c') => {
                        self.editor = None;
                    }
                    _ => {}
                },
                _ => {}
            }
        }

        event.get_client(true).register_helper("q", "Quit");

        let layout1 = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Fill(1), Constraint::Length(1)])
            .split(frame.area());

        let content_area = layout1[0];
        let help_area = layout1[1];

        let layout2 = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Percentage(30), Constraint::Fill(1)])
            .split(content_area);

        let content_left_area = layout2[0];
        let content_right_area = layout2[1];

        self.tree.render(
            frame,
            content_left_area,
            event.get_client(self.editor.is_none()),
        );

        if let Some(editor) = self.editor.as_mut() {
            event
                .get_client(true)
                .register_helper("Esc", "Close editor");
            editor.render(frame, content_right_area, event.get_client(true));
        } else {
            event.get_client(true).register_helper("Enter", "Select");
            frame.render_widget("Select folder from the left", content_right_area);
        }

        event.render_help(frame, help_area);
    }
}

pub struct AppEventHost {
    pub event: Option<Event>,
    pub registered_helper: Rc<RwLock<Vec<AppEventHelper>>>,
}

impl Default for AppEventHost {
    fn default() -> Self {
        Self {
            event: None,
            registered_helper: Rc::from(RwLock::new(vec![])),
        }
    }
}

impl AppEventHost {
    pub fn new(event: Option<Event>) -> Self {
        Self {
            event: event,
            ..Default::default()
        }
    }

    fn render_help(&self, frame: &mut Frame, area: Rect) {
        let mut help = String::new();
        let infos = self.registered_helper.read().unwrap();
        for info in infos.iter() {
            help.push_str(&format!("{}: {} | \n", info.key, info.help));
        }
        frame.render_widget(help, area);
    }

    fn get_client(&self, focus: bool) -> AppEventClient {
        AppEventClient {
            host: self.registered_helper.clone(),
            event: self.event.clone(),
            has_focus: focus,
        }
    }
}

pub struct AppEventClient {
    host: Rc<RwLock<Vec<AppEventHelper>>>,
    event: Option<Event>,
    pub has_focus: bool,
}

impl AppEventClient {
    pub fn has_focus(&self) -> bool {
        self.has_focus
    }

    pub fn get_client(&self, focus: bool) -> Self {
        Self {
            host: self.host.clone(),
            event: self.event.clone(),
            has_focus: focus,
        }
    }

    pub fn register_helper(&mut self, key: &str, help: &str) {
        self.host.write().unwrap().push(AppEventHelper {
            key: key.to_string(),
            help: help.to_string(),
        });
    }
}

pub struct AppEventHelper {
    pub key: String,
    pub help: String,
}

// impl eframe::App for App {
//     fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
//         egui::SidePanel::right("side_panel")
//             .min_width(300.0)
//             .max_width(ctx.available_rect().width() - 100.0)
//             .resizable(true)
//             .show(ctx, |ui| {
//                 self.editor.show(ui, &self.tree.selected);
//             });

//         egui::CentralPanel::default().show(ctx, |ui| {
//             egui::ScrollArea::both()
//                 .auto_shrink([false; 2])
//                 .show(ui, |ui| {
//                     self.tree.show(ui);
//                 });
//         });
//     }
// }

pub trait Component {
    fn render(&mut self, f: &mut Frame, rect: Rect, evt: AppEventClient);
}
