///| Ignore integration for git worktree operations (async, all targets)

///|
/// List files under root, excluding ignored paths and .git.
pub async fn list_working_files_async(
  fs : &@bit.RepoFileSystem,
  root : String,
) -> Array[String] raise @bit.GitError {
  let matcher = @ignore.Matcher::new()
  let out : Array[String] = []
  walk_dir_async(fs, root, "", matcher, out)
  out
}

///|
pub fn working_files_cache(root : String) -> Array[String]? noraise {
  ignore(root)
  None
}

///|
async fn walk_dir_async(
  fs : &@bit.RepoFileSystem,
  root : String,
  rel : String,
  matcher : @ignore.Matcher,
  out : Array[String],
) -> Unit raise @bit.GitError {
  let dir = if rel == "" { root } else { @bit.join_path(root, rel) }
  // Skip submodules (directories containing .git/.bit file or directory)
  if rel != "" {
    let dot_git_marker = @bit.join_path(dir, ".git")
    let bit_marker = @bit.join_path(dir, ".bit")
    if fs.is_file(dot_git_marker) || fs.is_file(bit_marker) {
      return ()
    }
  }
  // Use native readdir_typed if available to avoid per-entry stat syscalls
  let typed = @bitio.readdir_typed(dir)
  match typed {
    Some(typed_entries) =>
      walk_dir_async_typed(fs, root, rel, dir, matcher, out, typed_entries)
    None => walk_dir_async_fallback(fs, root, rel, dir, matcher, out)
  }
}

///|
async fn walk_dir_async_typed(
  fs : &@bit.RepoFileSystem,
  root : String,
  rel : String,
  dir : String,
  matcher : @ignore.Matcher,
  out : Array[String],
  typed_entries : Array[(String, Int)],
) -> Unit raise @bit.GitError {
  let mut has_git = false
  let mut has_gitignore = false
  for entry in typed_entries {
    let name = entry.0
    if name == ".git" || name == ".bit" {
      has_git = true
    }
    if name == ".gitignore" {
      has_gitignore = true
    }
    if has_git && has_gitignore {
      break
    }
  }
  if rel != "" && has_git {
    return ()
  }
  let prev_len = matcher.len()
  if has_gitignore {
    let ignore_path = @bit.join_path(dir, ".gitignore")
    let content = @utf8.decode_lossy(fs.read_file(ignore_path)[:])
    matcher.add_rules(rel, content)
  }
  for entry in typed_entries {
    let name = entry.0
    let d_type = entry.1
    if name == ".git" || name == ".bit" || name == ".jj" {
      continue
    }
    let child_rel = if rel == "" { name } else { rel + "/" + name }
    // d_type: 4=DT_DIR, 8=DT_REG, 10=DT_LNK, 0=DT_UNKNOWN
    let is_dir = if d_type == 4 {
      true
    } else if d_type == 10 {
      // Symlink: check if it points to a directory
      let child_path = @bit.join_path(root, child_rel)
      fs.is_dir(child_path) &&
      !(@bitio.read_symlink_target_path(child_path) is Some(_))
    } else if d_type == 0 {
      // DT_UNKNOWN: fall back to stat
      let child_path = @bit.join_path(root, child_rel)
      if fs.is_dir(child_path) {
        !(@bitio.read_symlink_target_path(child_path) is Some(_))
      } else {
        false
      }
    } else {
      false
    }
    if matcher.is_ignored(child_rel, is_dir) {
      if is_dir && matcher.has_negation() {
        walk_dir_async(fs, root, child_rel, matcher, out)
      }
      continue
    }
    if is_dir {
      walk_dir_async(fs, root, child_rel, matcher, out)
    } else {
      out.push(child_rel)
    }
  }
  matcher.truncate(prev_len)
}

///|
async fn walk_dir_async_fallback(
  fs : &@bit.RepoFileSystem,
  root : String,
  rel : String,
  dir : String,
  matcher : @ignore.Matcher,
  out : Array[String],
) -> Unit raise @bit.GitError {
  let entries = fs.readdir(dir)
  let mut has_git = false
  let mut has_gitignore = false
  for name in entries {
    if name == ".git" || name == ".bit" {
      has_git = true
    }
    if name == ".gitignore" {
      has_gitignore = true
    }
    if has_git && has_gitignore {
      break
    }
  }
  if rel != "" && has_git {
    return ()
  }
  let prev_len = matcher.len()
  if has_gitignore {
    let ignore_path = @bit.join_path(dir, ".gitignore")
    let content = @utf8.decode_lossy(fs.read_file(ignore_path)[:])
    matcher.add_rules(rel, content)
  }
  for name in entries {
    if name == "." ||
      name == ".." ||
      name == ".git" ||
      name == ".bit" ||
      name == ".jj" {
      continue
    }
    let child_rel = if rel == "" { name } else { rel + "/" + name }
    let child_path = @bit.join_path(root, child_rel)
    let is_dir = if fs.is_dir(child_path) {
      !(@bitio.read_symlink_target_path(child_path) is Some(_))
    } else {
      false
    }
    if matcher.is_ignored(child_rel, is_dir) {
      if is_dir && matcher.has_negation() {
        walk_dir_async(fs, root, child_rel, matcher, out)
      }
      continue
    }
    if is_dir {
      walk_dir_async(fs, root, child_rel, matcher, out)
    } else {
      out.push(child_rel)
    }
  }
  matcher.truncate(prev_len)
}