import Foundation

// OSSignposter on Darwin; nucleicd's Linux build has no `os`, so every call no-ops there.
#if canImport(os)
import os
#endif

/// Startup signposting (docs/MAIN_THREAD_PERFORMANCE_PLAN.md item 6, feeding item 9's
/// "startup hydration and maintenance" measurement pass): named `os_signpost` intervals over
/// the launch sequence, so Instruments' os_signpost track (subsystem "com.nucleic", category
/// "startup") can attribute launch latency to its phases instead of one opaque blob:
///
///   • `LaunchToFirstFrame` — begun in `NucleicApp.init` (the first code we own; the dyld +
///     Swift-runtime slice *before* it is what Instruments' App Launch template already
///     covers), ended when the root view's `.task` fires — i.e. the first frame at which the
///     window's content hierarchy is live and interactive.
///   • `Hydration` — the ordered project/session install plus the concurrent noncritical
///     hydration group (activity-feed seeding, todo loading, provider probing).
///   • `LockReconcile` — the launch lock/nvrsion reconciliation gate that commands await
///     (`AppStore.awaitStartupLockReconciliation`), running concurrently with hydration.
///   • `DeferredMaintenance` — the post-first-paint maintenance batch
///     (`AppStore.runDeferredStartupMaintenanceOnce`).
///
/// `@MainActor` because the in-flight interval state is shared mutable and every caller —
/// the app's init/`.task` and the store's launch tasks — is already main-actor-isolated.
@MainActor
public enum StartupSignposts {
    public enum Phase: Sendable, Hashable {
        case launchToFirstFrame
        case hydration
        case lockReconcile
        case deferredMaintenance

        /// Signpost interval names must be `StaticString`s and byte-identical at begin/end,
        /// hence a switch over literals rather than anything derived at runtime.
        var name: StaticString {
            switch self {
            case .launchToFirstFrame: return "LaunchToFirstFrame"
            case .hydration: return "Hydration"
            case .lockReconcile: return "LockReconcile"
            case .deferredMaintenance: return "DeferredMaintenance"
            }
        }
    }

    #if canImport(os)
    private static let signposter = OSSignposter(subsystem: "com.nucleic", category: "startup")
    /// In-flight interval states by phase. Each phase runs at most once per launch; a stray
    /// double-begin just replaces (and orphans) the earlier interval rather than trapping.
    private static var inFlight: [Phase: OSSignpostIntervalState] = [:]
    #endif

    public static func begin(_ phase: Phase) {
        #if canImport(os)
        inFlight[phase] = signposter.beginInterval(phase.name)
        #endif
    }

    /// Ends `phase` if it's open; a no-op otherwise (e.g. `end` on a phase whose `begin` was
    /// skipped because the process isn't the GUI app).
    public static func end(_ phase: Phase) {
        #if canImport(os)
        guard let state = inFlight.removeValue(forKey: phase) else { return }
        signposter.endInterval(phase.name, state)
        #endif
    }
}
