//
// OpenRocky — Voice-first AI Agent
// https://github.com/openrocky
//
// Developed by everettjf with the assistance of Claude Code and Codex.
// Date: 2026-03-25
// Copyright (c) 2026 everettjf. All rights reserved.
//

import Foundation
@preconcurrency import SwiftOpenAI

/// One pre-existing turn replayed into a fresh realtime session so the model
/// can continue an in-progress conversation (text only — tool traces and audio
/// don't help the model and would just inflate the input tokens).
struct OpenRockyVoicePrimingItem: Sendable, Equatable {
    enum Role: String, Sendable { case user, assistant }
    let role: Role
    let text: String
}

/// Abstraction over a realtime voice session. Currently has one implementation
/// (OpenAI Realtime). The protocol is preserved so future providers or alternative
/// transports can slot in without touching the bridge.
@RealtimeActor
protocol OpenRockyRealtimeVoiceClient: AnyObject, Sendable {
    var modelID: String { get }

    func connect(eventSink: @escaping @Sendable (OpenRockyRealtimeEvent) -> Void) async throws
    func disconnect() async
    func sendText(_ text: String) async throws
    func sendAudioChunk(base64Audio: String) async throws
    func sendToolOutput(callID: String, output: String) async throws

    /// Tell the server to abort the in-flight response. Pairs with `truncateAudio` —
    /// cancel stops generation, truncate corrects the conversation history.
    func cancelResponse() async throws
    /// Tell the server "the user only heard `audioEndMs` of the last assistant audio".
    /// Required after a barge-in so the model's view of conversation history matches
    /// what the user actually heard. Pass `audioEndMs: 0` to treat as not-heard.
    func truncateAssistantAudio(audioEndMs: Int) async throws
    /// Drop pending mic audio that hasn't been transcribed yet.
    func clearInputAudioBuffer() async throws
    /// Hot-update the live session with a new system prompt / tool whitelist
    /// without tearing down the WebSocket. Used when the bridge sees a warm
    /// reuse (config identity unchanged) but the soul or tool set changed —
    /// otherwise the active session would silently keep the old config.
    func updateSession(
        soulInstructions: String,
        realtimeTools: [OpenAIRealtimeSessionConfiguration.RealtimeTool]
    ) async throws
}

extension OpenRockyRealtimeVoiceClient {
    func cancelResponse() async throws {}
    func truncateAssistantAudio(audioEndMs: Int) async throws {}
    func clearInputAudioBuffer() async throws {}
    func updateSession(
        soulInstructions: String,
        realtimeTools: [OpenAIRealtimeSessionConfiguration.RealtimeTool]
    ) async throws {}
}
