using PalladiumWallet.Core.Chain;
namespace PalladiumWallet.Core.Storage;
///
/// Per-platform data paths (blueprint §8). The data root can be:
/// 1. portable: "palladium-data" folder next to the executable;
/// 2. custom: chosen by the user at first launch and stored in
/// a small "pointer" file at a fixed bootstrap location;
/// 3. legacy: old location (%APPDATA%/PalladiumWallet) if it already holds data;
/// 4. default: ~/.PalladiumWallet (Linux/macOS) or %ProgramFiles%\PalladiumWallet (Windows).
/// Under the root there is a per-network subfolder (config, wallet, header, certificates).
///
public static class AppPaths
{
public const string PortableDirName = "palladium-data";
/// Application folder name, used in the various paths.
public const string AppDirName = "PalladiumWallet";
/// Explicit override of the data root (e.g. CLI --data-dir). Takes priority over everything.
public static string? OverrideDataRoot { get; set; }
///
/// Default data root, following each platform's convention:
/// Windows → %APPDATA%\PalladiumWallet (PascalCase, like Electrum/Bitcoin);
/// Linux/macOS → ~/.palladium-wallet (lowercase dotfolder, like ~/.bitcoin).
/// Per-user and always writable, without administrator privileges.
///
public static string DefaultDataRoot()
{
if (OperatingSystem.IsWindows())
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
AppDirName);
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
return Path.Combine(home, ".palladium-wallet");
}
/// Pointer file to the user-chosen data root. Lives at a
/// bootstrap location that is always writable and independent of the data root.
private static string LocationPointerPath() =>
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
AppDirName, "data-location");
private static string PortableRoot() =>
Path.Combine(AppContext.BaseDirectory, PortableDirName);
private static bool HasData(string root) =>
Directory.Exists(root) && Directory.EnumerateFileSystemEntries(root).Any();
/// Effective data root, following the precedence order documented on the class.
public static string DataRoot()
{
if (!string.IsNullOrEmpty(OverrideDataRoot))
return OverrideDataRoot;
var portable = PortableRoot();
if (Directory.Exists(portable))
return portable;
if (ReadPointer() is { } custom)
return custom;
return DefaultDataRoot();
}
///
/// true if the data location is already determined and need not be asked
/// of the user: portable mode, override, pointer already written, or
/// data already present at the default location.
///
public static bool IsDataLocationConfigured() =>
!string.IsNullOrEmpty(OverrideDataRoot)
|| Directory.Exists(PortableRoot())
|| ReadPointer() is not null
|| HasData(DefaultDataRoot());
/// Stores the user-chosen data root and creates it on disk.
public static void ConfigureDataLocation(string root)
{
root = Path.GetFullPath(root.Trim());
Directory.CreateDirectory(root);
var pointer = LocationPointerPath();
Directory.CreateDirectory(Path.GetDirectoryName(pointer)!);
File.WriteAllText(pointer, root);
}
private static string? ReadPointer()
{
var pointer = LocationPointerPath();
if (!File.Exists(pointer))
return null;
var path = File.ReadAllText(pointer).Trim();
return string.IsNullOrEmpty(path) ? null : path;
}
/// Network data folder (config, wallet, header, certificates).
public static string ForNetwork(NetKind net)
{
var dir = Path.Combine(DataRoot(), ChainProfiles.For(net).NetName);
Directory.CreateDirectory(dir);
return dir;
}
public static string WalletsDir(NetKind net)
{
var dir = Path.Combine(ForNetwork(net), "wallets");
Directory.CreateDirectory(dir);
return dir;
}
public static string DefaultWalletPath(NetKind net) =>
Path.Combine(WalletsDir(net), "default.wallet.json");
/// All wallet files for the network, ordered by name (multi-wallet §8).
public static IReadOnlyList WalletFiles(NetKind net)
{
var dir = WalletsDir(net);
return Directory.EnumerateFiles(dir, "*.wallet.json")
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
.ToList();
}
public static string CertificatePinsPath(NetKind net) =>
Path.Combine(ForNetwork(net), "server-certs.json");
public static string ServersPath(NetKind net) =>
Path.Combine(ForNetwork(net), "servers.json");
public static string ConfigPath() =>
Path.Combine(DataRoot(), "config.json");
}