use path_clean::{self, PathClean};
use std::path::{Path, PathBuf};
use std::{env, fs, io};

#[derive(PartialEq, Debug)]
pub struct Paths {
    pub links_dir: String,
    pub src_path: String,
    pub dest_path: String,
}

impl Paths {
    pub fn new(links_dir_str: &str, src_path: &str, dest_path: &str) -> Result<Paths, String> {
        let pb_links_dir = PathBuf::from(links_dir_str);
        let pb_src_path = PathBuf::from(src_path);
        let mut pb_dest_path = PathBuf::from(dest_path);

        let pb_links_dir = match fs::canonicalize(&pb_links_dir) {
            Ok(x) => x,
            Err(_) => return Err(format!("links_dir: {:#?} is invalid", pb_links_dir)),
        };

        let pb_src_path = match fs::canonicalize(&pb_src_path) {
            Ok(x) => x,
            Err(_) => return Err(format!("src_path: {:#?} is invalid", pb_src_path)),
        };

        if pb_dest_path.is_relative() {
            pb_dest_path = absolute_path(&pb_dest_path).unwrap();
        }
        if pb_dest_path.exists() {
            if pb_dest_path.is_dir() {
                print!("{:#?}", pb_dest_path);
                print!("{:#?}", pb_src_path);
                pb_dest_path.push(pb_src_path.file_name().unwrap());
            } else {
                return Err(format!(
                    "dest_path: {:#?} is invalid: a file with that name already exists",
                    pb_dest_path
                ));
            }
        } else {
            let mut pb_tmp = pb_dest_path.clone();
            pb_tmp.pop();
            if !pb_tmp.exists() {
                return Err(format!(
                    "dest_path: {:#?} is invalid: path does not exist",
                    pb_dest_path
                ));
            }
        }
        let links_dir = pb_links_dir.into_os_string().into_string().unwrap();
        let src_path  = pb_src_path.into_os_string().into_string().unwrap();
        let dest_path = pb_dest_path.into_os_string().into_string().unwrap();

        Ok(Paths {
            links_dir,
            src_path,
            dest_path,
        })
    }
}

fn absolute_path(path: impl AsRef<Path>) -> io::Result<PathBuf> {
    let path = path.as_ref();
    let absolute_path = if path.is_absolute() {
        path.to_path_buf()
    } else {
        env::current_dir()?.join(path)
    }
    .clean();
    Ok(absolute_path)
}
