use crate::log::get_current_dir;
use crate::utils::cxx_string_to_string;
use cxx::CxxString;
use log::info;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{fs, io};
use sysinfo::{ProcessesToUpdate, System};
use toml::Value;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
#[derive(Default)]
pub struct User {
    username: String,
    password: String,
    server: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
#[derive(Default)]
pub struct Login {
    /// 协议登录，exe路径
    login_exe: String,
    game_path: String,
    list: Vec<User>,
}

#[cxx::bridge]
mod ffi {
    extern "Rust" {
        type Login;

        fn load_login_config() -> Result<Box<Login>>;

        fn get_login_size(cfg: &Box<Login>) -> usize;

        fn get_login_exe(cfg: &Box<Login>) -> String;

        fn login_game(cfg: &Box<Login>, index: usize) -> bool;

        fn get_username(cfg: &Box<Login>, index: usize) -> String;

        fn get_password(cfg: &Box<Login>, index: usize) -> String;

        fn get_server(cfg: &Box<Login>, index: usize) -> String;

        fn is_process_running(process_name: &CxxString) -> bool;

        fn get_process_pid(process_name: &CxxString) -> Vec<u32>;
    }
}

pub(crate) fn load_login_config() -> Result<Box<Login>, Box<dyn std::error::Error>> {
    let path = PathBuf::from("C:\\Windows\\Starpoles\\login.toml"); /* get_current_dir().join("config").join("login.toml");*/
    if !path.exists() {
        fs::create_dir_all(path.parent().unwrap())?;
        let cfg = Login::default();
        fs::write(path, toml::to_string(&cfg).unwrap())?;
        return Ok(Box::new(cfg));
    }
    let text = fs::read_to_string(path)?;
    let cfg: Login = toml::from_str(&text)?;
    Ok(Box::new(cfg))
}

pub(crate) fn get_login_size(p0: &Box<Login>) -> usize {
    p0.list.len()
}

pub(crate) fn get_login_exe(p0: &Box<Login>) -> String {
    p0.login_exe.clone()
}

pub(crate) fn get_username(p0: &Box<Login>, p1: usize) -> String {
    p0.list[p1].username.clone()
}

pub(crate) fn get_password(p0: &Box<Login>, p1: usize) -> String {
    p0.list[p1].password.clone()
}

pub(crate) fn get_server(p0: &Box<Login>, p1: usize) -> String {
    p0.list[p1].server.clone()
}

pub(crate) fn login_game(p0: &Box<Login>, p1: usize) -> bool {
    info!(
        "协议登录：{}，user:{}，password:{},server:{}",
        p0.login_exe, p0.list[p1].username, p0.list[p1].password, p0.list[p1].server
    );
    let status = Command::new(p0.login_exe.clone())
        .args([
            "-g",
            &p0.game_path,
            "-u",
            &p0.list[p1].username,
            "-p",
            &p0.list[p1].password,
            "-a",
            &p0.list[p1].server,
        ])
        .status()
        .unwrap();
    status.success()
}

fn is_process_running(process_name: &CxxString) -> bool {
    let process_name = cxx_string_to_string(process_name);
    let mut sys = System::new();
    sys.refresh_processes(ProcessesToUpdate::All, true);

    sys.processes()
        .values()
        .any(|p| p.name().eq_ignore_ascii_case(&process_name))
}

fn get_process_pid(process_name: &CxxString) -> Vec<u32> {
    let process_name = cxx_string_to_string(process_name);
    let mut sys = System::new();
    sys.refresh_processes(ProcessesToUpdate::All, true);
    sys.processes()
        .values()
        .filter(|p| p.name().eq_ignore_ascii_case(&process_name))
        .map(|p| p.pid().as_u32())
        .collect()
}
