import Foundation

// Linux substitutes for the Apple-only surface NucleicCore leans on (docs/ANTIMATTER_RUNNER.md
// §9, CLOUD_RUNTIME §3.1). Everything here is `#if`-gated to compile ONLY where the Apple
// original is absent, so Darwin builds are untouched.

// MARK: - os.Logger shim

#if !canImport(os)
/// The narrow slice of `os.Logger` NucleicCore uses (module-level loggers + privacy-annotated
/// interpolation), emitted to stderr with the subsystem/category label. Structured logging can
/// replace this with swift-log later; the call sites won't change.
public struct Logger: Sendable {
    public struct Message: ExpressibleByStringInterpolation, Sendable {
        public struct Privacy: Sendable {
            public static let `public` = Privacy()
            public static let `private` = Privacy()
            public static let auto = Privacy()
            public static let sensitive = Privacy()
        }

        public struct StringInterpolation: StringInterpolationProtocol, Sendable {
            var rendered = ""
            public init(literalCapacity: Int, interpolationCount: Int) {
                rendered.reserveCapacity(literalCapacity + interpolationCount * 8)
            }
            public mutating func appendLiteral(_ literal: String) { rendered += literal }
            public mutating func appendInterpolation<T>(_ value: @autoclosure () -> T) {
                rendered += String(describing: value())
            }
            public mutating func appendInterpolation<T>(
                _ value: @autoclosure () -> T, privacy: Privacy
            ) {
                rendered += String(describing: value())
            }
        }

        let rendered: String
        public init(stringLiteral value: String) { rendered = value }
        public init(stringInterpolation: StringInterpolation) {
            rendered = stringInterpolation.rendered
        }
    }

    let label: String
    public init(subsystem: String, category: String) { label = "\(subsystem).\(category)" }

    public func trace(_ message: Message) { emit("trace", message) }
    public func debug(_ message: Message) { emit("debug", message) }
    public func info(_ message: Message) { emit("info", message) }
    public func notice(_ message: Message) { emit("notice", message) }
    public func warning(_ message: Message) { emit("warning", message) }
    public func error(_ message: Message) { emit("error", message) }
    public func fault(_ message: Message) { emit("fault", message) }

    private func emit(_ level: String, _ message: Message) {
        FileHandle.standardError.write(Data("[\(label)] \(level): \(message.rendered)\n".utf8))
    }
}
#endif

// MARK: - File-based secret store (the Keychain's Linux stand-in)

#if !canImport(Security)
/// Where the Keychain-backed stores keep their secrets on Linux: 0600 files under a 0700 dir,
/// one per account string — the CLOUD_RUNTIME §3.1 "0600-file secret store" seam. Inside a
/// runner container this directory lives on the container's own disk (checkpointed per
/// CLOUD_RUNTIME §3.5), never in the repo or a mount the agent works in.
enum LinuxSecretStore {
    static var baseDirectory: URL {
        if let override = ProcessInfo.processInfo.environment["NUCLEIC_SECRETS_DIR"],
            !override.isEmpty
        {
            return URL(fileURLWithPath: override, isDirectory: true)
        }
        let home = FileManager.default.homeDirectoryForCurrentUser
        return home.appendingPathComponent(".config/nucleic/secrets", isDirectory: true)
    }

    private static func url(for account: String) -> URL {
        // Account strings are reverse-DNS-ish identifiers; keep the mapping readable while
        // guarding against separators.
        let safe = account.replacingOccurrences(of: "/", with: "_")
        return baseDirectory.appendingPathComponent(safe)
    }

    static func read(account: String) -> Data? {
        try? Data(contentsOf: url(for: account))
    }

    @discardableResult
    static func write(account: String, data: Data) -> Bool {
        let dir = baseDirectory
        do {
            try FileManager.default.createDirectory(
                at: dir, withIntermediateDirectories: true,
                attributes: [.posixPermissions: 0o700])
            let target = url(for: account)
            try data.write(to: target, options: .atomic)
            try FileManager.default.setAttributes(
                [.posixPermissions: 0o600], ofItemAtPath: target.path)
            return true
        } catch {
            return false
        }
    }

    static func remove(account: String) {
        try? FileManager.default.removeItem(at: url(for: account))
    }
}
#endif

// MARK: - Sandbox-service stubs

#if !canImport(Containerization)
/// The two container-probe value types the engine defines on Darwin
/// (`Container/ContainerEngine.swift`); mirrored here so the shared surface compiles.
public struct ContainerResourceSample: Sendable, Equatable {
    public let cpuPercent: Double
    public let memoryUsedBytes: UInt64
    public let memoryTotalBytes: UInt64

    public init(cpuPercent: Double, memoryUsedBytes: UInt64, memoryTotalBytes: UInt64) {
        self.cpuPercent = cpuPercent
        self.memoryUsedBytes = memoryUsedBytes
        self.memoryTotalBytes = memoryTotalBytes
    }

    /// Memory utilization 0…100. Zero when the total is unknown (avoids a divide-by-zero).
    public var memoryPercent: Double {
        memoryTotalBytes == 0 ? 0 : min(100, Double(memoryUsedBytes) / Double(memoryTotalBytes) * 100)
    }
}

public struct ContainerKillDiagnosis: Sendable, Equatable {
    public let containerRunning: Bool
    public let oomKills: Int?
    public let sample: ContainerResourceSample?

    public init(containerRunning: Bool, oomKills: Int?, sample: ContainerResourceSample?) {
        self.containerRunning = containerRunning
        self.oomKills = oomKills
        self.sample = sample
    }
}

/// Linux stand-in for the Apple-containerization sandbox orchestrator. On a runner the
/// container IS the sandbox (`RunSpec.container` stays nil — CLOUD_RUNTIME §0), so the
/// service is never enabled and the throwing members are never reached; they exist so the
/// shared object graph (AppStore, backends, SessionController signatures) compiles unchanged.
/// Tier-1 session sandboxes on Linux arrive later via the RunnerPool acquire seam
/// (docs/ANTIMATTER_RUNNER.md §4), not through this type. The pure naming statics mirror the
/// Darwin originals exactly (same channel defines), so names stay identical cross-platform.
public actor ContainerManager {
    public struct LinuxUnsupported: Error, CustomStringConvertible {
        public let description = "the Apple-containerization sandbox service does not exist on Linux"
    }

    // Naming statics — byte-identical to Container/ContainerManager.swift; keep in lockstep.
    public nonisolated static let channelSuffix: String = {
        #if NUCLEIC_STABLE
        return ""
        #elseif NUCLEIC_RC
        return "-rc"
        #elseif NUCLEIC_BETA
        return "-beta"
        #elseif NUCLEIC_CANARY
        return "-canary"
        #else
        return "-local"
        #endif
    }()
    public nonisolated static let nonReleaseSuffixes = ["-local", "-canary", "-beta", "-rc"]

    public nonisolated static func ownsContainer(named name: String) -> Bool {
        channelSuffix.isEmpty
            ? !nonReleaseSuffixes.contains(where: name.hasSuffix)
            : name.hasSuffix(channelSuffix)
    }

    public nonisolated static func containerName(for session: SessionID) -> String {
        "nucleic-\(session.short.lowercased())\(channelSuffix)"
    }

    public static let sharedControlContainerName = "nucleic-control" + channelSuffix
    public static let claudeControlContainerName = "nucleic-control-claude" + channelSuffix
    public static let codexControlContainerName = "nucleic-control-codex" + channelSuffix
    public static let xaiControlContainerName = "nucleic-control-xai" + channelSuffix
    public static let opencodeControlContainerName = "nucleic-control-opencode" + channelSuffix
    public static let openclawControlContainerName = "nucleic-control-openclaw" + channelSuffix
    public static let hermesControlContainerName = "nucleic-control-hermes" + channelSuffix
    public static let cursorControlContainerName = "nucleic-control-cursor" + channelSuffix
    public static let allSharedControlContainerNames = [
        sharedControlContainerName, claudeControlContainerName, codexControlContainerName,
        xaiControlContainerName, opencodeControlContainerName, openclawControlContainerName,
        hermesControlContainerName, cursorControlContainerName,
    ]

    public nonisolated static func sharedContainerName(for backend: BackendID, split: Bool) -> String {
        guard split else { return sharedControlContainerName }
        switch backend {
        case .claudeCode: return claudeControlContainerName
        case .codex, .codexExec: return codexControlContainerName
        case .grok: return xaiControlContainerName
        case .opencode: return opencodeControlContainerName
        case .openclaw: return openclawControlContainerName
        case .hermes: return hermesControlContainerName
        case .cursorAgent: return cursorControlContainerName
        case .acp: return sharedControlContainerName
        }
    }

    let onSharedContainerRemoved: @Sendable (String) async -> Void

    public init(onSharedContainerRemoved: @Sendable @escaping (String) async -> Void = { _ in }) {
        self.onSharedContainerRemoved = onSharedContainerRemoved
    }

    public func ensureRunning(_ spec: ContainerSpec) async throws
        -> (name: String, hostGateway: String)
    {
        throw LinuxUnsupported()
    }

    public func exec(
        name: String, workdir: String, env: [String: String], argv: [String],
        uid: Int? = nil, gid: Int? = nil
    ) async throws -> any ProcessHandle {
        throw LinuxUnsupported()
    }

    public func finished(name: String) {}
    public func stopAgentContainer(name: String) async {}
    public func removeAgentContainer(name: String) async -> Bool { false }
    public func teardown(_ session: SessionID, waitForActive: Bool = false) async -> String? { nil }
    public func teardownShared() async {}
    public func controlContainerStatus() async -> (exists: Bool, running: Bool) { (false, false) }
    public func controlContainers() async -> [ControlContainerEntry] { [] }
    public func controlContainerUsage() async -> ContainerResourceSample? { nil }
    public func controlDownloadProgress() async -> ContainerDownloadProgress? { nil }
    public func diagnoseContainerKill(name: String) async -> ContainerKillDiagnosis {
        ContainerKillDiagnosis(containerRunning: false, oomKills: nil, sample: nil)
    }
    public func reconcile(activeSessions: [SessionID]) async {}
    public func recreateShared() async {}
    public func restartShared() async {}
    public func updateAgentCLIs() async -> [AgentCLIUpdateResult] { [] }
    public func checkImageUpdate() async -> ImageUpdateStatus { .idle }
    public func reclaimMemoryNow() async {}
}
#endif

#if !canImport(Virtualization)
/// `UncheckedSendableBox` lives in `MacVM/MacVMEngine+Base.swift` on Darwin (excluded here);
/// `MacVMSurfaceHost` (portable, kept) names it in a signature, so mirror the box.
public struct UncheckedSendableBox<T>: @unchecked Sendable {
    public let value: T
    public init(_ value: T) { self.value = value }
}

/// Mirrors of the two Control-panel value types `MacVM/MacVMManager.swift` defines on Darwin.
public struct MacVMEntry: Sendable, Equatable {
    public let name: String
    public let ipAddress: String?
    public let os: GuestOS
    public init(name: String, ipAddress: String?, os: GuestOS = .macOS) {
        self.name = name
        self.ipAddress = ipAddress
        self.os = os
    }
}

public struct MacVMMaintenanceInfo: Sendable, Equatable {
    public let name: String
    public let os: GuestOS
    public let activity: String
    public let booting: Bool
    public init(name: String, os: GuestOS, activity: String, booting: Bool) {
        self.name = name
        self.os = os
        self.activity = activity
        self.booting = booting
    }
}

/// Linux stand-in for the macOS/Linux-VM orchestrator (`mac_vm_*` / `linux_vm_*` tools). The VM
/// service is Darwin-only (Apple Virtualization); on a runner these tools are simply never
/// exposed (`isSupported` is false, `MacVMSettings.serviceEnabled` defaults off, and no
/// `RunSpec.allow*VM*` is ever set), so the throwing members are never reached. Exists so the
/// shared object graph compiles unchanged. Signatures mirror `MacVM/MacVMManager.swift`.
public actor MacVMManager {
    public struct LinuxUnsupported: Error, CustomStringConvertible {
        public let description = "the macOS/Linux VM service does not exist on a Linux runner"
    }

    public nonisolated static var isSupported: Bool { false }

    public nonisolated static func vmName(for session: SessionID) -> String {
        "nucleic-mac-\(session.short.lowercased())\(ContainerManager.channelSuffix)"
    }

    public nonisolated static func linuxVMName(for session: SessionID) -> String {
        "nucleic-lin-\(session.short.lowercased())\(ContainerManager.channelSuffix)"
    }

    public init() {}

    public func ensureRunning(_ spec: MacVMSpec) async throws -> (name: String, ipAddress: String) {
        throw LinuxUnsupported()
    }

    public func exec(
        name: String, workdir: String?, env: [String: String], argv: [String]
    ) async throws -> any ProcessHandle {
        throw LinuxUnsupported()
    }

    public func run(
        name: String, command: String, workdir: String?, env: [String: String] = [:]
    ) async throws -> (exitCode: Int32, stdout: String, stderr: String) {
        throw LinuxUnsupported()
    }

    public func performComputerAction(
        name: String, action: String, x: Int?, y: Int?, text: String?,
        ref: String? = nil, value: String? = nil,
        scrollDirection: String?, scrollAmount: Int?, durationMs: Int?
    ) async throws -> (imageBase64: String?, summary: String) {
        throw LinuxUnsupported()
    }

    public func installApps(name: String, appPaths: [String]) async throws -> [String] {
        throw LinuxUnsupported()
    }
    public func installAppsIntoBase(appPaths: [String]) async throws -> [String] {
        throw LinuxUnsupported()
    }
    public func installPackages(name: String, packageIDs: [String]) async throws -> [String] {
        throw LinuxUnsupported()
    }
    public func installPackagesIntoBase(packageIDs: [String]) async throws -> [String] {
        throw LinuxUnsupported()
    }

    public func finished(name: String) async {}
    public func suspendIfIdle(_ session: SessionID) async {}
    public func teardown(_ session: SessionID, waitForActive: Bool = false) async -> String? { nil }
    public func restart(_ session: SessionID) async {}
    public func stop(name: String) async {}
    public func suspend(name: String) async -> Bool { false }
    public func resume(name: String) async -> Bool { false }
    public func restart(name: String) async {}
    public func remove(name: String) async -> Bool { false }
    public func exists(name: String) async -> Bool { false }
    public func isPaused(name: String) async -> Bool { false }
    public func isRunning(name: String) async -> Bool { false }
    public func isWorkingInBackground(name: String) -> Bool { false }
    public func runningVMs() async -> [MacVMEntry] { [] }
    public func baseMaintenanceVM() async -> MacVMMaintenanceInfo? { nil }
    public func captureScreen(name: String) async -> String? { nil }
    public func sampleResourceUsage(name: String) async -> MacVMResourceSample? { nil }
    public func baseProgress() async -> MacVMBaseProgress? { nil }
    public func baseImageOSVersion() async -> String? { nil }
    public func baseStatus() async -> MacVMBaseStatus { MacVMBaseStatus() }
    public func linuxBaseStatus() async -> MacVMBaseStatus { MacVMBaseStatus() }
    public func baseBundleRootPath() async -> String? { nil }
    public func beginBaseRecovery() async throws -> String { throw LinuxUnsupported() }
    public func endBaseRecovery() async {}
    public func setSurfaceHost(_ host: any MacVMSurfaceHost) async {}
    public func setEvictionPolicy(_ ranker: @escaping @Sendable ([String]) async -> [String]) async {}
    public func buildBaseImage(localRestoreImagePath: String? = nil) async throws {
        throw LinuxUnsupported()
    }
    public func deleteBaseImage(includingRestoreImages: Bool) async throws {
        throw LinuxUnsupported()
    }
    public func buildLinuxBaseImage() async throws { throw LinuxUnsupported() }
    public func deleteLinuxBaseImage() async throws { throw LinuxUnsupported() }
    public func setBaseProvisionObserver(visible: Bool) async {}
    public func presentOperatorAssist(name: String, visible: Bool) async {}
    public func reconcile(activeSessions: [SessionID]) async {}
    public func reprovisionStaleBaseImagesIfNeeded() async {}
}
#endif
