import AppIntents
#if NUCLEIC_APP
import NucleicProtocol
#endif

/// The interactive approve / deny an aggregate Live Activity (or widget) button fires — the flagship
/// "resolve from the lock screen without unlocking" path (docs/APP_INTENTS_OPPORTUNITIES §4.1).
///
/// It lives in the **Shared** group so both the app and the widget extension can reference it in
/// `Button(intent:)`. It carries plain `String` ids and compiles its real work only into the app,
/// gated on `#if NUCLEIC_APP` (a custom compilation condition set on the app target only). That's
/// sound because iOS runs a widget/Live-Activity button's intent in the **app's background
/// process** — where `RemoteStore` owns the live E2EE channel — never in the extension. The
/// extension-side copy exists solely to satisfy the `Button(intent:)` type reference.
///
/// NB: the guard is `#if NUCLEIC_APP`, *not* `#if canImport(NucleicProtocol)`. `canImport` tests
/// module findability, not linkage — and because the app builds `NucleicProtocol` into the shared
/// DerivedData products dir, it's findable from the widget extension too. So `canImport` is `true`
/// in the extension, which would compile this branch there and fail on the app-only `RemoteStore`
/// / `IntentError` types. `NUCLEIC_APP` tracks target membership, which is what we actually mean.
///
/// Not discoverable in Shortcuts/Spotlight: it's button-only, driven by ids embedded at render time
/// (a human uses `AnswerApprovalIntent` for the spoken/Shortcuts path).
struct ApproveFromActivityIntent: AppIntent {
    static let title: LocalizedStringResource = "Approve from Live Activity"
    static let isDiscoverable = false

    @Parameter(title: "Approval ID")
    var approvalID: String
    @Parameter(title: "Session ID")
    var sessionID: String
    @Parameter(title: "High Risk")
    var isHighRisk: Bool
    /// `true` = allow, `false` = deny.
    @Parameter(title: "Allow")
    var allow: Bool

    init() {}
    init(approvalID: String, sessionID: String, isHighRisk: Bool, allow: Bool) {
        self.approvalID = approvalID
        self.sessionID = sessionID
        self.isHighRisk = isHighRisk
        self.allow = allow
    }

    @MainActor
    func perform() async throws -> some IntentResult {
        #if NUCLEIC_APP
        let store = RemoteStore.shared
        // §3.3 — a high-risk allow is never resolved inline; route to the app's biometric-gated card.
        // (Surfaces shouldn't render an inline Allow for high-risk in the first place; this is the
        // backstop so the intent can never be a softer path than the UI.)
        if isHighRisk, allow {
            store.route(to: SessionID(rawValue: sessionID))
            throw IntentError.needsAppConfirmation
        }
        // Cloud lock-screen approvals are opt-in (SyncedSettings.resolveApprovalsViaCloud). When
        // off, don't push the decision over Nucleic's relay — hand off to the app to resolve it
        // locally (the foreground app can use the local network). Read the flag from the local
        // cache the sync layer keeps current, so a cold background launch decides without a host.
        guard SyncedSettings.resolveApprovalsViaCloud() else {
            store.route(to: SessionID(rawValue: sessionID))
            throw IntentError.resolveInApp
        }
        // On: resolve inline over the Nucleic Edge. Dial relay-first with a longer budget — a
        // backgrounded/locked intent can't rely on LAN, and the relay handshake (mint token +
        // WebSocket upgrade + Noise) needs more time than a direct dial. `respondToApproval`
        // queues briefly on a dropped link (§3.1); a lost first-responder race is de-duped
        // host-side (§3.4), so we never surface an error for it.
        _ = await store.awaitLiveConnection(timeout: 12, preferRelay: true)
        let decision: Decision = allow ? .allow(updatedInput: nil) : .deny(reason: nil)
        store.respondToApproval(
            id: ApprovalID(rawValue: approvalID),
            sessionID: SessionID(rawValue: sessionID),
            decision: decision)
        return .result()
        #else
        // Widget-extension build: never executed (the system performs this in the app process).
        return .result()
        #endif
    }
}
