///|
/// Failures while resolving framework-owned application paths.
pub(all) suberror AppPathError {
  InvalidIdentifier(identifier~ : String)
  MissingHomeDirectory(platform~ : String)
  PlatformProbe(@native.NativeError)
} derive(Debug, Eq)

///|
pub fn AppPathError::message(self : AppPathError) -> String {
  match self {
    InvalidIdentifier(identifier~) =>
      "invalid application identifier: " + identifier
    MissingHomeDirectory(platform~) =>
      "cannot resolve the application data directory on " + platform
    PlatformProbe(error) =>
      "cannot determine the Proton platform: " + error.message()
  }
}

///|
/// Resolves the stable per-application directory for native persistent data.
///
/// The identifier should match the packaged bundle/application identifier.
/// This function resolves the path but does not create the directory.
pub fn app_data_dir(identifier : String) -> String raise AppPathError {
  let info = @native.runtime_info() catch {
    error => raise PlatformProbe(error)
  }
  let platform = info.platform
  app_data_dir_for_platform(
    platform,
    identifier,
    @env.get_env_var("HOME"),
    @env.get_env_var("LOCALAPPDATA"),
    @env.get_env_var("XDG_DATA_HOME"),
  )
}

///|
fn browser_data_dir(identifier : String) -> String raise AppPathError {
  @mbpath.Path(app_data_dir(identifier)).join("browser").normalize().to_string()
}

///|
fn app_data_dir_for_platform(
  platform : String,
  identifier : String,
  home : String?,
  local_app_data : String?,
  xdg_data_home : String?,
) -> String raise AppPathError {
  validate_app_identifier(identifier)
  let base = match platform {
    "macos" =>
      @mbpath.Path(require_path_base(home, platform)).join(
        "Library/Application Support",
      )
    "windows" => @mbpath.Path(require_path_base(local_app_data, platform))
    "linux" =>
      match non_empty_path(xdg_data_home) {
        Some(path) => @mbpath.Path(path)
        None =>
          @mbpath.Path(require_path_base(home, platform)).join(".local/share")
      }
    _ => raise MissingHomeDirectory(platform~)
  }
  base.join(identifier).normalize().to_string()
}

///|
fn validate_app_identifier(identifier : String) -> Unit raise AppPathError {
  let value = identifier.trim().to_owned()
  if value == "" ||
    value == "." ||
    value == ".." ||
    value.contains("/") ||
    value.contains("\\") ||
    value.contains("\u0000") {
    raise InvalidIdentifier(identifier~)
  }
}

///|
fn non_empty_path(value : String?) -> String? {
  match value {
    Some(path) => {
      let path = path.trim().to_owned()
      if path == "" {
        None
      } else {
        Some(path)
      }
    }
    None => None
  }
}

///|
fn require_path_base(
  value : String?,
  platform : String,
) -> String raise AppPathError {
  match non_empty_path(value) {
    Some(path) => path
    None => raise MissingHomeDirectory(platform~)
  }
}