
use crate::utils::ffi;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LicenseStatus {

    NotVerified = 0,

    Verifying = 1,

    Verified = 2,

    Failed = 3,

    Expired = 4,

    MachineIdMismatch = 5,

    TimeRollback = 6,
}

pub struct Authorization;

impl Authorization {

    pub fn license_status() -> LicenseStatus {

        let raw = unsafe { ffi::GetLicenseStatusEx() };
        match raw {
            0 => LicenseStatus::NotVerified,
            1 => LicenseStatus::Verifying,
            2 => LicenseStatus::Verified,
            3 => LicenseStatus::Failed,
            4 => LicenseStatus::Expired,
            5 => LicenseStatus::MachineIdMismatch,
            6 => LicenseStatus::TimeRollback,

            _ => LicenseStatus::NotVerified,
        }
    }

    pub fn activate(activation_code: &str) -> Result<String, String> {
        let code_c = match CString::new(activation_code) {
            Ok(s) => s,
            Err(_) => return Err("激活码包含空字节".to_string()),
        };
        let mut err_buf = vec![0u8; 512];
        let ret = unsafe {
            ffi::LicenseActivate(
                code_c.as_ptr(),
                err_buf.as_mut_ptr() as *mut c_char,
                err_buf.len() as i32,
            )
        };

        let null_pos = err_buf.iter().position(|&b| b == 0).unwrap_or(err_buf.len());
        let msg = String::from_utf8_lossy(&err_buf[..null_pos]).to_string();
        if ret == 0 {
            Ok(if msg.is_empty() { "激活成功".to_string() } else { msg })
        } else {
            Err(if msg.is_empty() { format!("激活失败 (code={})", ret) } else { msg })
        }
    }
}

pub struct AdminHelper;

impl AdminHelper {

    pub fn ensure_running_as_admin() -> bool {
        #[cfg(target_os = "windows")]
        {

            #[link(name = "shell32")]
            extern "system" {
                fn IsUserAnAdmin() -> i32;
            }
            unsafe { IsUserAnAdmin() != 0 }
        }
        #[cfg(not(target_os = "windows"))]
        {

            extern "C" {
                fn geteuid() -> u32;
            }
            unsafe { geteuid() == 0 }
        }
    }

    pub fn ensure_admin_or_restart() -> bool {
        if Self::ensure_running_as_admin() {
            return true;
        }

        #[cfg(target_os = "windows")]
        {

            #[link(name = "kernel32")]
            extern "system" {
                fn GetModuleFileNameW(hModule: *const std::ffi::c_void, lpFilename: *mut u16, nSize: u32) -> u32;
            }
            #[link(name = "shell32")]
            extern "system" {
                fn ShellExecuteW(
                    hwnd: *const std::ffi::c_void,
                    lpOperation: *const u16,
                    lpFile: *const u16,
                    lpParameters: *const u16,
                    lpDirectory: *const u16,
                    nShowCmd: i32,
                ) -> isize;
            }

            let mut path_buf = vec![0u16; 260];
            let len = unsafe {
                GetModuleFileNameW(std::ptr::null(), path_buf.as_mut_ptr(), 260)
            };
            if len == 0 {
                return false;
            }
            path_buf.truncate(len as usize);
            path_buf.push(0);

            let runas: Vec<u16> = "runas\0".encode_utf16().collect();
            let empty: Vec<u16> = "\0".encode_utf16().collect();

            let result = unsafe {
                ShellExecuteW(
                    std::ptr::null(),
                    runas.as_ptr(),
                    path_buf.as_ptr(),
                    empty.as_ptr(),
                    empty.as_ptr(),
                    1,
                )
            };

            result > 32
        }
        #[cfg(not(target_os = "windows"))]
        {
            false
        }
    }
}

pub struct DeviceInfoHelper;

impl DeviceInfoHelper {

    pub fn get_windows_product_key() -> Option<String> {
        let ptr = unsafe { ffi::GetWindowsProductKey() };
        if ptr.is_null() {
            return None;
        }
        let s = unsafe { CStr::from_ptr(ptr) };
        let result = s.to_str().ok().map(|s| s.to_string());

        result.filter(|s| !s.is_empty())
    }

    pub fn get_driver_list() -> Vec<String> {
        let ptr = unsafe { ffi::GetDriverList() };
        if ptr.is_null() {
            return Vec::new();
        }
        let s = unsafe { CStr::from_ptr(ptr) };
        match s.to_str() {
            Ok(list) if !list.is_empty() => {
                list.split(';')
                    .filter(|s| !s.is_empty())
                    .map(|s| s.to_string())
                    .collect()
            }
            _ => Vec::new(),
        }
    }

    pub fn get_real_system_version() -> String {
        #[cfg(target_os = "windows")]
        {
            #[repr(C)]
            struct OsVersionInfoExW {
                os_version_info_size: u32,
                major_version: u32,
                minor_version: u32,
                build_number: u32,
                platform_id: u32,
                sz_csd_version: [u16; 128],
                service_pack_major: u16,
                service_pack_minor: u16,
                suite_mask: u16,
                product_type: u8,
                reserved: u8,
            }

            #[link(name = "ntdll")]
            extern "system" {
                fn RtlGetVersion(lpVersionInformation: *mut OsVersionInfoExW) -> i32;
            }

            let mut info: OsVersionInfoExW = unsafe { std::mem::zeroed() };
            info.os_version_info_size = std::mem::size_of::<OsVersionInfoExW>() as u32;

            let status = unsafe { RtlGetVersion(&mut info) };
            if status == 0 {
                return format!("Windows {}.{}.{}",
                    info.major_version, info.minor_version, info.build_number);
            }

            "Windows (版本未知)".to_string()
        }
        #[cfg(target_os = "linux")]
        {

            if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
                for line in content.lines() {
                    if line.starts_with("PRETTY_NAME=") {
                        return line.trim_start_matches("PRETTY_NAME=")
                            .trim_matches('"')
                            .to_string();
                    }
                }
            }
            "Linux (版本未知)".to_string()
        }
        #[cfg(not(any(target_os = "windows", target_os = "linux")))]
        {
            "未知操作系统".to_string()
        }
    }
}

#[derive(Clone, Debug, Default)]
pub struct LicenseCertificate {

    pub version: i32,

    pub certificate_id: String,

    pub license_type: String,

    pub activation_code: String,

    pub machine_id: String,

    pub device_name: String,

    pub enterprise_id: Option<String>,

    pub enterprise_name: Option<String>,

    pub max_devices: Option<i32>,

    pub issue_date: String,

    pub expiration_date: String,

    pub platform: String,

    pub library_version: String,

    pub core_version: String,

    pub system_version: String,

    pub library_hash: String,

    pub core_hash: String,

    pub signature: String,
}
