///|
/// Repository path parsed from URL or shorthand
pub struct RepoPath {
  /// Host (None = github.com implicit, Some("github.com") = explicit but normalized)
  host : String?
  /// Username or organization
  user : String
  /// Repository name
  repo : String
  /// Subdirectory for sparse checkout (optional)
  subdir : String?
} derive(Debug, Eq)

///|
fn hq_show_string(value : String) -> String {
  let buf = StringBuilder::new()
  buf.write_char('"')
  for c in value {
    if c == '"' {
      buf.write_string("\\\"")
    } else if c == '\\' {
      buf.write_string("\\\\")
    } else if c == '\n' {
      buf.write_string("\\n")
    } else if c == '\r' {
      buf.write_string("\\r")
    } else if c == '\t' {
      buf.write_string("\\t")
    } else {
      buf.write_char(c)
    }
  }
  buf.write_char('"')
  buf.to_string()
}

///|
fn hq_show_string_option(value : String?) -> String {
  match value {
    Some(s) => "Some(" + hq_show_string(s) + ")"
    None => "None"
  }
}

///|
pub impl Show for RepoPath with fn output(self, logger) {
  logger.write_string(
    "{host: " +
    hq_show_string_option(self.host) +
    ", user: " +
    hq_show_string(self.user) +
    ", repo: " +
    hq_show_string(self.repo) +
    ", subdir: " +
    hq_show_string_option(self.subdir) +
    "}",
  )
}

///|
pub fn RepoPath::new(
  user : String,
  repo : String,
  host? : String? = None,
  subdir? : String? = None,
) -> RepoPath {
  { host, user, repo, subdir }
}

///|
pub fn RepoPath::full_name(self : RepoPath) -> String {
  "\{self.user}/\{self.repo}"
}

///|
pub fn RepoPath::host_name(self : RepoPath) -> String {
  self.host.unwrap_or("github.com")
}

///|
/// Get the clone URL (always includes host)
pub fn RepoPath::clone_url(self : RepoPath, ssh? : Bool = false) -> String {
  let host = self.host.unwrap_or("github.com")
  if ssh {
    "git@\{host}:\{self.user}/\{self.repo}.git"
  } else {
    "https://\{host}/\{self.user}/\{self.repo}.git"
  }
}

///|
/// Get the local path under bhq root
/// github.com is normalized (no host in path)
pub fn RepoPath::local_path(self : RepoPath, root : String) -> String {
  match self.host {
    None | Some("github.com") => "\{root}/\{self.user}/\{self.repo}"
    Some(host) => "\{root}/\{host}/\{self.user}/\{self.repo}"
  }
}

///|
/// Check if this repo requires sparse checkout
pub fn RepoPath::needs_sparse(self : RepoPath) -> Bool {
  self.subdir is Some(_)
}

///|
/// Configuration for hq command
pub struct HqConfig {
  /// Primary root directory
  root : String
  /// Whether to use ghq compatibility mode
  ghq_compat : Bool
}

///|
pub fn HqConfig::new(root : String, ghq_compat? : Bool = false) -> HqConfig {
  { root, ghq_compat }
}

///|
/// Options for hq get command
pub struct HqGetOptions {
  /// Update if already exists (git pull)
  update : Bool
  /// Use shallow clone (--depth=1)
  shallow : Bool
  /// Explicit clone depth. 0 means use default behavior.
  depth : Int
  /// Use SSH protocol
  ssh : Bool
  /// Specific branch to clone
  branch : String?
  /// Unshallow existing repository before update
  unshallow : Bool
}

///|
pub fn HqGetOptions::default() -> HqGetOptions {
  {
    update: false,
    shallow: false,
    depth: 0,
    ssh: false,
    branch: None,
    unshallow: false,
  }
}

///|
pub fn HqGetOptions::new(
  update? : Bool = false,
  shallow? : Bool = false,
  depth? : Int = 0,
  ssh? : Bool = false,
  branch? : String? = None,
  unshallow? : Bool = false,
) -> HqGetOptions {
  { update, shallow, depth, ssh, branch, unshallow }
}

///|
/// Result of hq get operation
pub(all) enum HqGetResult {
  Cloned(String) // Successfully cloned to path
  Updated(String) // Successfully updated existing repo
  Skipped(String) // Already exists, no update requested
  Error(String) // Error message
} derive(Debug, Eq)

///|
pub impl Show for HqGetResult with fn output(self, logger) {
  match self {
    Cloned(path) => logger.write_string("Cloned(" + hq_show_string(path) + ")")
    Updated(path) =>
      logger.write_string("Updated(" + hq_show_string(path) + ")")
    Skipped(path) =>
      logger.write_string("Skipped(" + hq_show_string(path) + ")")
    Error(message) =>
      logger.write_string("Error(" + hq_show_string(message) + ")")
  }
}