use std::process::Command;

// Copied from SO: https://stackoverflow.com/a/55231715
#[cfg(target_os = "windows")]
use winapi::{
    shared::{minwindef::DWORD, ntdef::HANDLE},
    um::{
        processthreadsapi::{OpenProcess, TerminateProcess},
        winnt::{PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE},
    },
};

/// This file is meant to house (OS specific) implementations on how to kill processes.
use crate::utils::error::BottomError;

#[cfg(target_os = "windows")]
struct Process(HANDLE);

#[cfg(target_os = "windows")]
impl Process {
    fn open(pid: DWORD) -> Result<Process, String> {
        let pc = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, 0, pid) };
        if pc.is_null() {
            return Err("!OpenProcess".to_string());
        }
        Ok(Process(pc))
    }

    fn kill(self) -> Result<(), String> {
        unsafe { TerminateProcess(self.0, 1) };
        Ok(())
    }
}

/// Kills a process, given a PID.
pub fn kill_process_given_pid(pid: u32) -> crate::utils::error::Result<()> {
    if cfg!(target_os = "linux") || cfg!(target_os = "macos") {
        let output = Command::new("kill").arg(pid.to_string()).output()?;
        if !(output.status).success() {
            return Err(BottomError::GenericError(
                std::str::from_utf8(&output.stderr)?.to_string(),
            ));
        }
    } else if cfg!(target_os = "windows") {
        #[cfg(target_os = "windows")]
        {
            let process = Process::open(pid as DWORD)?;
            process.kill()?;
        }
    } else {
        return Err(BottomError::GenericError(
            "Sorry, support operating systems outside the main three are not implemented yet!"
                .to_string(),
        ));
    }

    Ok(())
}
