use std::ffi::CString;

use anyhow::{bail, Result};
use cyberex::xffi::sto::cchar_to_string;
use xmtool_sys::{
    util_cert_check, util_cert_parse, util_init_gmtool_runtime, util_pubkey_to_what,
    util_sm2key_gen,
};

pub fn init_gmtool_runtime(params: &str) -> Result<()> {
    let params_ctsring = CString::new(params)?;
    let mut out_err = vec![0; 256];

    if unsafe {
        util_init_gmtool_runtime(
            params_ctsring.as_ptr(),
            out_err.as_mut_ptr(),
            out_err.len() as _,
        )
    } == -1
    {
        bail!(cchar_to_string(out_err.as_ptr()))
    }
    Ok(())
}

pub fn sm2key_gen(password: &str) -> Result<(Vec<u8>, Vec<u8>)> {
    let mut out_pubkey = vec![0; 1024];
    let mut out_prikey = vec![0; 1024];
    let (mut out_pubkey_len, mut out_prikey_len) = (0_i32, 0_i32);
    let mut out_err = vec![0; 256];

    if unsafe {
        util_sm2key_gen(
            password.as_ptr(),
            password.len() as _,
            out_pubkey.as_mut_ptr(),
            out_pubkey.len() as _,
            &mut out_pubkey_len as _,
            out_prikey.as_mut_ptr(),
            out_prikey.len() as _,
            &mut out_prikey_len as _,
            out_err.as_mut_ptr(),
            out_err.len() as _,
        )
    } == -1
    {
        bail!(cchar_to_string(out_err.as_ptr()))
    }
    out_pubkey.resize(out_pubkey_len as _, 0);
    out_prikey.resize(out_prikey_len as _, 0);
    Ok((out_pubkey, out_prikey))
}

pub fn cert_check(cert: &str, root_cert: &str, etc: &str) -> Result<()> {
    let mut out_err = vec![0; 256];
    if unsafe {
        util_cert_check(
            cert.as_ptr(),
            cert.len() as _,
            root_cert.as_ptr(),
            root_cert.len() as _,
            etc.as_ptr().cast(),
            etc.len() as _,
            out_err.as_mut_ptr(),
            out_err.len() as _,
        )
    } == -1
    {
        bail!(cchar_to_string(out_err.as_ptr()))
    }
    Ok(())
}
// pubkey((cert, pubkey)pem(path), hex-string-(un)compress) to what(hex-string)
// what=1: pubkey -> hex-string-compress(len:66)
// what=2: pubkey -> hex-string-uncompress(len:130)
// what=3: pubkey -> [x, y]
pub fn pubkey_to_what_string(pubkey: &[u8], what: i32) -> Result<String> {
    let mut out_err = vec![0; 256];
    let mut out = vec![0; 1024];
    let len = unsafe {
        util_pubkey_to_what(
            pubkey.as_ptr(),
            pubkey.len() as _,
            what,
            out.as_mut_ptr(),
            out.len() as _,
            out_err.as_mut_ptr(),
            out_err.len() as _,
        )
    };
    if len == -1 {
        bail!(cchar_to_string(out_err.as_ptr()))
    }
    out.resize(len as _, 0);

    Ok(String::from_utf8(out)?)
}

pub fn cert_parse(pubkey: &[u8]) -> Result<String> {
    let mut out_err = vec![0; 256];
    let mut out = vec![0; 10 * 1024];

    let len = unsafe {
        util_cert_parse(
            pubkey.as_ptr(),
            pubkey.len() as _,
            out.as_mut_ptr(),
            out.len() as _,
            out_err.as_mut_ptr(),
            out_err.len() as _,
        )
    };
    if len == -1 {
        bail!(cchar_to_string(out_err.as_ptr()))
    }
    out.resize(len as _, 0);

    Ok(String::from_utf8(out)?)
}