// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
fn signal_from_native(sig : Int) -> Signal? {
  match sig {
    1 => Some(Signal::Hangup)
    2 => Some(Signal::Interrupt)
    3 => Some(Signal::Quit)
    4 => Some(Signal::Illegal)
    5 => Some(Signal::Trap)
    6 => Some(Signal::Abort)
    8 => Some(Signal::FloatingPointException)
    9 => Some(Signal::Kill)
    10 => Some(Signal::User1)
    11 => Some(Signal::Segv)
    12 => Some(Signal::User2)
    13 => Some(Signal::Pipe)
    14 => Some(Signal::Alarm)
    15 => Some(Signal::Term)
    17 => Some(Signal::Child)
    18 => Some(Signal::Continue)
    19 => Some(Signal::Stop)
    20 => Some(Signal::TSTP)
    21 => Some(Signal::TTIN)
    22 => Some(Signal::TTOU)
    23 => Some(Signal::Urgent)
    24 => Some(Signal::XCPU)
    25 => Some(Signal::XFSZ)
    26 => Some(Signal::VirtualAlarm)
    27 => Some(Signal::Profiling)
    28 => Some(Signal::Winch)
    29 => Some(Signal::IO)
    30 => Some(Signal::Power)
    31 => Some(Signal::Sys)
    _ => None
  }
}

///|
fn decode_wait_status(status : Int) -> ExitStatus {
  let term_signal = status & 0x7f
  if term_signal == 0 {
    let code = (status >> 8) & 0xff
    ExitStatus::from_code(code)
  } else {
    match signal_from_native(term_signal) {
      Some(signal) => ExitStatus::from_signal(signal)
      None => { code: None, signal: None, success: false }
    }
  }
}

///|
pub fn Process::kill(self : Process) -> Bool {
  self.kill_with(Signal::Term) == Some(true)
}

///|
pub fn Process::kill_with(self : Process, signal : Signal) -> Bool? {
  if !is_supported_system {
    return None
  }
  let rc = kill_pid_ffi(self.pid.to_int(), map_signal_to_native(signal))
  Some(rc == 0)
}

///|
fn wait_until_process_exits(pid : Pid, max_attempts : Int) -> Bool {
  for i = 0; i < max_attempts; i = i + 1 {
    if !process_exists(pid) {
      return true
    }
    sleep_millis_native(50)
  }
  !process_exists(pid)
}

///|
pub fn Process::wait(self : Process) -> ExitStatus? {
  if !is_supported_system {
    return None
  }
  let status = @ref.new(0)
  let rc = wait_pid_ffi(self.pid.to_int(), 0, status)
  if rc == self.pid.to_int() {
    Some(decode_wait_status(status.val))
  } else if rc < 0 && wait_until_process_exits(self.pid, 1200) {
    if get_os_tag() == "linux" {
      None
    } else {
      Some(ExitStatus::from_code(0))
    }
  } else {
    None
  }
}

///|
pub fn Process::kill_and_wait(self : Process) -> Result[ExitStatus?, KillError] {
  self.kill_with_and_wait(Signal::Term)
}

///|
pub fn Process::kill_with_and_wait(
  self : Process,
  signal : Signal,
) -> Result[ExitStatus?, KillError] {
  let wnohang = 1
  match self.kill_with(signal) {
    None => Err(KillError::Unsupported)
    Some(false) => Err(KillError::PermissionDenied)
    Some(true) => {
      let max_attempts = 80
      for i = 0; i < max_attempts; i = i + 1 {
        let status = @ref.new(0)
        let rc = wait_pid_ffi(self.pid.to_int(), wnohang, status)
        if rc == self.pid.to_int() {
          return Ok(Some(decode_wait_status(status.val)))
        }
        if rc < 0 {
          if wait_until_process_exits(self.pid, max_attempts - i) {
            return Ok(None)
          }
          return Err(KillError::WaitFailed)
        }
        // keep polling for child process completion
        sleep_millis_native(50)
      }
      Err(KillError::Timeout)
    }
  }
}