import Foundation
import NucleicProtocol

/// Agent/device messaging over the mesh (the "messages" cast channel): sessions publish
/// app-level messages through host-mediated MCP tools (`nucleic_send_message` /
/// `nucleic_subscribe_topic`), and the receiving host injects deliveries as ordinary queued
/// user turns — transcript-visible, auditable by construction.
///
/// The host stamps sender provenance from its own session record; tool arguments can name a
/// destination but never an identity. "Directed" means delivery filtering at the receiving
/// host — the cast still rides the whole mesh (one trust domain), it is NOT confidentiality
/// between sessions.
public enum MeshMessagingSettings {
    public static let enabledKey = "nucleic.mesh.agentMessaging"

    /// Whether agents may publish/receive mesh messages. Default OFF — cross-session input
    /// injection is powerful; the user opts in per device.
    public static var enabled: Bool {
        get { UserDefaults.standard.bool(forKey: enabledKey) }
        set { UserDefaults.standard.set(newValue, forKey: enabledKey) }
    }
}

/// Mesh dispatch settings — whether this host accepts chats auto-routed to it via the
/// composer's "Mesh" destination. Default ON: accepting a dispatch is the same trust tier as
/// the existing remote `startChat` any control-scope mesh peer can already send.
public enum MeshDispatchSettings {
    public static let acceptsKey = "nucleic.mesh.acceptsDispatch"

    public static var acceptsDispatch: Bool {
        get { UserDefaults.standard.object(forKey: acceptsKey) as? Bool ?? true }
        set { UserDefaults.standard.set(newValue, forKey: acceptsKey) }
    }
}

/// What an agent asked `nucleic_send_message` to do — parsed tool arguments, no identity
/// (provenance is stamped host-side from the calling session's record).
public struct MeshSendRequest: Sendable {
    /// Destination host, by friendly name or hostID prefix. Nil with a `topic` = broadcast.
    public let to: String?
    /// Target session id on the destination host (optional refinement of `to`).
    public let sessionID: String?
    /// Broadcast topic (mutually exclusive with `to`/`sessionID`).
    public let topic: String?
    public let body: String

    public init(to: String?, sessionID: String?, topic: String?, body: String) {
        self.to = to
        self.sessionID = sessionID
        self.topic = topic
        self.body = body
    }
}

/// The per-run tool bridge threaded AppStore → SessionController → backend → MCP server. Each
/// closure returns the tool's result text (agent-facing prose/JSON).
public struct MeshMessagingHandlers: Sendable {
    public let send: @Sendable (MeshSendRequest) async -> String
    public let subscribe: @Sendable (String) async -> String
    /// `nucleic_wait_for_message`: block until a message arrives for this session (optionally on
    /// a given topic) or the timeout lapses, then return it as the tool result instead of it
    /// being injected as a user turn. `(topic, timeoutSeconds)`.
    public let waitForMessage: @Sendable (String?, Int?) async -> String

    public init(
        send: @escaping @Sendable (MeshSendRequest) async -> String,
        subscribe: @escaping @Sendable (String) async -> String,
        waitForMessage: @escaping @Sendable (String?, Int?) async -> String
    ) {
        self.send = send
        self.subscribe = subscribe
        self.waitForMessage = waitForMessage
    }
}
