///|
/// Show a native dialog and return an encoded success or failure code.
#borrow(title, message, accept_label, reject_label, cancel_label)
extern "c" fn show_dialog_ffi(
  level : Int,
  buttons : Int,
  title : Bytes,
  message : Bytes,
  accept_label : Bytes,
  reject_label : Bytes,
  cancel_label : Bytes,
) -> Int = "moonbit_dialog_show_dialog"

///|
const BACKEND_UNAVAILABLE_STATUS : Int = -1

///|
const UNSUPPORTED_PLATFORM_STATUS : Int = -2

///|
const NATIVE_FAILURE_BASE : Int = 1000000

///|
const NATIVE_FAILURE_STRIDE : Int = 100000

///|
const SUCCESS_RESPONSE_STRIDE : Int = 10

///|
const DIALOG_BUTTONS_OK_CODE : Int = 0

///|
const DIALOG_BUTTONS_OK_CANCEL_CODE : Int = 1

///|
const DIALOG_BUTTONS_YES_NO_CODE : Int = 2

///|
const DIALOG_BUTTONS_YES_NO_CANCEL_CODE : Int = 3

///|
/// Encode a MoonBit string as a NUL-terminated UTF-8 byte buffer for FFI.
fn encode_utf8_bytes(text : String) -> Bytes {
  let encoded = @utf8.encode(text[:])
  let len = encoded.length()
  Bytes::makei(len + 1, i => if i < len { encoded[i] } else { b'\x00' })
}

///|
/// Decode a NUL-terminated UTF-8 byte buffer into a MoonBit string.
fn utf8_bytes_to_mbt_string(bytes : Bytes) -> String {
  let res = StringBuilder::new()
  let len = bytes.length()
  let mut i = 0
  while i < len {
    let mut c = bytes[i].to_int()
    if c == 0 {
      break
    } else if c < 0x80 {
      res.write_char(c.unsafe_to_char())
      i += 1
    } else if c < 0xE0 {
      if i + 1 >= len {
        break
      }
      c = ((c & 0x1F) << 6) | (bytes[i + 1].to_int() & 0x3F)
      res.write_char(c.unsafe_to_char())
      i += 2
    } else if c < 0xF0 {
      if i + 2 >= len {
        break
      }
      c = ((c & 0x0F) << 12) |
        ((bytes[i + 1].to_int() & 0x3F) << 6) |
        (bytes[i + 2].to_int() & 0x3F)
      res.write_char(c.unsafe_to_char())
      i += 3
    } else {
      if i + 3 >= len {
        break
      }
      c = ((c & 0x07) << 18) |
        ((bytes[i + 1].to_int() & 0x3F) << 12) |
        ((bytes[i + 2].to_int() & 0x3F) << 6) |
        (bytes[i + 3].to_int() & 0x3F)
      c -= 0x10000
      res.write_char(((c >> 10) + 0xD800).unsafe_to_char())
      res.write_char(((c & 0x3FF) + 0xDC00).unsafe_to_char())
      i += 4
    }
  }
  res.to_string()
}

///|
/// Convert a public level enum into the native FFI code.
fn dialog_level_to_code(level : DialogLevel) -> Int {
  match level {
    Info => 0
    Warning => 1
    Error => 2
    Question => 3
  }
}

///|
/// Convert a public button combination into the native FFI code.
fn dialog_buttons_to_code(buttons : DialogButtons) -> Int {
  match buttons {
    Ok => DIALOG_BUTTONS_OK_CODE
    OkCancel => DIALOG_BUTTONS_OK_CANCEL_CODE
    YesNo => DIALOG_BUTTONS_YES_NO_CODE
    YesNoCancel => DIALOG_BUTTONS_YES_NO_CANCEL_CODE
  }
}

///|
/// Convert a native backend code into the corresponding enum.
fn backend_from_code(code : Int) -> DialogBackend? {
  match code {
    0 => Some(WindowsWin32)
    1 => Some(MacOSCoreFoundation)
    2 => Some(MacOSAppleScript)
    3 => Some(LinuxZenity)
    4 => Some(LinuxKDialog)
    5 => Some(LinuxXMessage)
    _ => None
  }
}

///|
/// Decode the backend part of a successful native result.
fn backend_from_success(raw : Int) -> DialogBackend? {
  backend_from_code(raw % SUCCESS_RESPONSE_STRIDE)
}

///|
/// Decode the response part of a successful native result.
fn response_from_success(raw : Int) -> DialogResponse? {
  match raw / SUCCESS_RESPONSE_STRIDE {
    0 => Some(Ok)
    1 => Some(Cancel)
    2 => Some(Yes)
    3 => Some(No)
    _ => None
  }
}

///|
/// Decode backend failures from the C stub result.
fn decode_failure_status(raw : Int) -> (DialogBackend, Int)? {
  if raw > -NATIVE_FAILURE_BASE {
    None
  } else {
    let encoded = -raw - NATIVE_FAILURE_BASE
    match backend_from_code(encoded / NATIVE_FAILURE_STRIDE) {
      Some(backend) => Some((backend, encoded % NATIVE_FAILURE_STRIDE))
      None => None
    }
  }
}

///|
/// Decode a raw native status into a public error when the dialog did not succeed.
fn decode_status_error(platform : Platform, raw : Int) -> DialogError? {
  if raw == BACKEND_UNAVAILABLE_STATUS {
    Some(BackendUnavailable(platform))
  } else if raw == UNSUPPORTED_PLATFORM_STATUS {
    Some(UnsupportedPlatform(platform))
  } else if raw < 0 {
    match decode_failure_status(raw) {
      Some((backend, detail)) => Some(BackendFailed(backend, detail))
      None => Some(UnsupportedPlatform(platform))
    }
  } else {
    None
  }
}

///|
/// Decode a raw native dialog status into a backend-and-response result.
fn decode_dialog_outcome(
  platform : Platform,
  raw : Int,
) -> Result[DialogOutcome, DialogError] {
  match decode_status_error(platform, raw) {
    Some(error) => Err(error)
    None =>
      match (backend_from_success(raw), response_from_success(raw)) {
        (Some(backend), Some(response)) => Ok({ backend, response })
        _ => Err(UnsupportedPlatform(platform))
      }
  }
}

///|
/// Resolve the current platform and reject unsupported targets up front.
fn supported_platform_result() -> Result[Platform, DialogError] {
  let platform = current_platform_internal()
  if platform == Unknown {
    Err(UnsupportedPlatform(Unknown))
  } else {
    Ok(platform)
  }
}

///|
/// Show a dialog request and return the platform plus raw native status.
fn show_dialog_raw_request(
  title : String,
  message : String,
  level : DialogLevel,
  buttons : DialogButtons,
  labels : DialogLabels,
) -> Result[(Platform, Int), DialogError] {
  match supported_platform_result() {
    Ok(platform) =>
      Ok(
        (
          platform,
          show_dialog_ffi(
            dialog_level_to_code(level),
            dialog_buttons_to_code(buttons),
            encode_utf8_bytes(title),
            encode_utf8_bytes(message),
            encode_utf8_bytes(labels.accept),
            encode_utf8_bytes(labels.reject),
            encode_utf8_bytes(labels.cancel),
          ),
        ),
      )
    Err(error) => Err(error)
  }
}

///|
/// Decode a raw native dialog status into a backend-only public result.
fn decode_backend_result(
  platform : Platform,
  raw : Int,
) -> Result[DialogBackend, DialogError] {
  match decode_dialog_outcome(platform, raw) {
    Ok(outcome) => Ok(outcome.backend)
    Err(error) => Err(error)
  }
}

///|
/// Show a dialog request and decode the backend plus selected response.
fn show_dialog_request(
  title : String,
  message : String,
  level : DialogLevel,
  buttons : DialogButtons,
  labels : DialogLabels,
) -> Result[DialogOutcome, DialogError] {
  match show_dialog_raw_request(title, message, level, buttons, labels) {
    Ok((platform, raw_result)) => decode_dialog_outcome(platform, raw_result)
    Err(error) => Err(error)
  }
}

///|
/// Show a dialog request and decode only the backend that displayed it.
fn show_dialog_backend_request(
  title : String,
  message : String,
  level : DialogLevel,
  buttons : DialogButtons,
  labels : DialogLabels,
) -> Result[DialogBackend, DialogError] {
  match show_dialog_raw_request(title, message, level, buttons, labels) {
    Ok((platform, raw_result)) => decode_backend_result(platform, raw_result)
    Err(error) => Err(error)
  }
}

///|
/// Resolve the native platform at compile time when the toolchain exposes it.
#cfg(platform="windows")
fn current_platform_internal() -> Platform {
  Windows
}

///|
/// Resolve the current platform as macOS for supported Apple targets.
#cfg(any(platform="macos", platform="darwin", platform="osx"))
fn current_platform_internal() -> Platform {
  MacOS
}

///|
/// Resolve the current platform as Linux when the native target is Linux.
#cfg(platform="linux")
fn current_platform_internal() -> Platform {
  Linux
}

///|
/// Fall back to `Unknown` when no recognized platform tag is available.
#cfg(not(any(platform="windows", platform="macos", platform="darwin", platform="osx", platform="linux")))
fn current_platform_internal() -> Platform {
  Unknown
}