#![feature(yggdrasil_os, rustc_private)]

use std::{
    io::{self, BufRead, Stdout, Write, stdin, stdout},
    os::{fd::AsRawFd, yggdrasil::io::terminal::start_terminal_session},
    path::PathBuf,
    process::{Command, ExitCode},
    thread,
    time::Duration,
};

use clap::Parser;
use cross::io::{TerminalOptions, TerminalOptionsImpl};
use runtime::rt::io::terminal;

#[derive(Debug, Parser)]
struct Args {
    #[clap(
        short,
        long,
        help = "Serial terminal baud rate",
        default_value_t = 115200
    )]
    baud: u32,
    #[clap(
        short = 'N',
        long,
        help = "Convert NL to CR+NL",
        default_value_t = true
    )]
    nl_to_crnl: bool,

    terminal: PathBuf,
}

fn login_readline<R: BufRead>(
    reader: &mut R,
    buf: &mut String,
    _secret: bool,
) -> Result<usize, io::Error> {
    reader.read_line(buf)
}

fn login_as_inner(username: &str, _password: &str) -> Result<(), io::Error> {
    let mut shell = Command::new("/bin/sh").arg("-l").spawn()?;
    println!("Hello {:?}", username);
    shell.wait()?;
    Ok(())
}

fn login_as(
    stdout: Stdout,
    options: &TerminalOptionsImpl,
    username: &str,
    password: &str,
) -> Result<(), io::Error> {
    let old_termios = terminal::update_terminal_options(stdout.as_raw_fd(), |_| *options)?;
    let result = login_as_inner(username, password);
    terminal::set_terminal_options(stdout.as_raw_fd(), &old_termios)?;
    result
}

fn login_attempt(erase: bool, options: &TerminalOptionsImpl) -> Result<(), io::Error> {
    let stdin = stdin();
    let mut stdout = stdout();

    if erase {
        print!("\x1b[1;1f\x1b[2J");
        stdout.flush().ok();
    }

    let mut username = String::new();

    print!("Username: ");
    stdout.flush().ok();
    if login_readline(&mut stdin.lock(), &mut username, false)? == 0 {
        return Ok(());
    }

    login_as(stdout, options, username.trim(), "")
}

fn main() -> ExitCode {
    logsink::setup_logging(false);
    let args = Args::parse();
    // let args: Vec<_> = env::args().skip(1).collect();
    // if args.len() != 1 {
    //     eprintln!("Usage: /sbin/login TTY");
    //     return ExitCode::FAILURE;
    // }

    // TODO check that `terminal` is a terminal
    log::info!("Starting a session at {}", args.terminal.display());
    let mut success = false;
    for _ in 0..3 {
        if let Err(err) = unsafe { start_terminal_session(&args.terminal) } {
            log::error!("{}: {:?}", args.terminal.display(), err);
            eprintln!(
                "Could not setup a session at {}: {:?}",
                args.terminal.display(),
                err
            );
        } else {
            success = true;
            break;
        }

        thread::sleep(Duration::from_secs(1));
    }
    if !success {
        return ExitCode::FAILURE;
    }

    let mut terminal_options = TerminalOptionsImpl::normal();
    terminal_options.set_nl_to_crnl(args.nl_to_crnl);

    let mut attempt_number = 0;
    loop {
        log::debug!("Login attempt {}", attempt_number);

        // "Attach" the terminal
        let group_id = std::os::yggdrasil::process::group_id();
        terminal::set_terminal_group(stdin().as_raw_fd(), group_id)
            .expect("Could not set terminal group id");

        if let Err(err) = login_attempt(attempt_number % 3 == 0, &terminal_options) {
            eprintln!("login: {}", err);
        }
        attempt_number += 1;
    }
}
