use std::{
    collections::HashSet,
    fs,
    io::{Read, Write},
    path::PathBuf,
    process::{Command, Stdio},
};

use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};

use crate::keys::get_passwd_key;

fn is_usb(devices: &mut Vec<MyDevices>) {
    let mut i = 0;
    while i < devices.len() {
        if !pipe(&devices[i].serial_number) {
            devices.remove(i);
            continue;
        }
        i += 1;
    }
}

pub fn get_trusted_devices() -> HashSet<String> {
    let mut trusted_devices: HashSet<String> = HashSet::new();
    let serial_number = fs::read_to_string("/etc/etapkilit/trusted_devices.txt");
    match serial_number {
        Ok(lines) => {
            lines.trim().lines().for_each(|line| {
                trusted_devices.insert(line.to_string());
            });
        }
        Err(_) => (),
    }
    trusted_devices
}
fn is_device_trusted(serial_number: &String, trusted_devices: HashSet<String>) -> bool {
    trusted_devices.get(serial_number).is_some()
}

fn add_trusted_device(serial_number: &String) {
    let mut file = fs::OpenOptions::new()
        .write(true)
        .append(true)
        .open("/etc/etapkilit/trusted_devices.txt")
        .unwrap();
    writeln!(file, "{serial_number}");
}

pub fn get_devices() -> Vec<MyDevices> {
    let stdout_reader = String::from_utf8(lsblk_output()).unwrap();
    //let stdout_lines: Vec<String> = stdout_reader.lines().map(|l| l.unwrap()).collect();
    let stdout_lines = stdout_reader.lines();
    let mut devices = Vec::new();
    for l in stdout_lines {
        let device: Vec<&str> = l.split(" ").collect();
        let disk = device[0];
        if disk.len() == 3 {
            let new_device = MyDevices {
                vendor: device[3].to_string(),
                model: device[2].to_string(),
                name: disk.to_string(),
                serial_number: get_serial_number(disk),
                mountpoint: Vec::new(),
            };
            devices.push(new_device);
        } else {
            let last_disk = devices.last_mut().unwrap();
            let mount = device[1];
            if mount.len() > 0 {
                last_disk.mountpoint.push(mount.to_string());
            }
        }
    }
    is_usb(&mut devices);
    devices
}

fn get_serial_number(disk: &str) -> String {
    let mut cmd_ls = Command::new("udevadm")
        .args(["info", "--query=all"])
        .arg(format!("--name={}", disk))
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    let mut cmd_grep = Command::new("grep")
        .arg("ID_SERIAL_SHORT")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    if let Some(ref mut stdout) = cmd_ls.stdout {
        if let Some(ref mut stdin) = cmd_grep.stdin {
            let mut buf: Vec<u8> = Vec::new();
            stdout.read_to_end(&mut buf).unwrap();
            stdin.write_all(&buf).unwrap();
        }
    }

    let res = cmd_grep.wait_with_output().unwrap().stdout;
    let res = String::from_utf8(res).unwrap();
    let res = res.trim().split("=").collect::<Vec<&str>>();
    //println!("{res:?}");
    res.get(1).unwrap_or(&"").to_string()
}
fn lsblk_output() -> Vec<u8> {
    let a = Command::new("lsblk")
        .arg("-o")
        .arg("name,mountpoInt,model,vendor")
        .args(["-n", "-r", "-i"])
        .stdout(Stdio::piped())
        .output()
        .unwrap();
    a.stdout
}

fn pipe(s: &String) -> bool {
    let mut cmd_ls = Command::new("usb-devices")
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    let mut cmd_grep = Command::new("grep")
        .arg(s)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    if let Some(ref mut stdout) = cmd_ls.stdout {
        if let Some(ref mut stdin) = cmd_grep.stdin {
            let mut buf: Vec<u8> = Vec::new();
            stdout.read_to_end(&mut buf).unwrap();
            stdin.write_all(&buf).unwrap();
        }
    }

    let res = cmd_grep.wait_with_output().unwrap().stdout;
    let res = String::from_utf8(res).unwrap();
    res.trim().len() != 0
}

pub fn list_devices(devices: &Vec<MyDevices>) {
    for d in devices.iter().enumerate() {
        d.1.list_device(d.0 + 1);
    }
}

#[derive(Debug, Clone)]
pub struct MyDevices {
    vendor: String,
    model: String,
    name: String,
    serial_number: String,
    mountpoint: Vec<String>,
}

impl MyDevices {
    pub fn is_mounted(&self) -> bool {
        self.mountpoint.len() > 0
    }
    pub fn write_pub_key(&self, key: &RsaPrivateKey) {
        let pub_key = RsaPublicKey::from(key);
        let serial_number = pub_key.encrypt(
            &mut rand::thread_rng(),
            Pkcs1v15Encrypt,
            &self.serial_number.as_bytes(),
        );
        let mut path = PathBuf::new();
        path.push(self.mountpoint[0].clone());
        path.push(".etapkilit.key");
        std::fs::write(path.as_path(), serial_number.unwrap());
        // println!("etap kilit oluşturuldu");
    }
    pub fn read_key(&self, priv_key: &rsa::RsaPrivateKey) -> bool {
        //println!("okumaya başlandı");
        let mut trusted_devices = get_trusted_devices();
        let serial_number = &self.serial_number;
        if is_device_trusted(serial_number, trusted_devices) {
            // log::info!("1 0");
            return true;
        }
        for m in &self.mountpoint {
            let mut path = PathBuf::new();
            path.push(&m);
            path.push(".etapkilit.key");
            let enc_data = fs::read(path.as_path());
            if let Ok(e_data) = enc_data {
                if let Ok(dec_data) = priv_key.decrypt(Pkcs1v15Encrypt, &e_data) {
                    if dec_data == serial_number.as_bytes() {
                        add_trusted_device(serial_number);
                        // log::info!("1 1");
                        return true;
                    }
                }
            } else {
                let mut path = PathBuf::new();
                path.push(&m);
                path.push(".password.key");
                let enc_data = fs::read(path.as_path());
                // println!("{enc_data:?}");
                if let Ok(e_data) = enc_data {
                    if let Ok(dec_data) = priv_key.decrypt(Pkcs1v15Encrypt, &e_data) {
                        let passwd = get_passwd_key();
                        if let Ok(passwd) = priv_key.decrypt(Pkcs1v15Encrypt, &passwd) {
                            let dec_data = String::from_utf8(dec_data).unwrap();
                            let passwd = String::from_utf8(passwd).unwrap();
                            if dec_data == passwd || dec_data == passwd.trim() {
                                add_trusted_device(serial_number);
                                fs::remove_file(path.as_path());
                                // log::info!("1 2");
                                return true;
                            }
                        }
                    }
                }
            }
        }
        false
    }
    fn list_device(&self, index: usize) {
        println!(
            "{}- {} {} {}",
            index,
            self.vendor.replace("\\x20", ""),
            &self.model,
            &self.serial_number
        );
    }
    pub fn get_serial(&self) -> String {
        self.serial_number.clone()
    }
}