///|
/// Parsed command-line arguments. `CliArgs::parse()` returns `None`
/// when help was printed or the user supplied a malformed `--relay`
/// (the argparse / URL error is already logged — `main` should just
/// return).
struct CliArgs {
  relay_url : @url.Url
  child_argv : Array[String]
}

///|
/// True for hosts that should use plaintext `ws://` / `http://`
/// instead of TLS. Mirrors `isLocalHost` in
/// `android/.../TerminalScreen.kt` so both clients classify the same
/// host identically.
fn is_local_host(host : String) -> Bool {
  host.has_prefix("127.") ||
  host.has_prefix("10.") ||
  host.has_prefix("192.168.") ||
  host.has_prefix("localhost")
}

///|
/// Build the relay WebSocket URL from a bare host (optionally with
/// `:port`). Picks `ws://` for local-style hosts and `wss://`
/// otherwise, appends `/server`, and parses the result.
fn relay_url_from_host(host : String) -> @url.Url raise {
  let scheme = if is_local_host(host) { "ws" } else { "wss" }
  @url.Url::parse("\{scheme}://\{host}/server")
}

///|
fn build_command() -> @argparse.Command {
  @argparse.Command::new(
    "tun-server",
    about="TUN server that connects to a relay via WebSocket",
    options=[
      @argparse.OptionArg::new(
        "relay",
        short='r',
        long="relay",
        about="Relay host (optionally with :port, e.g. 127.0.0.1:8080)",
        default_values=["tun-poc.moonbitlang.cn"],
      ),
    ],
    positionals=[
      @argparse.PositionArg::new(
        "child",
        num_args=@argparse.ValueRange::new(lower=0),
        allow_hyphen_values=true,
        about="Program to spawn (defaults to $SHELL or /bin/sh on Unix, %COMSPEC% or cmd.exe on Windows). Use `--` to pass flags to the program.",
      ),
    ],
  )
}

///|
/// Parse argv. Logs + returns None on argparse error or malformed
/// `--relay` URL; otherwise returns the parsed arguments with the
/// default-shell fallback already applied.
fn CliArgs::parse() -> CliArgs? {
  let cmd = build_command()
  let matches = cmd.parse() catch {
    err => {
      @log.error(err.to_string())
      println(cmd.render_help())
      return None
    }
  }
  let relay_host = matches.values["relay"][0]
  let relay_url = relay_url_from_host(relay_host) catch {
    e => {
      @log.error("invalid --relay host '\{relay_host}': \{e}")
      return None
    }
  }
  let child_argv : Array[String] = match matches.values.get("child") {
    Some(args) if args.length() > 0 => args
    _ =>
      if @platform.win32 {
        [@env.get_env_var("COMSPEC").unwrap_or("cmd.exe")]
      } else {
        [@env.get_env_var("SHELL").unwrap_or("/bin/sh")]
      }
  }
  Some({ relay_url, child_argv })
}