mod app;
mod editor;
mod ui;

use std::{env, error::Error, io, time::Duration};

use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyEventKind},
    execute,
};
use gotcha_gitea::Config;

const HELP: &str = "\
Gotcha TUI

Usage: gotcha-tui [--server SERVER]

Keyboard:
  1..5              switch Home, Issues, Repos, PRs, Milestones
  j/k, arrows       move selection
  n/p or ]/[        next/previous page
  Enter             open selected item
  Backspace         go back (edits text inside an editor)
  a/e/c/x/d         add, edit, comment, toggle state, delete
  /, v, b, *        filters, activity filter, branch, repository favorite
  f                 browse files from a repository commit view
  r                 reload
  s                 choose or manage servers
  ,                 edit TUI preferences
  q                 quit

Editors use Tab/Shift-Tab between fields, normal cursor/editing keys, Ctrl-S to
save, and Esc to cancel. Mouse clicks select items, double-click opens them,
and the scroll wheel moves through lists.";

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let requested_server = parse_args()?;
    let config = Config::load()?;
    let mut app = app::App::new(config, requested_server.as_deref()).await?;

    let mut terminal = ratatui::init();
    execute!(io::stdout(), EnableMouseCapture)?;
    let result = run(&mut terminal, &mut app).await;
    execute!(io::stdout(), DisableMouseCapture)?;
    ratatui::restore();
    result?;
    app.persist_selected_server()?;
    Ok(())
}

async fn run(
    terminal: &mut ratatui::DefaultTerminal,
    app: &mut app::App,
) -> Result<(), Box<dyn Error>> {
    while !app.should_quit {
        terminal.draw(|frame| ui::draw(frame, app))?;
        if event::poll(Duration::from_millis(100))? {
            match event::read()? {
                Event::Key(key) if key.kind == KeyEventKind::Press => app.handle_key(key).await,
                Event::Mouse(mouse) => app.handle_mouse(mouse).await,
                Event::Resize(_, _) => {}
                _ => {}
            }
        } else if app.should_auto_reload() {
            app.reload().await;
        }
    }
    Ok(())
}

fn parse_args() -> Result<Option<String>, String> {
    let args: Vec<_> = env::args().skip(1).collect();
    match args.as_slice() {
        [] => Ok(None),
        [help] if matches!(help.as_str(), "-h" | "--help" | "help") => {
            println!("{HELP}");
            std::process::exit(0);
        }
        [option, server] if option == "--server" => Ok(Some(server.clone())),
        _ => Err(format!("invalid arguments\n\n{HELP}")),
    }
}
