///|
/// High-level managed app that owns the parent dispatcher and child webview
/// process lifecycle.
pub struct Window {
  label : String
  title : String
  width : Int
  height : Int
  size_hint : SizeHint
  debug : Int
  devtools : Bool
  child_arg : String
  mut frameless : Bool
  mut resizable : Bool
  mut closeable : Bool
  mut always_on_top : Bool
  mut transparent : Bool
  mut title_bar_style : TitleBarStyle
  mut title_bar_overlay : Bool
  mut traffic_light_position : (Int, Int)
  position : (Int, Int)
  hidden : Bool
  focused : Bool
  enable_window_controls_plugin : Bool
  mut url : String
  mut html : String
  pending_custom_protocols : Array[(String, String)]
  mut runtime_window_id : Int
  plugins : Array[Plugin]
}

///|
/// Creates a managed app. The library handles the parent dispatcher and child
/// webview process automatically.
#alias(new, deprecated="Use `Window()` instead")
pub fn Window::Window(
  label? : String = "",
  title? : String = "MoonBit WebView",
  url? : String = "",
  width? : Int = 800,
  height? : Int = 600,
  size_hint? : SizeHint = None,
  debug? : Int = 0,
  devtools? : Bool = false,
  child_arg? : String = "--moonbit-webview-child",
  frameless? : Bool = false,
  resizable? : Bool = true,
  closeable? : Bool = true,
  always_on_top? : Bool = false,
  transparent? : Bool = false,
  title_bar_style? : TitleBarStyle = Default,
  title_bar_overlay? : Bool = false,
  traffic_light_position? : (Int, Int) = (-1, -1),
  position? : (Int, Int) = (-1, -1),
  hidden? : Bool = false,
  focused? : Bool = true,
  enable_window_controls_plugin? : Bool = true,
) -> Window {
  {
    label,
    title,
    url,
    width,
    height,
    debug,
    devtools,
    child_arg,
    frameless,
    resizable,
    closeable,
    always_on_top,
    transparent,
    title_bar_style,
    title_bar_overlay,
    traffic_light_position,
    position,
    hidden,
    focused,
    enable_window_controls_plugin,
    html: "",
    pending_custom_protocols: [],
    runtime_window_id: -1,
    plugins: [],
    size_hint,
  }
}

///|
/// Returns the stable label used for cross-window addressing.
/// Falls back to the `title` when no explicit label was provided.
pub fn Window::label(self : Window) -> String {
  if self.label.is_empty() {
    self.title
  } else {
    self.label
  }
}

///|
/// Applies native custom-window style flags.
pub fn Window::set_window_customization(
  self : Window,
  frameless : Bool,
  resizable : Bool,
  closeable : Bool,
  always_on_top : Bool,
  transparent : Bool,
  title_bar_style : TitleBarStyle,
  title_bar_overlay : Bool,
) -> Unit {
  self.frameless = frameless
  self.resizable = resizable
  self.closeable = closeable
  self.always_on_top = always_on_top
  self.transparent = transparent
  self.title_bar_style = title_bar_style
  self.title_bar_overlay = title_bar_overlay
  if self.runtime_window_id > 0 {
    ignore(
      wm_set_window_customization(
        self.runtime_window_id,
        if frameless {
          1
        } else {
          0
        },
        if resizable {
          1
        } else {
          0
        },
        if closeable {
          1
        } else {
          0
        },
        if always_on_top {
          1
        } else {
          0
        },
        if transparent {
          1
        } else {
          0
        },
        title_bar_style,
        if title_bar_overlay {
          1
        } else {
          0
        },
      ),
    )
  }
}

///|
/// Set macOS traffic-light buttons position.
pub fn Window::set_traffic_light_position(
  self : Window,
  x : Int,
  y : Int,
) -> Unit {
  self.traffic_light_position = (x, y)
  if self.runtime_window_id > 0 {
    ignore(
      @window_manager.wm_set_traffic_light_position(
        self.runtime_window_id,
        x,
        y,
      ),
    )
  }
}

///|
/// Installs a managed plugin into the app.
pub fn Window::install(self : Window, plugin : Plugin) -> Unit {
  self.plugins.push(plugin)
}

///|
/// Sets inline HTML content for the child webview.
pub fn Window::set_html(self : Window, html : String) -> Unit {
  self.html = html
  if self.runtime_window_id > 0 {
    ignore(
      @window_manager.wm_set_html(
        self.runtime_window_id,
        @encoding/utf8.encode(html),
      ),
    )
  }
}

///|
pub fn Window::navigate(self : Window, url : String) -> Unit {
  self.url = url
  if self.runtime_window_id > 0 {
    ignore(
      @window_manager.wm_navigate(
        self.runtime_window_id,
        @encoding/utf8.encode(url),
      ),
    )
  }
}

///|
/// Register a custom scheme for the child webview before it navigates.
pub fn Window::set_custom_protocol(
  self : Window,
  scheme : String,
  root_dir : String,
) -> Unit {
  self.pending_custom_protocols.push((scheme, root_dir))
}

///|
/// Runs the managed app end-to-end.
pub async fn Window::run(self : Window) -> Unit {
  let args = @env.args()
  if args.length() > 1 && args[1] == self.child_arg {
    guard WindowManager::connect_child_process() == 0 else {
      abort("ManagedApp::run: connect child process failed")
    }
    self.run_child_async()
    return
  }
  let wm = WindowManager::init()
  guard args.length() > 0 else { abort("ManagedApp::run: missing argv[0]") }
  let child_pid = wm.spawn_process(args[0], self.child_arg)
  guard child_pid > 0 else {
    abort("ManagedApp::run: spawn webview process failed")
  }
  self.build_router().serve(wm, child_pid)
}

///|
/// Encodes a unique child-process marker used to carry a globally-unique
/// window-id `base` and a stable `label` across a re-exec`ed child process.
pub fn child_arg_for(base : Int, label : String) -> String {
  "--lepus-child:" + base.to_string() + ":" + label
}

///|
/// Detects whether the current process was spawned as a Lepus multi-window
/// child.
///
/// Returns `(window_id_base, label)` when argv carries a
/// `--lepus-child::