///|
/// Parse "owner/repo#issue-id" or "#issue-id" or bare "issue-id".
/// Returns (repo_ref, issue_id) where repo_ref is "" for local refs.
fn parse_cross_repo_ref(input : String) -> (String, String) {
  match input.find("#") {
    Some(idx) => {
      let repo_ref = String::unsafe_substring(input, start=0, end=idx)
      let issue_id = String::unsafe_substring(
        input,
        start=idx + 1,
        end=input.length(),
      )
      (repo_ref, issue_id)
    }
    None => ("", input)
  }
}

///|
/// Resolve "owner/repo" or named alias to local filesystem path.
/// Resolution order: alias map → ghq path → error
fn resolve_repo_ref_to_path(
  repo_ref : String,
  home : String,
  aliases? : Map[String, String] = Map([]),
) -> String raise Error {
  // 1. Check alias map
  match aliases.get(repo_ref) {
    Some(path) => return path
    None => ()
  }
  // 2. Try ghq path (owner/repo format)
  if repo_ref.find("/") is Some(_) {
    // Security: reject path traversal
    if repo_ref.contains("..") {
      raise @bitcore.GitError::InvalidObject(
        "Invalid repo ref '\{repo_ref}': path traversal not allowed",
      )
    }
    let ghq_path = home + "/ghq/github.com/" + repo_ref
    return ghq_path
  }
  // 3. Error
  raise @bitcore.GitError::InvalidObject(
    "Cannot resolve repo ref '\{repo_ref}': not an alias and not in owner/repo format",
  )
}

///|
fn hub_remotes_path(git_dir : String) -> String {
  git_dir + "/hub/remotes"
}

///|
/// Serialize alias entries to TSV format: "name\tpath\n" per line.
fn serialize_alias_entries(entries : Array[(String, String)]) -> String {
  let buf = StringBuilder()
  for entry in entries {
    let (name, path) = entry
    buf.write_string(name)
    buf.write_char('\t')
    buf.write_string(path)
    buf.write_char('\n')
  }
  buf.to_string()
}

///|
/// Parse alias entries from text format. Ignores blank lines and lines starting with #.
fn parse_alias_entries(content : String) -> Array[(String, String)] {
  let result : Array[(String, String)] = []
  if content.length() == 0 {
    return result
  }
  for line_view in content.split("\n") {
    let trimmed = line_view.to_owned().trim().to_owned()
    if trimmed.length() == 0 || trimmed.has_prefix("#") {
      continue
    }
    // Split on first tab
    match trimmed.find("\t") {
      Some(idx) => {
        let a = String::unsafe_substring(trimmed, start=0, end=idx)
        let p = String::unsafe_substring(
            trimmed,
            start=idx + 1,
            end=trimmed.length(),
          )
          .trim()
          .to_owned()
        if a.length() > 0 && p.length() > 0 {
          result.push((a, p))
        }
      }
      None => () // skip lines without space separator
    }
  }
  result
}

///|
/// Load aliases from git_dir/hub/remotes file.
fn load_aliases(git_dir : String) -> Map[String, String] {
  let fs = OsFs::new()
  let path = hub_remotes_path(git_dir)
  if !fs.is_file(path) {
    return Map([])
  }
  let bytes = fs.read_file(path) catch { _ => return Map([]) }
  let content = @utf8.decode_lossy(bytes[:]).to_string()
  let entries = parse_alias_entries(content)
  let map : Map[String, String] = Map([])
  for entry in entries {
    let (name, path) = entry
    map[name] = path
  }
  map
}

///|
/// Save aliases to git_dir/hub/remotes file.
fn save_aliases(
  git_dir : String,
  aliases : Map[String, String],
) -> Unit raise Error {
  let fs = OsFs::new()
  let path = hub_remotes_path(git_dir)
  let dir = match path.rev_find("/") {
    Some(idx) => String::unsafe_substring(path, start=0, end=idx)
    None => "."
  }
  fs.mkdir_p(dir)
  let entries : Array[(String, String)] = []
  aliases.each(fn(k, v) { entries.push((k, v)) })
  // Sort for deterministic output
  entries.sort_by(fn(a, b) { a.0.compare(b.0) })
  let content = serialize_alias_entries(entries)
  fs.write_string(path, content)
}

///|
async fn handle_hub_issue_remote(args : Array[String]) -> Unit raise Error {
  if args.length() == 0 {
    println("Usage: git-bit issue remote ")
    println("")
    println("Subcommands:")
    println("  add      Add a remote alias")
    println("  list                  List remote aliases")
    println("  remove         Remove a remote alias")
    return
  }
  let subcmd = args[0]
  let rest = collect_args(args, 1)
  match subcmd {
    "add" => handle_hub_issue_remote_add(rest)
    "list" | "ls" => handle_hub_issue_remote_list(rest)
    "remove" | "rm" => handle_hub_issue_remote_remove(rest)
    _ => {
      eprint_line("git-bit issue remote: unknown subcommand '\{subcmd}'")
      @sys.exit(1)
    }
  }
}

///|
async fn handle_hub_issue_remote_add(args : Array[String]) -> Unit raise Error {
  if args.length() < 2 {
    eprint_line("Usage: git-bit issue remote add  ")
    @sys.exit(1)
  }
  let name = args[0]
  let target = args[1]
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let aliases = load_aliases(git_dir)
  // Resolve target: if it looks like owner/repo, resolve to ghq path
  let home = @env.get_env_var("HOME").unwrap_or("/")
  let resolved_path = if target.has_prefix("/") {
    target
  } else {
    resolve_repo_ref_to_path(target, home) catch {
      _ => target
    }
  }
  aliases[name] = resolved_path
  save_aliases(git_dir, aliases)
  print_line("Added remote alias '\{name}' -> \{resolved_path}")
}

///|
async fn handle_hub_issue_remote_list(
  _args : Array[String],
) -> Unit raise Error {
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let aliases = load_aliases(git_dir)
  if aliases.length() == 0 {
    print_line("No remote aliases configured.")
    return
  }
  let entries : Array[(String, String)] = []
  aliases.each(fn(k, v) { entries.push((k, v)) })
  entries.sort_by(fn(a, b) { a.0.compare(b.0) })
  for entry in entries {
    let (name, path) = entry
    print_line("\{name}\t\{path}")
  }
}

///|
async fn handle_hub_issue_remote_remove(
  args : Array[String],
) -> Unit raise Error {
  if args.length() < 1 {
    eprint_line("Usage: git-bit issue remote remove ")
    @sys.exit(1)
  }
  let name = args[0]
  let root = get_work_root()
  let git_dir = resolve_hub_git_dir(root)
  let aliases = load_aliases(git_dir)
  match aliases.get(name) {
    Some(_) => {
      aliases.remove(name)
      save_aliases(git_dir, aliases)
      print_line("Removed remote alias '\{name}'")
    }
    None => {
      eprint_line("git-bit issue remote: alias '\{name}' not found")
      @sys.exit(1)
    }
  }
}