import Foundation

/// The part of Codex's runtime contract that is specific to running *inside* a Nucleic Control
/// container.
///
/// `SessionController.sandboxBuildGuidance` describes the common environment to every provider, but
/// Codex needs one extra piece of grounding that Claude does not: Codex normally supplies its own
/// Linux sandbox around commands. That inner bwrap/landlock layer cannot nest in the Control
/// container and is deliberately disabled. Without saying so, a model that encounters an old or
/// drifted sandbox setting tends to diagnose Nucleic as a broken Codex installation and tries to
/// repair `bwrap` instead of using the platform tools Nucleic gave it.
enum CodexRuntimeEnvironment {
    static let externalSandboxFlag = "--dangerously-bypass-approvals-and-sandbox"

    /// Reassert the real isolation boundary for every app-server start *and resume*. Reapplying the
    /// value on resume also repairs threads created before Nucleic disabled Codex's nested sandbox.
    static func sandboxMode(containerized: Bool, requested: SandboxMode?) -> SandboxMode {
        containerized ? .fullAccess : (requested ?? .workspaceWrite)
    }

    /// Global CLI arguments for `codex exec`. A Control container is exactly the use case Codex's
    /// explicit externally-sandboxed flag exists for; unlike `--sandbox danger-full-access`, it
    /// cannot accidentally retain or reconstruct an inner bwrap policy when a session resumes.
    static func execIsolationArgs(containerized: Bool, requested: SandboxMode?) -> [String] {
        if containerized { return [externalSandboxFlag] }
        return [
            "--sandbox", sandboxMode(containerized: false, requested: requested).rawValue,
            "--ask-for-approval", "never",
        ]
    }

    /// Add Codex-only grounding to the provider-neutral environment guidance. Tool names come from
    /// the actual per-turn `RunSpec`, so the prompt never advertises a host/VM/container escape hatch
    /// that the session did not expose.
    static func developerInstructions(for run: RunSpec, containerized: Bool) -> String? {
        guard containerized else { return nonempty(run.appendSystemPrompt) }

        let tools = platformToolNames(for: run)
        let toolSentence: String
        if tools.isEmpty {
            toolSentence = "No optional Nucleic execution tool is enabled for this turn; do not "
                + "invent one or tell the user that one exists."
        } else {
            toolSentence = "The optional Nucleic execution capabilities enabled for this turn are "
                + tools.map { "`\($0)`" }.joined(separator: ", ") + "."
        }

        let contract = """
            Nucleic Codex runtime: this Codex process already runs inside Nucleic's managed Linux \
            container, which is the OS isolation boundary. Codex's own nested Linux sandbox is \
            deliberately disabled because bubblewrap (`bwrap`) and Landlock cannot nest in this \
            container's capability set. This is expected and is not a broken dependency. Never try \
            to invoke, install, repair, or diagnose `bwrap`, and never ask the user to fix it. \
            `danger-full-access` means full access only inside the outer Nucleic container; it does \
            not grant access to the macOS host.

            Treat the tools actually present in your tool list as the authoritative description of \
            this session's capabilities. Nucleic MCP tools (shown by Codex as \
            `mcp__nucleic__<name>`, and referred to here by their short names) are real first-class \
            tools: call them directly when their descriptions fit instead of recreating their \
            behavior with shell workarounds or merely telling the user what to run. Their own \
            approval flow is the permission request. \(toolSentence)
            """

        guard let base = nonempty(run.appendSystemPrompt) else { return contract }
        return contract + "\n\n" + base
    }

    /// Optional execution surfaces only. Core Nucleic tools such as conflict coordination and
    /// orchestration have their own standing prompts/tool descriptions and do not belong in this
    /// environment summary.
    static func platformToolNames(for run: RunSpec) -> [String] {
        var names: [String] = []
        if run.allowAgentContainers { names.append("linux_container") }
        if run.allowLinuxVMExec { names.append("linux_vm_exec") }
        if run.allowLinuxVMExec || run.allowLinuxVMComputer { names.append("linux_vm_control") }
        if run.allowLinuxVMComputer {
            names.append(contentsOf: ["linux_vm_computer", "linux_vm_computer_batch"])
        }
        if run.allowMacVMExec { names.append("mac_vm_exec") }
        if run.allowMacVMExec || run.allowMacVMComputer { names.append("mac_vm_control") }
        if run.allowMacVMComputer {
            names.append(contentsOf: [
                "mac_vm_computer", "mac_vm_computer_batch", "mac_vm_clear_notifications",
                "mac_vm_request_operator",
            ])
        }
        if run.allowHostExec { names.append("host_exec") }
        return names
    }

    private static func nonempty(_ text: String?) -> String? {
        guard let text, !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
            return nil
        }
        return text
    }
}
