use rand::RngExt;
use std::{io, time::Duration};

use crossterm::{
    event::{self, Event, KeyCode, KeyEventKind},
    execute,
    terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};

use ratatui::{
    prelude::*,
    widgets::{Block, BorderType, Borders, List, ListItem, Paragraph, Tabs},
};

use crate::{
    Ngn,
    db::{Issue, IssueStatus, Todo, TodoStatus},
    utils::helpers,
};

pub fn app(core: Ngn) -> Result<(), io::Error> {
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen)?;

    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    let app = App::new(core);

    let result = run_app(&mut terminal, app);

    disable_raw_mode()?;
    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
    terminal.show_cursor()?;

    result
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Tab {
    Issues,
    Todo,
    Settings,
}

impl Tab {
    fn titles() -> [&'static str; 3] {
        ["Issues", "Todo", "Settings"]
    }

    fn next(self) -> Self {
        match self {
            Self::Issues => Self::Todo,
            Self::Todo => Self::Settings,
            Self::Settings => Self::Issues,
        }
    }

    fn previous(self) -> Self {
        match self {
            Self::Issues => Self::Settings,
            Self::Todo => Self::Issues,
            Self::Settings => Self::Todo,
        }
    }

    fn index(self) -> usize {
        match self {
            Self::Issues => 0,
            Self::Todo => 1,
            Self::Settings => 2,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Sort {
    Status,
    Id,
    Date,
}

impl Sort {
    fn titles() -> [&'static str; 3] {
        ["status", "id", "date"]
    }

    fn cycle(self) -> Self {
        match self {
            Self::Status => Self::Id,
            Self::Id => Self::Date,
            Self::Date => Self::Status,
        }
    }

    fn index(self) -> usize {
        match self {
            Self::Status => 0,
            Self::Id => 1,
            Self::Date => 2,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FilterTodoStatus {
    All,
    NotDone,
    Done,
}

impl FilterTodoStatus {
    fn cycle(self) -> Self {
        match self {
            Self::All => Self::NotDone,
            Self::NotDone => Self::Done,
            Self::Done => Self::All,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FilterIssueStatus {
    Open,
    InProgress,
    Closed,
    All,
}

impl FilterIssueStatus {
    fn cycle(self) -> Self {
        match self {
            Self::Open => Self::InProgress,
            Self::InProgress => Self::Closed,
            Self::Closed => Self::All,
            Self::All => Self::Open,
        }
    }
}

/// UI-only state
#[derive(Debug)]
struct UiState {
    active_tab: Tab,
    active_sort: Sort,
    active_filter_issue_status: FilterIssueStatus,
    active_filter_todo_status: FilterTodoStatus,
    should_quit: bool,
}

impl UiState {
    fn new() -> Self {
        Self {
            active_sort: Sort::Status,
            active_tab: Tab::Issues,
            active_filter_issue_status: FilterIssueStatus::All,
            active_filter_todo_status: FilterTodoStatus::All,
            should_quit: false,
        }
    }
}

/// App = core backend + gui frontend
struct App {
    core: Ngn,
    ui: UiState,
}

impl App {
    fn new(core: Ngn) -> Self {
        Self {
            core,
            ui: UiState::new(),
        }
    }

    fn on_key(&mut self, key: KeyCode) {
        match key {
            KeyCode::Char('q') => self.ui.should_quit = true,

            // tab navigation
            KeyCode::Right | KeyCode::Char('l') => {
                self.ui.active_tab = self.ui.active_tab.next();
            }
            KeyCode::Left | KeyCode::Char('h') => {
                self.ui.active_tab = self.ui.active_tab.previous();
            }

            KeyCode::Char('s') => {
                self.ui.active_sort = self.ui.active_sort.cycle();
            }

            // per-tab actions
            _ => match self.ui.active_tab {
                Tab::Issues => self.handle_issues_key(key),
                Tab::Todo => self.handle_todo_key(key),
                Tab::Settings => self.handle_settings_key(key),
            },
        }
    }

    fn handle_issues_key(&mut self, key: KeyCode) {
        match key {
            KeyCode::Char('a') => {
                let mut rng = rand::rng();
                let rand_num = rng.random_range(1..9);
                let rand_bool = rng.random_bool(1.0 / 2.0);
                let mut status = IssueStatus::Closed;
                if !rand_bool {
                    status = IssueStatus::InProgress
                }

                let mut i = Issue {
                    id: None,
                    descr: String::from("fix y bug in another function"),
                    tag: String::from("fix"),
                    file_name: String::from("main.rs"),
                    status,
                    priority: rand_num,
                    date: helpers::get_current_date(),
                };
                i.insert(&self.core.db.conn).unwrap();

                self.core.db.collect_issues(self.ui.active_sort).unwrap();
            }
            KeyCode::Char('f') => {
                self.ui.active_filter_issue_status =
                    self.ui.active_filter_issue_status.cycle();
            }
            _ => {}
        }
    }

    fn handle_todo_key(&mut self, key: KeyCode) {
        match key {
            KeyCode::Char('a') => {
                let mut rng = rand::rng();
                let rand_bool = rng.random_bool(1.0 / 2.0);
                let mut status = TodoStatus::Done;
                if !rand_bool {
                    status = TodoStatus::Done
                }
                let mut t = Todo {
                    id: None,
                    descr: String::from("do the laundry"),
                    status,
                    date: helpers::get_current_date(),
                };
                t.insert(&self.core.db.conn).unwrap();

                self.core.db.collect_todos(self.ui.active_sort).unwrap();
            }
            KeyCode::Char('f') => {
                self.ui.active_filter_todo_status =
                    self.ui.active_filter_todo_status.cycle();
            }
            _ => {}
        }
    }

    fn handle_settings_key(&mut self, key: KeyCode) {
        match key {
            // KeyCode::Char('d') => {
            //     self.backend.toggle_dark_mode();
            // }
            _ => {}
        }
    }
}

fn run_app(
    terminal: &mut Terminal<CrosstermBackend<std::io::Stdout>>,
    mut app: App,
) -> io::Result<()> {
    loop {
        terminal.draw(|frame| draw(frame, &mut app))?;

        if app.ui.should_quit {
            return Ok(());
        }

        if event::poll(Duration::from_millis(100))? {
            if let Event::Key(key) = event::read()? {
                if key.kind == KeyEventKind::Press {
                    app.on_key(key.code);
                }
            }
        }
    }
}

fn draw(frame: &mut Frame, app: &mut App) {
    let areas = Layout::vertical([
        Constraint::Length(3),
        Constraint::Length(3),
        Constraint::Min(0),
        Constraint::Length(2),
    ])
        .split(frame.area());

    let sort_and_filter = Layout::horizontal([Constraint::Percentage(75), Constraint::Min(0)])
        .split(areas[1]);
    draw_sort(frame, sort_and_filter[0], app.ui.active_sort);

    draw_tabs(frame, areas[0], app.ui.active_tab);

    // *brakoll - d: map filters in gui to collect_issues and collect_todos in db, p: 0, t: feature, s: closed
    match app.ui.active_sort {
        Sort::Status => {
            app.core.db.collect_todos(Sort::Status).unwrap();
            app.core.db.collect_issues(Sort::Status).unwrap();
        }
        Sort::Id => {
            app.core.db.collect_todos(Sort::Id).unwrap();
            app.core.db.collect_issues(Sort::Id).unwrap();
        }
        Sort::Date => {
            app.core.db.collect_todos(Sort::Date).unwrap();
            app.core.db.collect_issues(Sort::Date).unwrap();
        }
    }

    draw_filter(
        frame,
        sort_and_filter[1],
        app.ui.active_filter_issue_status,
        app.ui.active_filter_todo_status,
        app.ui.active_tab,
    );

    match app.ui.active_tab {
        Tab::Issues => match app.ui.active_filter_issue_status {
            FilterIssueStatus::All => {}
            FilterIssueStatus::Open => {}
            FilterIssueStatus::Closed => {}
            FilterIssueStatus::InProgress => {}
        },
        Tab::Todo => match app.ui.active_filter_todo_status {
            FilterTodoStatus::All => {}
            FilterTodoStatus::Done => {}
            FilterTodoStatus::NotDone => {}
        },
        _ => {}
    }

    match app.ui.active_tab {
        Tab::Issues => draw_issues_tab(frame, areas[2], &app.core),
        Tab::Todo => draw_todo_tab(frame, areas[2], &app.core),
        Tab::Settings => draw_settings_tab(frame, areas[2], &app.core),
    }

    frame.render_widget(help_text(), areas[3]);
}

fn help_text() -> Paragraph<'static> {
    let icon_style = Style::default().fg(Color::Blue);

    Paragraph::new(vec![
        Line::from(vec![
            Span::styled("←/→", icon_style),
            Span::raw(" tabs • "),
            Span::styled("↑/↓", icon_style),
            Span::raw(" rows • "),
            Span::styled("q", icon_style),
            Span::raw(" quit • "),
            Span::styled("a", icon_style),
            Span::raw(" add"),
        ]),
        Line::from(vec![
            Span::styled("u", icon_style),
            Span::raw(" update • "),
            Span::styled("r", icon_style),
            Span::raw(" remove • "),
            Span::styled("s", icon_style),
            Span::raw(" sort • "),
            Span::styled("f", icon_style),
            Span::raw(" filter"),
        ]),
        ])
            .block(Block::default())
            .centered()
}

fn draw_filter(
    frame: &mut Frame,
    area: Rect,
    f_issue_stat: FilterIssueStatus,
    f_todo_stat: FilterTodoStatus,
    active_tab: Tab,
) {
    match active_tab {
        Tab::Issues => {
            let text = match f_issue_stat {
                FilterIssueStatus::All => "󰈳 All",
                FilterIssueStatus::Open => "󰈳 Open",
                FilterIssueStatus::Closed => "󰈳 Closed",
                FilterIssueStatus::InProgress => "󰈳 In Prog.",
            };
            let text = format!(" {}", text);

            let text = Paragraph::new(text).block(Block::default()
                .border_type(BorderType::Rounded)
                .borders(Borders::ALL));

            frame.render_widget(text, area);
        }

        Tab::Todo => {
            let text = match f_todo_stat {
                FilterTodoStatus::All => "󰈳 All",
                FilterTodoStatus::NotDone => "󰈳 Todo",
                FilterTodoStatus::Done => "󰈳 Done",
            };
            let text = format!(" {}", text);

            let text = Paragraph::new(text).block(Block::default()
                .border_type(BorderType::Rounded)
                .borders(Borders::ALL));

            frame.render_widget(text, area);
        }
        _ => {
            let text = Paragraph::new("").block(Block::default()
                .border_type(BorderType::Rounded)
                .borders(Borders::ALL));
                frame.render_widget(text, area);
        }
    }
}

fn draw_sort(frame: &mut Frame, area: Rect, active_sort: Sort) {
    let titles = Sort::titles()
        .into_iter()
        .map(Line::from)
        .collect::<Vec<_>>();

    let sort = Tabs::new(titles)
        .select(active_sort.index())
        .block(Block::default()
            .border_type(BorderType::Rounded)
            .borders(Borders::ALL)
            .title("Sort"))
        .highlight_style(
            Style::default().add_modifier(Modifier::BOLD | Modifier::REVERSED),
        );

    frame.render_widget(sort, area);
}

fn draw_tabs(frame: &mut Frame, area: Rect, active_tab: Tab) {
    let titles = Tab::titles()
        .into_iter()
        .map(Line::from)
        .collect::<Vec<_>>();

    let tabs = Tabs::new(titles)
        .select(active_tab.index())
        .block(Block::default()
            .border_type(BorderType::Rounded)
            .borders(Borders::ALL)
            .title(" Tabs "))
        .highlight_style(
            Style::default().add_modifier(Modifier::BOLD | Modifier::REVERSED),
        );

    frame.render_widget(tabs, area);
}

fn draw_issues_tab(frame: &mut Frame, area: Rect, core: &Ngn) {
    let items =
        core.db.issues
        .iter()
        .map(|issue| {
            ListItem::new(format!(
                    "\n{id:<4}| {da} - {ta:}({fi}): P{pr} {st} \n{de}",
                    id = format! {"#{}",issue.id.unwrap()},
                    ta = issue.tag,
                    de = issue.descr,
                    fi = issue.file_name,
                    st = issue.status,
                    pr = issue.priority,
                    da = issue.date
            ))
        })
    .collect::<Vec<_>>();

    let list = List::new(items).block(Block::default()
        .border_type(BorderType::Rounded)
        .borders(Borders::ALL));

    frame.render_widget(list, area);
}

fn draw_todo_tab(frame: &mut Frame, area: Rect, core: &Ngn) {
    let items =
        core.db.todos
        .iter()
        .map(|todo| {
            let ma = match todo.status {
                TodoStatus::NotDone => "󰄱",
                TodoStatus::Done => "󰄵",
            };
            ListItem::new(format!(
                    "\n{id:<4} {da} {ma}\n{de}",
                    id = format! {"#{}",todo.id.unwrap()},
                    de = todo.descr,
                    da = todo.date
            ))
        })
    .collect::<Vec<_>>();

    let list = List::new(items).block(Block::default()
        .border_type(BorderType::Rounded)
        .borders(Borders::ALL));

    frame.render_widget(list, area);
}

fn draw_settings_tab(frame: &mut Frame, area: Rect, _: &Ngn) {
    let text = format!("Clear all todo items \nClear all issue items");

    let paragraph = Paragraph::new(text).block(Block::default()
        .border_type(BorderType::Rounded)
        .borders(Borders::ALL));

    frame.render_widget(paragraph, area);
}