import Foundation

/// **Where app-side state lives** — the board registry, the clipboard's staging snapshots, the user
/// template store (02-architecture.md § Per-board app state, "App-wide state has the same home").
///
/// ### One app, one sandbox, one home
///
/// There is one application (12-editions.md ▸ The target, re-ruled 2026-07-30), so there is one
/// sandbox container and nothing to share state *with*. `Library/Application Support` inside that
/// container is the whole answer: the sandbox already scopes it per app, which is why there is **no
/// bundle-id subfolder** — a subfolder inside a container that is already this app's alone would be
/// ceremony naming the app twice.
///
/// Scalars are not here. A window size or a toggle goes to `UserDefaults.standard`
/// (`AppPreferences`), which the sandbox scopes on exactly the same terms; this type is only about
/// the *file* stores.
///
/// ### Why the type exists at all rather than three copies of four lines
///
/// Three stores answer "where do I live" and they must answer it identically: the registry, the
/// clipboard staging root and the template store are neighbours by design, and a test that asserts
/// they are neighbours (`BoardRegistryTests`) is asserting something real. One name for the home is
/// what keeps them moving together the day it moves.
public enum AppStateHome {

    /// The directory every app-side file store lives in.
    ///
    /// In a shipped app this is `productionDirectory`. **In a unit-test host it is a scratch
    /// directory** (`isUnitTestHost`) — see that property for why the default has to move for a test
    /// host when everything else about testing is injection.
    public static var directory: URL {
        isUnitTestHost ? unitTestDirectory : productionDirectory
    }

    /// What a shipped app uses: `Library/Application Support` inside the sandbox container.
    ///
    /// The `NSHomeDirectory()` fallback covers the case where `FileManager` answers with no domain
    /// at all — not a state a shipped app is in, but this file must not be the thing that throws.
    public static var productionDirectory: URL {
        FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
            ?? URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)
                .appendingPathComponent("Library/Application Support", isDirectory: true)
    }

    // MARK: - The unit-test host

    /// Whether this process is hosting a unit-test bundle.
    ///
    /// ### Why the app has to know
    ///
    /// The unit-test host **is the app** (`KanbanTests` is a hosted bundle), so `KanbanApp.init()`
    /// runs for real on every test launch and builds an `AppModel` over whatever the defaults resolve
    /// to. Every *object* a test constructs takes its storage by injection — that is the seam, and it
    /// is untouched — but the host's own launch has no injection point, and the thing it reaches for
    /// is the developer's own state: its launch sweep would collect real staged clipboard trees, and
    /// its `refreshRecents()` would resolve, refresh and rewrite real records.
    ///
    /// So the *default* moves for a test host, which is the one place a default can be wrong in a way
    /// injection cannot fix.
    ///
    /// ### Why this variable and not a launch flag
    ///
    /// `UITestLaunch.fixtureFlag` is the flag-shaped answer and remains the right one for the UI
    /// suites, which launch the app themselves and can pass arguments. A *unit*-test host is launched
    /// by the test runner, which passes nothing of ours — but it does set these variables, and it has
    /// set them for as long as XCTest has existed. Three spellings are checked because Apple has used
    /// each at some point and a missed one would silently mean "not a test".
    ///
    /// It cannot fire in a shipped app: nothing sets these but a test runner.
    public static var isUnitTestHost: Bool {
        let environment = ProcessInfo.processInfo.environment
        return environment["XCTestConfigurationFilePath"] != nil
            || environment["XCTestBundlePath"] != nil
            || environment["XCTestSessionIdentifier"] != nil
    }

    /// The scratch home a test host uses instead. Inside the app's own container
    /// (`NSTemporaryDirectory` sandboxes there), so nothing outside this app can see it and the OS
    /// reclaims it.
    ///
    /// One fixed folder rather than one per run: the suites do not depend on it being empty — they
    /// inject their own paths for anything they assert on — and a stable name keeps it inspectable
    /// when something writes there that should not have.
    public static var unitTestDirectory: URL {
        URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
            .appendingPathComponent("LaneworkUnitTestState", isDirectory: true)
    }
}
