use crate::app;
use crate::ui;
use criterion::{criterion_group, criterion_main, Criterion};
use crossterm::execute;
use crossterm::terminal as term;
use std::fs;
use tui::backend::CrosstermBackend;
use tui::Terminal;
use xplr::runner::get_tty;
use xplr::*;

const PWD: &str = "/tmp/xplr_bench";

fn navigation_benchmark(c: &mut Criterion) {
    fs::create_dir_all(PWD).unwrap();
    (1..10000).for_each(|i| {
        fs::File::create(std::path::Path::new(PWD).join(i.to_string()))
            .unwrap();
    });

    let lua = mlua::Lua::new();
    let mut app = app::App::create(PWD.into(), &lua, None, [].into())
        .expect("failed to create app");

    app = app
        .clone()
        .handle_task(app::Task::new(
            app::MsgIn::External(app::ExternalMsg::ChangeDirectory(PWD.into())),
            None,
        ))
        .unwrap();

    c.bench_function("focus next item", |b| {
        b.iter(|| {
            app.clone()
                .handle_task(app::Task::new(
                    app::MsgIn::External(app::ExternalMsg::FocusNext),
                    None,
                ))
                .unwrap()
        })
    });

    c.bench_function("focus previous item", |b| {
        b.iter(|| {
            app.clone()
                .handle_task(app::Task::new(
                    app::MsgIn::External(app::ExternalMsg::FocusPrevious),
                    None,
                ))
                .unwrap()
        })
    });

    c.bench_function("focus first item", |b| {
        b.iter(|| {
            app.clone()
                .handle_task(app::Task::new(
                    app::MsgIn::External(app::ExternalMsg::FocusFirst),
                    None,
                ))
                .unwrap()
        })
    });

    c.bench_function("focus last item", |b| {
        b.iter(|| {
            app.clone()
                .handle_task(app::Task::new(
                    app::MsgIn::External(app::ExternalMsg::FocusLast),
                    None,
                ))
                .unwrap()
        })
    });

    c.bench_function("leave and enter directory", |b| {
        b.iter(|| {
            app.clone()
                .handle_task(app::Task::new(
                    app::MsgIn::External(app::ExternalMsg::Back),
                    None,
                ))
                .unwrap()
                .handle_task(app::Task::new(
                    app::MsgIn::External(app::ExternalMsg::Enter),
                    None,
                ))
                .unwrap()
        })
    });
}

fn draw_benchmark(c: &mut Criterion) {
    fs::create_dir_all(PWD).unwrap();
    (1..10000).for_each(|i| {
        fs::File::create(std::path::Path::new(PWD).join(i.to_string()))
            .unwrap();
    });

    let lua = mlua::Lua::new();
    let mut app = app::App::create(PWD.into(), &lua, None, [].into())
        .expect("failed to create app");

    app = app
        .clone()
        .handle_task(app::Task::new(
            app::MsgIn::External(app::ExternalMsg::ChangeDirectory(PWD.into())),
            None,
        ))
        .unwrap();

    term::enable_raw_mode().unwrap();
    let mut stdout = get_tty().unwrap();
    // let mut stdout = stdout.lock();
    execute!(stdout, term::EnterAlternateScreen).unwrap();
    // let stdout = MouseTerminal::from(stdout);
    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend).unwrap();
    terminal.hide_cursor().unwrap();

    c.bench_function("draw on terminal", |b| {
        b.iter(|| {
            terminal.draw(|f| ui::draw(f, &app, &lua)).unwrap();
        })
    });

    terminal.clear().unwrap();
    terminal.set_cursor(0, 0).unwrap();
    execute!(terminal.backend_mut(), term::LeaveAlternateScreen).unwrap();
    term::disable_raw_mode().unwrap();
    terminal.show_cursor().unwrap();
}

criterion_group!(benches, navigation_benchmark, draw_benchmark);
criterion_main!(benches);
