use std::ffi::OsStr;
use std::io;
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt as _;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;

use encoding::all::GBK;
use encoding::{DecoderTrap, Encoding};

/// 弹窗
pub const CREATE_NEW_CONSOLE: u32 = 0x00000010;
/// 不弹窗
pub const CREATE_NO_WINDOW: u32 = 0x08000000;

/// 命令调用
pub fn cmd<I, S>(
  exe: S,
  args: I,
  cwd: Option<PathBuf>,
  has_window: bool,
  autodecode: bool,
) -> io::Result<String>
where
  I: IntoIterator<Item = S>,
  S: AsRef<OsStr>,
{
  let exe_full = if let Some(x) = &cwd {
    x.join(exe.as_ref()).to_string_lossy().to_string()
  } else {
    exe.as_ref().to_string_lossy().to_string()
  };
  let mut binding = Command::new(&*exe_full);
  let cmd = binding
    .args(args)
    .stdin(Stdio::null())
    .stdout(Stdio::piped())
    .stderr(Stdio::piped());
  if let Some(x) = cwd {
    cmd.current_dir(x);
  }
  // 设置 CREATE_NO_WINDOW 标志
  #[cfg(target_os = "windows")]
  {
    let flag = if has_window {
      CREATE_NEW_CONSOLE
    } else {
      CREATE_NO_WINDOW
    };
    cmd.creation_flags(flag);
  }
  let output = cmd.output()?;
  if !output.status.success() {
    let stderr = String::from_utf8_lossy(&output.stderr);
    Err(io::Error::new(io::ErrorKind::Other, stderr))
  } else {
    let stdout = output.stdout;
    let _res = String::from_utf8(stdout.clone());
    let res = if autodecode {
      _res.unwrap_or(
        GBK
          .decode(&stdout, DecoderTrap::Strict)
          .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("GPK解码失败{}", e)))?,
      )
    } else {
      _res.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
    };
    Ok(res)
  }
}

/// 命令调用不等待
pub fn cmd_spawn<I, S>(
  exe: S,
  args: I,
  cwd: Option<PathBuf>,
  has_window: bool,
) -> io::Result<std::process::Child>
where
  I: IntoIterator<Item = S>,
  S: AsRef<OsStr>,
{
  let exe_full = if let Some(x) = &cwd {
    x.join(exe.as_ref()).to_string_lossy().to_string()
  } else {
    exe.as_ref().to_string_lossy().to_string()
  };
  let mut binding = Command::new(&*exe_full);
  let cmd = binding
    .args(args)
    .stdin(Stdio::null())
    .stdout(Stdio::piped())
    .stderr(Stdio::piped());
  if let Some(x) = cwd {
    cmd.current_dir(x);
  }
  // 设置 CREATE_NO_WINDOW 标志
  #[cfg(target_os = "windows")]
  {
    let flag = if has_window {
      CREATE_NEW_CONSOLE
    } else {
      CREATE_NO_WINDOW
    };
    cmd.creation_flags(flag);
  }
  cmd.spawn()
}
