use std::os::windows::fs::MetadataExt;
use std::path::PathBuf;
use windows::Win32::Foundation::ERROR_SUCCESS;
use windows::Win32::NetworkManagement::NetManagement::{NetApiBufferFree, NetUserEnum, USER_INFO_1};
use windows::Win32::Storage::FileSystem::{FILE_ATTRIBUTE_HIDDEN, INVALID_FILE_ATTRIBUTES};

use super::ty::DesktopItem;

pub fn get_desktop_items(query_user: Option<&str>, attr_filter: Option<u32>, filters: &Vec<&str>) -> Vec<DesktopItem> {
  let mut items = vec![];
  let mut users = get_system_users();
  users.push("Public".to_string());

  if let Some(query) = query_user {
    if users.iter().any(|v| v == &query) {
      users = vec![query.to_string()];
    }
  }

  if let Some(system_drive) = std::env::var_os("SystemDrive") {
    for uname in users {
      let desktop_path = PathBuf::from(&system_drive).join("Users").join(&uname).join("Desktop");

      if desktop_path.exists() {
        process_desktop_entries(&desktop_path, &uname, attr_filter, &filters, &mut items);
      }
    }
  }

  items
}

fn process_desktop_entries(desktop_path: &PathBuf, uname: &str, attr_filter: Option<u32>, filters: &Vec<&str>, items: &mut Vec<DesktopItem>) {
  if let Ok(entries) = std::fs::read_dir(desktop_path) {
    for entry in entries.flatten() {
      let path = entry.path();
      let attr = if let Ok(meta) = entry.metadata() {
        meta.file_attributes()
      } else {
        INVALID_FILE_ATTRIBUTES
      };

      if let Some(filter) = attr_filter {
        if attr == INVALID_FILE_ATTRIBUTES || (attr & filter) != filter {
          continue;
        }
      }

      if entry
        .file_name()
        .into_string()
        .map(|fname| filters.contains(&&*fname))
        .unwrap_or_default()
      {
        continue;
      }

      items.push(DesktopItem {
        uname: uname.to_string(),
        is_dir: path.is_dir(),
        is_hidden: attr & FILE_ATTRIBUTE_HIDDEN != 0,
        path,
        attribute: attr,
      });
    }
  }
}

pub fn get_system_users() -> Vec<String> {
  let mut users = Vec::new();

  unsafe {
    let mut buf = std::ptr::null_mut();
    let mut entries_read = 0u32;
    let mut total_entries = 0u32;
    let mut resume_handle = 0u32;

    let result = NetUserEnum(None, 1, 0, &mut buf, u32::MAX, &mut entries_read, &mut total_entries, &mut resume_handle);

    if result == ERROR_SUCCESS {
      let user_info = std::slice::from_raw_parts(buf as *const USER_INFO_1, entries_read as usize);
      for info in user_info {
        if let Ok(name) = String::from_utf16(std::slice::from_raw_parts(info.usri1_name.0 as *const u16, wcslen(info.usri1_name.0))) {
          users.push(name);
        }
      }
      NetApiBufferFree(buf as *mut ::core::ffi::c_void);
    }
  }

  users
}

unsafe fn wcslen(ptr: *const u16) -> usize {
  let mut len = 0;
  while *ptr.add(len) != 0 {
    len += 1;
  }
  len
}
