#![feature(io_error_more)]

use std::{
    fs, io::{self, stdin, stdout, Write},
    path::{Path, PathBuf},
    process::ExitCode,
};

use clap::Parser;

#[derive(Parser)]
struct Args {
    #[arg(short)]
    recurse: bool,
    #[arg(short)]
    verbose: bool,
    #[arg(short)]
    force: bool,
    #[clap(required = true)]
    paths: Vec<PathBuf>,
}

fn read_answer() -> Result<bool, io::Error> {
    let stdin = stdin();

    for _ in 0..3 {
        let mut buffer = String::new();
        let len = stdin.read_line(&mut buffer)?;
        if len == 0 {
            return Ok(false);
        }

        match buffer.as_str().trim() {
            "y" | "Y" | "" => return Ok(true),
            "n" | "N" => return Ok(false),
            _ => {
                eprintln!("Please answer `y` (default) or `n`");
            }
        }

    }

    Ok(false)
}

fn rm<P: AsRef<Path>>(path: P, args: &Args) -> bool {
    let path = path.as_ref();

    if !args.force {
        print!("Remove {} [Y/n]? ", path.display());
        stdout().flush().ok();
        match read_answer() {
            Ok(true) => (),
            Ok(false) => return true,
            Err(err) => {
                eprintln!("stdin: {err}");
                return false;
            }
        }
    }

    if args.verbose {
        println!("remove {}", path.display());
    }

    match fs::remove_file(path) {
        Err(e) if args.recurse && e.kind() == io::ErrorKind::IsADirectory => {
            let readdir = match fs::read_dir(path) {
                Ok(dir) => dir,
                Err(error) => {
                    eprintln!("{}: {}", path.display(), error);
                    return false;
                }
            };

            let mut result = true;

            for entry in readdir {
                let entry = match entry {
                    Ok(entry) => entry,
                    Err(error) => {
                        eprintln!("{}/: {}", path.display(), error);
                        result = false;
                        continue;
                    }
                };

                if entry.file_name() == ".." || entry.file_name() == "." {
                    continue;
                }

                result &= rm(entry.path(), args);
            }

            if result {
                if let Err(error) = fs::remove_dir(path) {
                    eprintln!("{}: {}", path.display(), error);
                    result = false;
                }
            }

            result
        }
        Err(e) => {
            eprintln!("{}: {}", path.display(), e);
            false
        }
        Ok(_) => true,
    }
}

fn main() -> ExitCode {
    let args = Args::parse();
    let mut result = ExitCode::SUCCESS;

    for arg in args.paths.iter() {
        if !rm(arg, &args) {
            result = ExitCode::FAILURE;
        }
    }

    result
}
