///| Transport utilities (remote parsing)

///|
pub(all) enum RemoteKind {
  Http
  Ssh
  File
} derive(Eq)

///|
pub struct RemoteSpec {
  kind : RemoteKind
  raw : String
  host : String?
  path : String
  base_url : String?
}

///|
pub fn parse_remote(remote : String) -> RemoteSpec {
  if remote.has_prefix("http://") || remote.has_prefix("https://") {
    let base = normalize_http_url(remote)
    return {
      kind: RemoteKind::Http,
      raw: remote,
      host: None,
      path: "",
      base_url: Some(base),
    }
  }
  if remote.has_prefix("ssh://") {
    return parse_ssh_url(remote)
  }
  if remote.has_prefix("file://") {
    let path = String::unsafe_substring(remote, start=7, end=remote.length())
    return {
      kind: RemoteKind::File,
      raw: remote,
      host: None,
      path,
      base_url: None,
    }
  }
  if remote.has_prefix("[") {
    match remote.find("]:") {
      Some(idx) => {
        let host = String::unsafe_substring(remote, start=1, end=idx)
        let path = String::unsafe_substring(
          remote,
          start=idx + 2,
          end=remote.length(),
        )
        if host.length() > 0 && path.length() > 0 && !host.contains("/") {
          return {
            kind: RemoteKind::Ssh,
            raw: remote,
            host: Some(host),
            path,
            base_url: None,
          }
        }
      }
      None => ()
    }
  }
  // scp-style: [user@]host:path
  let colon = remote.find(":")
  if colon is Some(idx) && !remote.has_prefix("/") {
    let host = String::unsafe_substring(remote, start=0, end=idx)
    let path = String::unsafe_substring(
      remote,
      start=idx + 1,
      end=remote.length(),
    )
    if host.length() > 0 && path.length() > 0 && !host.contains("/") {
      return {
        kind: RemoteKind::Ssh,
        raw: remote,
        host: Some(host),
        path,
        base_url: None,
      }
    }
  }
  // default: local path
  {
    kind: RemoteKind::File,
    raw: remote,
    host: None,
    path: remote,
    base_url: None,
  }
}

///|
/// Convert SSH-style remote URL into HTTPS smart transport URL.
/// Returns None for non-SSH remotes.
pub fn ssh_remote_to_https(remote : String) -> String? {
  let spec = parse_remote(remote)
  if spec.kind != RemoteKind::Ssh {
    return None
  }
  guard spec.host is Some(raw_host) else { return None }
  let host = normalize_ssh_host_for_https(raw_host)
  let path = normalize_ssh_path_for_https(spec.path)
  if host.length() == 0 || path.length() == 0 {
    return None
  }
  Some("https://" + host + "/" + path)
}

///|
fn normalize_http_url(url : String) -> String {
  if url.has_suffix(".git") {
    String::unsafe_substring(url, start=0, end=url.length() - 4)
  } else {
    url
  }
}

///|
fn normalize_ssh_host_for_https(raw_host : String) -> String {
  let host_with_port = match raw_host.rev_find("@") {
    Some(idx) =>
      String::unsafe_substring(raw_host, start=idx + 1, end=raw_host.length())
    None => raw_host
  }
  if host_with_port.has_prefix("[") {
    match host_with_port.find("]") {
      Some(idx) => String::unsafe_substring(host_with_port, start=1, end=idx)
      None => host_with_port
    }
  } else {
    match host_with_port.find(":") {
      Some(idx) => String::unsafe_substring(host_with_port, start=0, end=idx)
      None => host_with_port
    }
  }
}

///|
fn normalize_ssh_path_for_https(raw_path : String) -> String {
  let mut start = 0
  while start < raw_path.length() && raw_path[start] == '/' {
    start += 1
  }
  String::unsafe_substring(raw_path, start~, end=raw_path.length())
}

///|
fn parse_ssh_url(remote : String) -> RemoteSpec {
  // ssh://[user@]host[:port]/path
  let rest = String::unsafe_substring(remote, start=6, end=remote.length())
  let slash = rest.find("/")
  match slash {
    None =>
      {
        kind: RemoteKind::Ssh,
        raw: remote,
        host: Some(rest),
        path: "",
        base_url: None,
      }
    Some(idx) => {
      let authority = String::unsafe_substring(rest, start=0, end=idx)
      let host = if authority.has_suffix(":") {
        String::unsafe_substring(authority, start=0, end=authority.length() - 1)
      } else {
        authority
      }
      let path = "/" +
        String::unsafe_substring(rest, start=idx + 1, end=rest.length())
      {
        kind: RemoteKind::Ssh,
        raw: remote,
        host: Some(host),
        path,
        base_url: None,
      }
    }
  }
}