use futures_util::future::BoxFuture;
use futures_util::io::BufReader;
use futures_util::io::Cursor;
use futures_util::FutureExt;
use ssh2::OpenFlags;
use ssh2::Session;
use ssh2::Sftp;
use std::io::prelude::*;
use std::net::TcpStream;
use std::path::Path;
use std::path::PathBuf;
use tauri::Emitter;

use crate::https;

pub fn connect(username: String, password: String) -> Result<Session, String> {
    let tcp = TcpStream::connect("gitea.piwalker.net:22")
        .or(Err(format!("Unable to connect to host")))?;
    let mut sess = Session::new().or(Err(format!("Unable to creat stream")))?;
    sess.set_tcp_stream(tcp);
    sess.handshake().unwrap();
    sess.userauth_password(username.as_str(), password.as_str())
        .or(Err(format!("Invalid username or password")))?;
    Ok(sess)
}

pub async fn uplaod(
    window: Option<tauri::AppHandle>,
    sess: Session,
    path: PathBuf,
    mut reader: impl Read,
    upload_name: String,
    total_size: usize,
) -> Result<(), String> {
    let sftp = sess.sftp().or(Err("unable to open sftp session"))?;
    let mut file = sftp.create(path.as_path()).or(Err("Unable to open file"))?;
    let mut uploaded = 0;
    while true {
        let mut buf = vec![0u8; 1024 * 32];
        let res = reader.read(&mut buf).unwrap();
        if res <= 0 {
            if let Some(window) = window.clone() {
                window
                    .emit("download_finished", true)
                    .or(Err(format!("Unable to signal window!")))?;
            }
            break;
        }
        file.write_all(buf.split_at(res).0).unwrap();
        uploaded += res;
        println!(
            "Uploading {} {}MB / {}MB",
            upload_name,
            uploaded / (1024 * 1024),
            total_size / (1024 * 1024)
        );
        if let Some(window) = window.clone() {
            window
                .emit(
                    "download_progress",
                    https::DownloadStatus {
                        downloaded: uploaded as usize,
                        total: total_size as usize,
                        time_elapsed: 0,
                        download_name: upload_name.clone(),
                    },
                )
                .or(Err(format!("Unable to signal window")))?;
        }
    }
    Ok(())
}

pub async fn mkdir(sess: Session, path: PathBuf) -> Result<(), String> {
    let sftp = sess.sftp().or(Err("Unable to open sftp session"))?;
    sftp.mkdir(path.as_path(), 0o775)
        .or(Err(format!("Unable to create directory")))?;
    Ok(())
}

pub fn rmdir(sess: Session, path: PathBuf) -> BoxFuture<'static, ()> {
    async move {
        let sftp = sess.sftp().or(Err("Unable to open sftp session")).unwrap();
        let dirs = sftp
            .readdir(path.as_path())
            .or(Err("unable to stat directory"))
            .unwrap();
        for dir in dirs {
            if dir.1.is_dir() {
                rmdir(sess.clone(), dir.0).await;
            } else {
                sftp.unlink(dir.0.as_path())
                    .or(Err(format!("Unable to delete file")))
                    .unwrap();
            }
        }
        sftp.rmdir(path.as_path())
            .or(Err(format!("Unable to delete directory")))
            .unwrap();
    }
    .boxed()
}
