///| GitHub sync subcommands for `git-bit issue sync`

///|
fn print_hub_issue_sync_usage() -> Unit {
  println("Usage: git-bit issue sync  []")
  println("")
  println("Subcommands:")
  println(
    "  pull --repo  [--include-prs] [--comments append-only|full|no-delete]",
  )
  println(
    "  push --github --repo  [--apply] [--force-remote] [--include-prs] [--comments append-only|full|no-delete]",
  )
  println("  status --repo ")
  println("  install-hook                            (install git hook)")
}

///|
priv struct HubIssueSyncPushOptions {
  repo : String?
  github_flag : Bool
  apply : Bool
  include_prs : Bool
  comments_mode : String
  force_remote : Bool
  help : Bool
}

///|
fn parse_hub_issue_sync_push_options(
  args : Array[String],
) -> HubIssueSyncPushOptions {
  let mut repo : String? = None
  let mut github_flag = false
  let mut apply = false
  let mut include_prs = false
  let mut comments_mode = "append-only"
  let mut force_remote = false
  let mut help = false
  let mut i = 0
  while i < args.length() {
    let arg = args[i]
    if arg == "--repo" && i + 1 < args.length() {
      i += 1
      repo = Some(args[i])
    } else if arg.has_prefix("--repo=") {
      repo = Some(String::unsafe_substring(arg, start=7, end=arg.length()))
    } else if arg == "--github" {
      github_flag = true
    } else if arg == "--apply" {
      apply = true
    } else if arg == "--include-prs" {
      include_prs = true
    } else if arg == "--comments" && i + 1 < args.length() {
      i += 1
      comments_mode = args[i]
    } else if arg.has_prefix("--comments=") {
      comments_mode = String::unsafe_substring(arg, start=11, end=arg.length())
    } else if arg == "--force-remote" {
      force_remote = true
    } else if arg == "-h" || arg == "--help" {
      help = true
    }
    i += 1
  }
  { repo, github_flag, apply, include_prs, comments_mode, force_remote, help }
}

///|
priv struct HubIssueSyncBidirectionalOptions {
  repo : String?
  github_flag : Bool
  apply : Bool
  conflict : String
  comments_mode : String
  include_prs : Bool
  since : String
  state : String
  force_remote : Bool
  help : Bool
}

///|
fn parse_hub_issue_sync_bidirectional_options(
  args : Array[String],
) -> HubIssueSyncBidirectionalOptions {
  let mut repo : String? = None
  let mut github_flag = false
  let mut apply = false
  let mut conflict = "newer-wins"
  let mut comments_mode = "append-only"
  let mut include_prs = false
  let mut since = ""
  let mut state = "all"
  let mut force_remote = false
  let mut help = false
  let mut i = 0
  while i < args.length() {
    let arg = args[i]
    if arg == "--repo" && i + 1 < args.length() {
      i += 1
      repo = Some(args[i])
    } else if arg.has_prefix("--repo=") {
      repo = Some(String::unsafe_substring(arg, start=7, end=arg.length()))
    } else if arg == "--github" {
      github_flag = true
    } else if arg == "--apply" {
      apply = true
    } else if arg == "--conflict" && i + 1 < args.length() {
      i += 1
      conflict = args[i]
    } else if arg.has_prefix("--conflict=") {
      conflict = String::unsafe_substring(arg, start=11, end=arg.length())
    } else if arg == "--comments" && i + 1 < args.length() {
      i += 1
      comments_mode = args[i]
    } else if arg.has_prefix("--comments=") {
      comments_mode = String::unsafe_substring(arg, start=11, end=arg.length())
    } else if arg == "--include-prs" {
      include_prs = true
    } else if arg == "--since" && i + 1 < args.length() {
      i += 1
      since = args[i]
    } else if arg.has_prefix("--since=") {
      since = String::unsafe_substring(arg, start=8, end=arg.length())
    } else if arg == "--state" && i + 1 < args.length() {
      i += 1
      state = args[i]
    } else if arg.has_prefix("--state=") {
      state = String::unsafe_substring(arg, start=8, end=arg.length())
    } else if arg == "--force-remote" {
      force_remote = true
    } else if arg == "-h" || arg == "--help" {
      help = true
    }
    i += 1
  }
  {
    repo,
    github_flag,
    apply,
    conflict,
    comments_mode,
    include_prs,
    since,
    state,
    force_remote,
    help,
  }
}

///|
async fn handle_hub_issue_sync(args : Array[String]) -> Unit raise Error {
  if args.length() == 0 {
    print_hub_issue_sync_usage()
    return ()
  }
  let subcmd = args[0]
  let rest = collect_args(args, 1)
  match subcmd {
    "pull" => handle_hub_issue_sync_pull(rest)
    "push" => {
      if !github_sync_write_enabled() {
        github_sync_write_disabled_message("push")
        return ()
      }
      handle_hub_issue_sync_push(rest)
    }
    "status" => handle_hub_issue_sync_status(rest)
    "install-hook" => {
      if !github_sync_write_enabled() {
        github_sync_write_disabled_message("install-hook")
        return ()
      }
      handle_hub_issue_sync_install_hook(rest)
    }
    "help" | "-h" | "--help" => print_hub_issue_sync_usage()
    _ => {
      // Bidirectional includes push — check write access
      if !github_sync_write_enabled() {
        github_sync_write_disabled_message("sync")
        return ()
      }
      handle_hub_issue_sync_bidirectional(args)
    }
  }
}

///|
async fn handle_hub_issue_sync_pull(args : Array[String]) -> Unit raise Error {
  let mut repo : String? = None
  let mut since = ""
  let mut state = "all"
  let mut conflict = "newer-wins"
  let mut include_prs = false
  let mut comments_mode = "append-only"
  let mut i = 0
  while i < args.length() {
    let arg = args[i]
    if arg == "--repo" && i + 1 < args.length() {
      i += 1
      repo = Some(args[i])
    } else if arg.has_prefix("--repo=") {
      repo = Some(String::unsafe_substring(arg, start=7, end=arg.length()))
    } else if arg == "--since" && i + 1 < args.length() {
      i += 1
      since = args[i]
    } else if arg.has_prefix("--since=") {
      since = String::unsafe_substring(arg, start=8, end=arg.length())
    } else if arg == "--state" && i + 1 < args.length() {
      i += 1
      state = args[i]
    } else if arg.has_prefix("--state=") {
      state = String::unsafe_substring(arg, start=8, end=arg.length())
    } else if arg == "--conflict" && i + 1 < args.length() {
      i += 1
      conflict = args[i]
    } else if arg.has_prefix("--conflict=") {
      conflict = String::unsafe_substring(arg, start=11, end=arg.length())
    } else if arg == "--include-prs" {
      include_prs = true
    } else if arg == "--comments" && i + 1 < args.length() {
      i += 1
      comments_mode = args[i]
    } else if arg.has_prefix("--comments=") {
      comments_mode = String::unsafe_substring(arg, start=11, end=arg.length())
    } else if arg == "-h" || arg == "--help" {
      print_hub_issue_sync_usage()
      return ()
    }
    i += 1
  }
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let fs = OsFs::new()
  let resolved_repo = resolve_github_repo(fs, git_dir, repo)
  let (objects, refs, clock) = make_hub_stores(git_dir)
  let hub = load_hub_store(objects, refs)
  print_line("Pulling issues from GitHub \{resolved_repo}...")
  let result = @hub_native.github_sync_pull_issues(
    hub, objects, refs, clock, resolved_repo, since, state, conflict,
  )
  print_line("Issues pull complete:")
  print_line("  Created: \{result.created}")
  print_line("  Updated: \{result.updated}")
  print_line("  Skipped: \{result.skipped}")
  if result.conflicts.length() > 0 {
    print_line("  Conflicts (manual review needed):")
    for c in result.conflicts {
      print_line("    \{c}")
    }
  }
  // Pull comments for synced issues
  let links = hub.list_sync_links(objects, repo=resolved_repo)
  let mut total_comments_created = 0
  let mut total_comments_updated = 0
  let mut total_comments_deleted = 0
  for link in links {
    if link.local_kind() == "issue" || link.local_kind() == "pr" {
      let cr = @hub_native.github_sync_pull_comments(
        hub,
        objects,
        refs,
        clock,
        resolved_repo,
        link.local_id(),
        link.remote_number(),
        comments_mode,
      )
      total_comments_created += cr.created()
      total_comments_updated += cr.updated()
      total_comments_deleted += cr.deleted()
      if cr.errors().length() > 0 {
        for err in cr.errors() {
          print_line("  Comment error: \{err}")
        }
      }
    }
  }
  if total_comments_created > 0 ||
    total_comments_updated > 0 ||
    total_comments_deleted > 0 {
    print_line("")
    print_line("Comments pull complete:")
    print_line("  Created: \{total_comments_created}")
    print_line("  Updated: \{total_comments_updated}")
    print_line("  Deleted: \{total_comments_deleted}")
  }
  if include_prs {
    print_line("")
    print_line("Pulling PRs from GitHub \{resolved_repo}...")
    let pr_result = @hub_native.github_sync_pull_prs(
      hub, objects, refs, clock, resolved_repo, since, state, conflict,
    )
    print_line("PRs pull complete:")
    print_line("  Created: \{pr_result.created}")
    print_line("  Updated: \{pr_result.updated}")
    print_line("  Skipped: \{pr_result.skipped}")
    if pr_result.conflicts.length() > 0 {
      print_line("  Conflicts (manual review needed):")
      for c in pr_result.conflicts {
        print_line("    \{c}")
      }
    }
  }
}

///|
async fn handle_hub_issue_sync_push(args : Array[String]) -> Unit raise Error {
  let options = parse_hub_issue_sync_push_options(args)
  if options.help {
    print_hub_issue_sync_usage()
    return ()
  }
  if !options.github_flag {
    eprint_line("Error: --github flag required for push (safety measure)")
    eprint_line(
      "Usage: git-bit issue sync push --github --repo  [--apply]",
    )
    @sys.exit(1)
  }
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let fs = OsFs::new()
  let resolved_repo = resolve_github_repo(fs, git_dir, options.repo)
  if !options.force_remote {
    let can_push = @hub_native.github_check_repo_push_permission(resolved_repo) catch {
      _ => false
    }
    if !can_push {
      eprint_line(
        "Error: push requires GitHub write access to the remote repository",
      )
      eprint_line(
        "Repository '\{resolved_repo}' is not writable by the authenticated GitHub user",
      )
      eprint_line("Use --force-remote to override (not recommended)")
      @sys.exit(1)
    }
  }
  let (objects, refs, clock) = make_hub_stores(git_dir)
  let hub = load_hub_store(objects, refs)
  let changes = @hub_native.github_sync_plan_push(hub, objects, resolved_repo)
  let pr_changes = if options.include_prs {
    @hub_native.github_sync_plan_push_prs(hub, objects, resolved_repo)
  } else {
    []
  }
  if changes.length() == 0 && pr_changes.length() == 0 {
    print_line("Nothing to push to \{resolved_repo}")
    return ()
  }
  // Display issue plan
  if changes.length() > 0 {
    print_line("Issue changes to push to GitHub (\{resolved_repo}):")
    print_line("")
    for change in changes {
      print_push_change(change, "issue")
    }
    print_line("")
  }
  // Display PR plan
  if pr_changes.length() > 0 {
    print_line("PR changes to push to GitHub (\{resolved_repo}):")
    print_line("")
    for change in pr_changes {
      print_push_change(change, "PR")
    }
    print_line("")
  }
  if !options.apply {
    print_line("Run with --apply to execute these changes.")
    return ()
  }
  // Execute issues
  if changes.length() > 0 {
    print_line("Pushing issues to GitHub...")
    let result = @hub_native.github_sync_execute_push(
      hub, objects, refs, clock, resolved_repo, changes,
    )
    print_line("Issue push complete:")
    print_line("  Created: \{result.created}")
    print_line("  Updated: \{result.updated}")
    if result.errors.length() > 0 {
      print_line("  Errors:")
      for err in result.errors {
        print_line("    \{err}")
      }
    }
  }
  // Execute PRs
  if pr_changes.length() > 0 {
    print_line("Pushing PR updates to GitHub...")
    let pr_result = @hub_native.github_sync_execute_push_prs(
      hub, objects, refs, clock, resolved_repo, pr_changes,
    )
    print_line("PR push complete:")
    print_line("  Created: \{pr_result.created}")
    print_line("  Updated: \{pr_result.updated}")
    if pr_result.errors.length() > 0 {
      print_line("  Errors:")
      for err in pr_result.errors {
        print_line("    \{err}")
      }
    }
  }
  // Push comments for synced issues/PRs
  let links = hub.list_sync_links(objects, repo=resolved_repo)
  let mut total_comments_created = 0
  let mut total_comments_updated = 0
  for link in links {
    if link.local_kind() == "issue" || link.local_kind() == "pr" {
      let cr = @hub_native.github_sync_push_comments(
        hub,
        objects,
        refs,
        clock,
        resolved_repo,
        link.local_id(),
        link.remote_number(),
        options.comments_mode,
        options.apply,
      )
      total_comments_created += cr.created()
      total_comments_updated += cr.updated()
      if cr.errors().length() > 0 {
        for err in cr.errors() {
          print_line("  Comment error: \{err}")
        }
      }
    }
  }
  if total_comments_created > 0 || total_comments_updated > 0 {
    print_line("")
    print_line("Comments push complete:")
    print_line("  Created: \{total_comments_created}")
    print_line("  Updated: \{total_comments_updated}")
  }
}

///|
async fn print_push_change(
  change : @hub_native.SyncPushChange,
  kind : String,
) -> Unit raise Error {
  let prefix = if change.is_destructive { "  ! " } else { "  " }
  match change.action {
    "create" => {
      print_line("\{prefix}CREATE \{kind} \"\{change.title}\"")
      if change.labels_change.length() > 0 {
        print_line("    labels: \{change.labels_change}")
      }
    }
    "update" => {
      print_line(
        "\{prefix}UPDATE \{kind} #\{change.remote_number} \"\{change.title}\"",
      )
      if change.state_change.length() > 0 {
        print_line("    state: \{change.state_change}")
      }
      if change.title_change.length() > 0 {
        print_line("    title: \{change.title_change}")
      }
      if change.labels_change.length() > 0 {
        print_line("    labels: \{change.labels_change}")
      }
    }
    _ => ()
  }
}

///|
async fn handle_hub_issue_sync_install_hook(
  _args : Array[String],
) -> Unit raise Error {
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let fs = OsFs::new()
  let hooks_dir = git_dir + "/hooks"
  let hook_path = hooks_dir + "/post-push"
  // Detect repo from git remote
  let repo = resolve_github_repo(fs, git_dir, None)
  let hook_line = "bit issue sync push --github --repo \"\{repo}\" --apply --comments append-only\n"
  let hook_content = "#!/bin/sh\n# Auto-sync bit issues to GitHub after push\n" +
    hook_line
  // Check if hook already exists
  if fs.is_file(hook_path) {
    let existing = decode_bytes(
      fs.read_file(hook_path) catch {
        _ => Default::default()
      },
    )
    if existing.contains("bit issue sync") {
      print_line("Hook already installed at \{hook_path}")
      return ()
    }
    // Append to existing hook
    fs.write_string(hook_path, existing.to_string() + "\n" + hook_line) catch {
      err => {
        eprint_line("Error writing hook: \{err}")
        @sys.exit(1)
      }
    }
    print_line("Appended sync hook to \{hook_path}")
  } else {
    fs.mkdir_p(hooks_dir) catch {
      _ => ()
    }
    fs.write_string(hook_path, hook_content) catch {
      err => {
        eprint_line("Error writing hook: \{err}")
        @sys.exit(1)
      }
    }
    print_line("Created sync hook at \{hook_path}")
  }
  // Make executable
  let _ = @process.collect_output("chmod", ["+x", hook_path], inherit_env=true)
}

///|
async fn handle_hub_issue_sync_bidirectional(
  args : Array[String],
) -> Unit raise Error {
  let options = parse_hub_issue_sync_bidirectional_options(args)
  if options.help {
    print_hub_issue_sync_usage()
    return ()
  }
  if !options.github_flag {
    eprint_line("Error: --github flag required for bidirectional sync")
    eprint_line(
      "Usage: git-bit issue sync --github --repo  [--apply]",
    )
    @sys.exit(1)
  }
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let fs = OsFs::new()
  let resolved_repo = resolve_github_repo(fs, git_dir, options.repo)
  if !options.force_remote {
    let can_push = @hub_native.github_check_repo_push_permission(resolved_repo) catch {
      _ => false
    }
    if !can_push {
      eprint_line(
        "Error: bidirectional sync requires GitHub write access to the remote repository",
      )
      eprint_line(
        "Repository '\{resolved_repo}' is not writable by the authenticated GitHub user",
      )
      eprint_line("Use --force-remote to override (not recommended)")
      @sys.exit(1)
    }
  }
  let (objects, refs, clock) = make_hub_stores(git_dir)
  let hub = load_hub_store(objects, refs)
  // Phase 1: Pull (always executes)
  print_line("=== Pull (GitHub → bit) ===")
  print_line("Pulling issues from GitHub \{resolved_repo}...")
  let pull_result = @hub_native.github_sync_pull_issues(
    hub,
    objects,
    refs,
    clock,
    resolved_repo,
    options.since,
    options.state,
    options.conflict,
  )
  print_line("Issues pull complete:")
  print_line("  Created: \{pull_result.created}")
  print_line("  Updated: \{pull_result.updated}")
  print_line("  Skipped: \{pull_result.skipped}")
  if pull_result.conflicts.length() > 0 {
    print_line("  Conflicts (manual review needed):")
    for c in pull_result.conflicts {
      print_line("    \{c}")
    }
  }
  // Pull comments
  let links = hub.list_sync_links(objects, repo=resolved_repo)
  let mut total_pull_comments_created = 0
  let mut total_pull_comments_updated = 0
  let mut total_pull_comments_deleted = 0
  for link in links {
    if link.local_kind() == "issue" || link.local_kind() == "pr" {
      let cr = @hub_native.github_sync_pull_comments(
        hub,
        objects,
        refs,
        clock,
        resolved_repo,
        link.local_id(),
        link.remote_number(),
        options.comments_mode,
      )
      total_pull_comments_created += cr.created()
      total_pull_comments_updated += cr.updated()
      total_pull_comments_deleted += cr.deleted()
      if cr.errors().length() > 0 {
        for err in cr.errors() {
          print_line("  Comment error: \{err}")
        }
      }
    }
  }
  if total_pull_comments_created > 0 ||
    total_pull_comments_updated > 0 ||
    total_pull_comments_deleted > 0 {
    print_line("")
    print_line("Comments pull complete:")
    print_line("  Created: \{total_pull_comments_created}")
    print_line("  Updated: \{total_pull_comments_updated}")
    print_line("  Deleted: \{total_pull_comments_deleted}")
  }
  if options.include_prs {
    print_line("")
    print_line("Pulling PRs from GitHub \{resolved_repo}...")
    let pr_result = @hub_native.github_sync_pull_prs(
      hub,
      objects,
      refs,
      clock,
      resolved_repo,
      options.since,
      options.state,
      options.conflict,
    )
    print_line("PRs pull complete:")
    print_line("  Created: \{pr_result.created}")
    print_line("  Updated: \{pr_result.updated}")
    print_line("  Skipped: \{pr_result.skipped}")
    if pr_result.conflicts.length() > 0 {
      print_line("  Conflicts (manual review needed):")
      for c in pr_result.conflicts {
        print_line("    \{c}")
      }
    }
  }
  // Phase 2: Push
  print_line("")
  print_line("=== Push (bit → GitHub) ===")
  let changes = @hub_native.github_sync_plan_push(hub, objects, resolved_repo)
  let pr_changes = if options.include_prs {
    @hub_native.github_sync_plan_push_prs(hub, objects, resolved_repo)
  } else {
    []
  }
  if changes.length() == 0 && pr_changes.length() == 0 {
    print_line("Nothing to push to \{resolved_repo}")
    return ()
  }
  if changes.length() > 0 {
    print_line("Issue changes to push to GitHub (\{resolved_repo}):")
    print_line("")
    for change in changes {
      print_push_change(change, "issue")
    }
    print_line("")
  }
  if pr_changes.length() > 0 {
    print_line("PR changes to push to GitHub (\{resolved_repo}):")
    print_line("")
    for change in pr_changes {
      print_push_change(change, "PR")
    }
    print_line("")
  }
  if !options.apply {
    print_line("Run with --apply to execute push changes.")
    return ()
  }
  // Execute push
  if changes.length() > 0 {
    print_line("Pushing issues to GitHub...")
    let result = @hub_native.github_sync_execute_push(
      hub, objects, refs, clock, resolved_repo, changes,
    )
    print_line("Issue push complete:")
    print_line("  Created: \{result.created}")
    print_line("  Updated: \{result.updated}")
    if result.errors.length() > 0 {
      print_line("  Errors:")
      for err in result.errors {
        print_line("    \{err}")
      }
    }
  }
  if pr_changes.length() > 0 {
    print_line("Pushing PR updates to GitHub...")
    let pr_result = @hub_native.github_sync_execute_push_prs(
      hub, objects, refs, clock, resolved_repo, pr_changes,
    )
    print_line("PR push complete:")
    print_line("  Created: \{pr_result.created}")
    print_line("  Updated: \{pr_result.updated}")
    if pr_result.errors.length() > 0 {
      print_line("  Errors:")
      for err in pr_result.errors {
        print_line("    \{err}")
      }
    }
  }
  // Push comments
  let push_links = hub.list_sync_links(objects, repo=resolved_repo)
  let mut total_push_comments_created = 0
  let mut total_push_comments_updated = 0
  for link in push_links {
    if link.local_kind() == "issue" || link.local_kind() == "pr" {
      let cr = @hub_native.github_sync_push_comments(
        hub,
        objects,
        refs,
        clock,
        resolved_repo,
        link.local_id(),
        link.remote_number(),
        options.comments_mode,
        options.apply,
      )
      total_push_comments_created += cr.created()
      total_push_comments_updated += cr.updated()
      if cr.errors().length() > 0 {
        for err in cr.errors() {
          print_line("  Comment error: \{err}")
        }
      }
    }
  }
  if total_push_comments_created > 0 || total_push_comments_updated > 0 {
    print_line("")
    print_line("Comments push complete:")
    print_line("  Created: \{total_push_comments_created}")
    print_line("  Updated: \{total_push_comments_updated}")
  }
}

///|
async fn handle_hub_issue_sync_status(args : Array[String]) -> Unit raise Error {
  let mut repo : String? = None
  let mut i = 0
  while i < args.length() {
    let arg = args[i]
    if arg == "--repo" && i + 1 < args.length() {
      i += 1
      repo = Some(args[i])
    } else if arg.has_prefix("--repo=") {
      repo = Some(String::unsafe_substring(arg, start=7, end=arg.length()))
    } else if arg == "-h" || arg == "--help" {
      print_hub_issue_sync_usage()
      return ()
    }
    i += 1
  }
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let fs = OsFs::new()
  let resolved_repo = resolve_github_repo(fs, git_dir, repo)
  let (objects, refs, _clock) = make_hub_stores(git_dir)
  let hub = load_hub_store(objects, refs)
  let links = hub.list_sync_links(objects, repo=resolved_repo)
  if links.length() == 0 {
    print_line("No sync links found for \{resolved_repo}")
    print_line("")
    print_line(
      "Run `git-bit issue sync pull --repo \{resolved_repo}` to pull issues from GitHub.",
    )
    return ()
  }
  print_line("Sync status for \{resolved_repo}")
  print_line("  Linked items: \{links.length()}")
  print_line("")
  // Show recent sync links (up to 20)
  let limit = if links.length() > 20 { 20 } else { links.length() }
  for idx = 0; idx < limit; idx = idx + 1 {
    let link = links[idx]
    let kind = link.local_kind()
    let num = link.remote_number()
    let local_id_short = if link.local_id().length() > 8 {
      String::unsafe_substring(link.local_id(), start=0, end=8)
    } else {
      link.local_id()
    }
    let pull_ts = link.last_pull_at()
    let push_ts = link.last_push_at()
    let pull_str = if pull_ts > 0L {
      format_timestamp_iso(pull_ts, "+0000")
    } else {
      "never"
    }
    let push_str = if push_ts > 0L {
      format_timestamp_iso(push_ts, "+0000")
    } else {
      "never"
    }
    print_line(
      "  \{kind} #\{num} <-> \{local_id_short}  pulled: \{pull_str}  pushed: \{push_str}",
    )
  }
  if links.length() > limit {
    print_line("  ... and \{links.length() - limit} more")
  }
}

///|
fn github_sync_write_enabled() -> Bool {
  match @env.get_env_var("BIT_GITHUB_SYNC_WRITE") {
    Some(v) => v == "1" || v == "true"
    None => false
  }
}

///|
async fn github_sync_write_disabled_message(
  command : String,
) -> Unit raise Error {
  eprint_line(
    "Error: 'bit issue sync \{command}' is currently disabled (writes to GitHub)",
  )
  eprint_line("")
  eprint_line("Read-only commands are available:")
  eprint_line("  bit issue sync pull --repo    (GitHub → bit)")
  eprint_line("  bit issue sync status --repo ")
  eprint_line("")
  eprint_line("To enable write operations, set: export BIT_GITHUB_SYNC_WRITE=1")
  @sys.exit(1)
}