#![windows_subsystem = "windows"]

use itertools::Itertools;
use log::{error, info};
use semver::Version;
use simplelog::*;
use std::collections::BTreeMap;
use std::env;
use std::ffi::OsString;
use std::fs;
use std::fs::File;
use std::option::Option;
use std::path::{Path, PathBuf};

fn main() {
    if env::var("APPSTUB_DEBUG").is_ok() {
        CombinedLogger::init(vec![
            TermLogger::new(LevelFilter::Error, Config::default(), TerminalMode::Mixed),
            WriteLogger::new(
                LevelFilter::Info,
                Config::default(),
                File::create("app-stub.log").unwrap(),
            ),
        ])
        .unwrap();
    }

    info!("Search for folders");
    let app_folders = get_all_app_folders();
    if let Some(executed_version) = execute_application(&app_folders) {
        delete_old_versions(&app_folders, &executed_version);
    }
}

fn execute_application(app_folders: &BTreeMap<Version, PathBuf>) -> Option<&Version> {
    if let Some(executable_name) = get_executable_name() {
        info!("Executable name: {:?}", executable_name);
        if let Some(programm_to_start) = app_folders.iter().next_back() {
            let folder = programm_to_start.1;
            let mut path: PathBuf = PathBuf::from(folder);
            path.push(Path::new(&executable_name));
            info!("Path to start: {:?}", path);
            let arguments = env::args().skip(1).format(" ");
            info!("Arguments: {:?}", arguments);
            let process = std::process::Command::new(path.as_os_str())
                .args(env::args().skip(1))
                .current_dir(folder)
                .spawn();
            match process {
                Ok(out) => info!("{:?}", out),
                Err(error) => error!("{:?}", error),
            }
            return Some(programm_to_start.0);
        }
    }
    return None;
}

fn delete_old_versions(app_folders: &BTreeMap<Version, PathBuf>, executed_version: &Version) {
    for app_folder in app_folders {
        if app_folder.0 != executed_version {
            info!("Foler to remove: {:?}", app_folder.1);
            let remove_dir_all = std::fs::remove_dir_all(&app_folder.1);
            match remove_dir_all {
                Ok(out) => info!("{:?}", out),
                Err(error) => error!("{:?}", error),
            }
        }
    }
}

fn get_semantic_version(path: PathBuf) -> Option<Version> {
    let last_component = path.components().last();
    let folder_name = last_component?.as_os_str().to_str();
    if folder_name?.to_lowercase().starts_with("app-") {
        let strip_folder_name = folder_name?.get(4..folder_name?.len());
        let parsed_semver = Version::parse(strip_folder_name?);

        if let Ok(parsed_semver) = parsed_semver {
            return Some(parsed_semver);
        }
    }
    return None;
}

fn get_executable_name() -> Option<OsString> {
    if let Ok(current_exe) = env::current_exe() {
        if let Some(file_name) = current_exe.file_name() {
            return Some(file_name.to_os_string());
        }
    }
    return None;
}

fn get_all_app_folders() -> BTreeMap<Version, PathBuf> {
    let mut app_folders: BTreeMap<Version, PathBuf> = BTreeMap::new();
    if let Ok(current_exe) = env::current_exe() {
        info!("Current executable: {:?}", current_exe);
        if let Some(parent_dir) = current_exe.parent() {
            info!("Parent directory: {:?}", parent_dir);
            if let Ok(entries) = fs::read_dir(parent_dir) {
                for entry in entries {
                    if let Ok(entry) = entry {
                        if let Ok(file_type) = entry.file_type() {
                            if file_type.is_dir() {
                                let semantic = get_semantic_version(entry.path());
                                if let Some(semantic) = semantic {
                                    info!("Found folder (Version {:?}): {:?}", semantic, entry);
                                    app_folders.insert(semantic, entry.path());
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    info!("Totally found {:?} folders", app_folders.len());
    return app_folders;
}
