import Foundation

/// The single source of truth for Nucleic's on-disk locations (docs/WINDOWS_PORT.md §4.4).
///
/// Before this existed, half a dozen call sites each spelled
/// `FileManager.urls(for: .applicationSupportDirectory, …).appendingPathComponent("Nucleic")`
/// inline — fine on a Mac, where that one expression is right, but there is no single right
/// expression across platforms: Windows wants `%APPDATA%\Nucleic<channel>` (channel-suffixed, so
/// all five channels coexist, mirroring the container naming scheme), and a headless host wants an
/// explicit override. Every artifact now derives from ``dataRoot`` so a host that relocates its
/// data relocates ALL of it, not the subset that happened to be routed through a parameter.
///
/// **macOS/Linux behavior is unchanged by construction**: with no override set, ``dataRoot`` is
/// exactly `<Application Support>/Nucleic`, the literal every call site used before. Only Windows
/// (and an explicit `NUCLEIC_DATA_DIR`) takes a different branch.
///
/// Not covered here, deliberately: `nucleicd`'s runner root (`NUCLEIC_RUNNER_DATA_DIR`, defaulting
/// to `NucleicRunner`) is a *different* root by design, and the container/MacVM engines' storage
/// roots are Apple-framework-owned paths. Those keep their own lookups.
public enum NucleicPaths {
    /// Env var that relocates every derived path. Set by `nucleic-hostd` (§11) so its
    /// `%APPDATA%\Nucleic<channel>` root governs the secrets dir, the run dir, the tailnet state
    /// and the logs — not just the store path it passes to `NucleicHeadless.makeStore`.
    public static let dataDirEnvKey = "NUCLEIC_DATA_DIR"

    /// The per-user data root: `~/Library/Application Support/Nucleic` on macOS,
    /// `<XDG app support>/Nucleic` on Linux, `%APPDATA%\Nucleic<channelSuffix>` on Windows.
    /// Overridden wholesale by ``dataDirEnvKey``. Read on every access (not memoized) so a test
    /// or a host that sets the override before touching any store is honored regardless of which
    /// path is consulted first.
    public static var dataRoot: URL {
        if let override = ProcessInfo.processInfo.environment[dataDirEnvKey], !override.isEmpty {
            return URL(fileURLWithPath: override, isDirectory: true)
        }
        #if os(Windows)
        // `%APPDATA%` is always set on Windows and is what corelibs-foundation maps
        // `.applicationSupportDirectory` onto; read it directly so the root doesn't depend on
        // that mapping staying put across toolchain versions.
        let base = ProcessInfo.processInfo.environment["APPDATA"].map {
            URL(fileURLWithPath: $0, isDirectory: true)
        } ?? applicationSupportBase
        // Channel-suffixed on Windows only: a Mac shares one root across channels and suffixes
        // individual artifacts (`tailnet-beta`, container names); a Windows install is packaged
        // per channel with its own MSIX identity, so the whole root splits.
        return base.appendingPathComponent(
            "Nucleic" + ContainerManager.channelSuffix, isDirectory: true)
        #else
        return applicationSupportBase.appendingPathComponent("Nucleic", isDirectory: true)
        #endif
    }

    /// Short-lived endpoint files (unix sockets on Darwin, the rendezvous file on Windows).
    /// Kept short on purpose: the AF_UNIX `sun_path` cap is ~104 bytes, so only the
    /// `Nucleic/run/<name>.sock` tail may live here — see `ApprovalServerRegistry`.
    public static var runDirectory: URL {
        dataRoot.appendingPathComponent("run", isDirectory: true)
    }

    /// Rolling diagnostic logs (`SmartMove.log`, the mesh debug log, and on Windows the
    /// `PortableLogging` file sink).
    public static var logsDirectory: URL {
        #if os(Windows)
        return dataRoot.appendingPathComponent("logs", isDirectory: true)
        #else
        // Darwin/Linux logs have always sat directly in the root next to the store; keep them
        // there so an existing install's log paths don't move.
        return dataRoot
        #endif
    }

    /// Secret material written to disk by the file-backed `SecretStore` (`KeychainOwnedAccess`'s
    /// non-Keychain fallback and the Linux/Windows backends). Owner-only — write through
    /// ``SecretFile`` so the hardening is applied on every platform.
    public static var secretsDirectory: URL {
        dataRoot.appendingPathComponent("Secrets", isDirectory: true)
    }

    /// The platform's Application Support base. Falls back to the temp dir on the (effectively
    /// impossible) empty-search-path case rather than trapping on `[0]` the way the inline call
    /// sites did — a headless host must not crash at startup over a path lookup.
    private static var applicationSupportBase: URL {
        FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
            ?? URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
    }
}
