use std::error::Error;
use std::process::Command;
extern crate log;
use aes::Aes128;
use block_modes::{BlockMode, Cbc};
use block_modes::block_padding::Pkcs7;
use base64::{Engine as _, engine::general_purpose};
type Aes128Cbc = Cbc<Aes128, Pkcs7>;

use std::ffi::CString;
use winapi::um::fileapi::GetVolumeInformationA;
use std::fmt;

#[derive(Debug)]
pub enum EncryptErr {
    NotBase64,
}

impl fmt::Display for EncryptErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            EncryptErr::NotBase64 => write!(f, "String is not base64"),
        }
    }
}

impl std::error::Error for EncryptErr {}

// pub fn get_key() -> String {
//     let output = Command::new("wmic")
//         .args(&["DISKDRIVE", "get", "serialnumber"])
//         .output()
//         .expect("Failed to execute command");

//     let serial = String::from_utf8_lossy(&output.stdout)
//         .lines()
//         .nth(1) // 获取第二行，通常是序列号
//         .unwrap()
//         .trim()
//         .to_string();
//     log::trace!("hdd serial is {}", serial);
//     serial
// }
pub fn get_key() -> String {
    let drive: &str = "C:\\";
    let mut volume_name: [i8; 100] = [0; 100];
    let mut file_system_name: [i8; 100] = [0; 100];
    let mut volume_serial_number: u32 = 0;
    let mut max_component_length: u32 = 0;
    let mut file_system_flags: u32 = 0;
    let tstr = CString::new(drive).unwrap();
    let success = unsafe {
        GetVolumeInformationA(
            tstr.as_ptr(),
            volume_name.as_mut_ptr(),
            volume_name.len() as u32,
            &mut volume_serial_number,
            &mut max_component_length,
            &mut file_system_flags,
            file_system_name.as_mut_ptr(),
            file_system_name.len() as u32,
        )
    };

    if success != 0 {
        return volume_serial_number.to_string()
    } else {
        log::warn!("Failed to get volume information.");
        return "".to_string();
    }
}
pub fn is_base64(encrypted_data: &str) -> bool {
    match general_purpose::STANDARD.decode(encrypted_data) {
        Ok(_) => true,
        Err(_) => false,
    }
}

pub fn encrypt(raw_data: &str, key: &str, iv:&str) -> String {
    let key = append_align(key);
    let iv = append_align(iv);

    let cipher = Aes128Cbc::new_from_slices(&key, &iv).unwrap();

    let pos = cipher.encrypt_vec(&raw_data.as_bytes());
    // println!("pos buff size is {} {}, {}", general_purpose::STANDARD.encode(&pos), pos.len(), data.len());
    let ret = [iv.to_vec(), pos.to_vec()].concat();// Prepend IV for decryption
    general_purpose::STANDARD.encode(ret)
}

pub fn decrypt(encrypted_data: &str, key: &str) -> Result<String, Box<dyn Error>> {
    let key: Vec<u8> = append_align(key);
    let encrypted_data = general_purpose::STANDARD.decode(encrypted_data)?;
    let (iv, data) = encrypted_data.split_at(16); // First 16 bytes are IV

    let cipher =  Aes128Cbc::new_from_slices(&key, &iv)?;
    let decrypted_data = cipher.decrypt_vec(data).unwrap();

    let result = String::from_utf8(decrypted_data)?;
    Ok(result)
}


fn append_align(uuid: &str) -> Vec<u8> {
    let mut key = uuid.as_bytes().to_vec();
    key.resize(16, b'_');
    key
}

#[test]
fn test_get_key(){
    get_key();
}

#[test]
fn test_encrypt_decrypt() {
    let user_name = "ekko.bao";
    let password = "PASSWORD";
    let machine_id = get_key();
    let encrypted_data = encrypt(password,&machine_id, user_name);
    // println!("encrypted_data is {}", encrypted_data);
    let ret = decrypt(&encrypted_data, &machine_id).unwrap();
    println!("decrypt is {}", ret);
    assert_eq!(password, ret);
}