//Untrusted file system
//Encrypting databases and leaving keys on the drive might be
//like leaving keys on the roof on the car when locked:
//InsecureRemovableMediaProvider let's you store keys on a removable media:
//It will wait for the removable media (usb drive, micro ssd, ...) 
//to be plugged into computer,
//read keys from the removable media filesystem, then start the node.
//Be aware that a daemon could fetch keys when the removable media
//is inserted.
//Obviously for some or at first glance for others, you need physical
//access to the machine.
//doesn't use notify yet.

use super::*;
use std::io::Cursor;
use tokio::time::{sleep,Duration};

#[cfg(target_os="linux")]
pub async fn get_key_ans_certs(
  key_path: &str,
  cert_path: &str,
  )->(Vec<u8>,Vec<u8>) {
  tracing::info!("get_key_ans_certs");
  loop {
    let media_path=std::path::Path::new(&format!("/media/{}",std::env::var("USER").unwrap())).to_path_buf();
    
    let dir_content = tokio::fs::read_dir(&media_path).await;
    if let Ok(mut dir_content)=dir_content {
      loop {
        let entry = dir_content.next_entry().await;
        if !entry.is_ok() {
          continue;
        }
        let entry= entry.unwrap();
        if entry.is_none() {
          break;
        }
        let file_path = entry.unwrap().path();
        if file_path.is_dir() {
          let key_path=file_path.join(key_path);
          let cert_path=file_path.join(cert_path);
          tracing::info!("checking {} and {}...",key_path.display(),cert_path.display());
          if key_path.is_file() && cert_path.is_file() {
            
            if let Ok(cert_pem)= tokio::fs::read(&cert_path).await {
              if let Ok(key_pem) = tokio::fs::read(&key_path).await {
                return (key_pem,cert_pem);
              }
            }
          }
          tracing::info!("not found.");
        }
      }
    }
    sleep(Duration::from_secs(1)).await;
  }
}

#[cfg(target_os="windows")]
use winapi::um::fileapi::{GetLogicalDrives, GetDriveTypeW};
#[cfg(target_os="windows")]
use winapi::um::winbase::{DRIVE_REMOVABLE,/* DRIVE_FIXED, DRIVE_REMOTE, DRIVE_CDROM, DRIVE_RAMDISK,*/ DRIVE_NO_ROOT_DIR, /*DRIVE_UNKNOWN*/};
#[cfg(target_os="windows")]
pub async fn get_key_ans_certs(
  key_path: &str,
  cert_path: &str,
)->(Vec<u8>,Vec<u8>) {
  tracing::info!("get_key_ans_certs");
  loop {
    let drives = unsafe { GetLogicalDrives() };
    
    
    for i in 0..26 {
      let drive_number = 1 << i;
      if drives & drive_number != 0 {
        let drive_name = format!("{}:\\", (b'A' + i) as char);
        let drive_type = unsafe { GetDriveTypeW(drive_name.as_ptr() as *const u16) };
        //tracing::info!("{} : {}({})",drive_name,drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_UNKNOWN,drive_type);
        if drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_NO_ROOT_DIR {
          let key_path=std::path::Path::new(&drive_name).join(key_path);
          let cert_path=std::path::Path::new(&drive_name).join(cert_path);
          tracing::info!("checking {} and {}...",key_path.display(),cert_path.display());
          if key_path.is_file() && cert_path.is_file() {
            if let Ok(cert_pem)= tokio::fs::read(&cert_path).await {
              if let Ok(key_pem) = tokio::fs::read(&key_path).await {
                return (key_pem,cert_pem);
              }
            }
          }
        }
      }
    }
    sleep(Duration::from_secs(1)).await;
  }
}

pub struct InsecureRemovableMediaKeyProvider {
  private_key: rustls::pki_types::PrivatePkcs8KeyDer<'static>,
  certificates: Vec<rustls::pki_types::CertificateDer<'static>>,
}

impl InsecureRemovableMediaKeyProvider  {
  pub async fn new(key_path: &str,cert_path: &str)->anyhow::Result<Box<dyn KeyProvider>> {
    
    let (mut pkey_pem,mut certs_pem)=get_key_ans_certs(key_path,cert_path).await;
    
    let certificates =
      rustls_pemfile::certs(&mut Cursor::new(&mut certs_pem))
      .into_iter()
      .filter_map(|x| x.ok())
      .collect::<Vec<_>>();
    
    let private_key = 
      rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&mut pkey_pem))
      .filter_map(|x| x.ok())
      .next();
    if private_key.is_none() {
        return Err(anyhow::anyhow!(
            "No valid key found in {}",
            key_path
        ));
    }
    let private_key = private_key.unwrap();
    //let hashed_key = digest::digest(&digest::SHA256, private_key.secret_pkcs8_der());
    
    Ok(Box::new(Self {
      private_key,
      certificates,
    }))
  }
}

impl KeyProvider for InsecureRemovableMediaKeyProvider  {
  fn get_private_key(&self)->&'_ rustls::pki_types::PrivatePkcs8KeyDer<'static> {
    &self.private_key
  }
  fn get_certificates(&self)->&'_ Vec<rustls::pki_types::CertificateDer<'static>> {
    &self.certificates
  }
} 