//
// 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

struct OpenRockyRealtimeProviderConfiguration: Equatable {
    var provider: OpenRockyRealtimeProviderKind
    var modelID: String
    var credential: String? = nil
    var openaiVoice: String? = nil
    var customHost: String? = nil
    /// Optional advanced tuning. Nil = use defaults from `OpenRockyRealtimeAdvancedSettings.default`.
    var advancedSettings: OpenRockyRealtimeAdvancedSettings? = nil

    // Character persona (injected from active character)
    var characterName: String? = nil
    var characterSpeakingStyle: String? = nil
    var characterGreeting: String? = nil

    /// Effective advanced settings, falling back to defaults when the user hasn't tuned them.
    nonisolated var effectiveAdvancedSettings: OpenRockyRealtimeAdvancedSettings {
        advancedSettings ?? .default
    }

    nonisolated var identity: String {
        var parts = [provider.rawValue, modelID]
        parts.append(credential?.isEmpty == false ? "connected" : "disconnected")
        parts.append(openaiVoice ?? "-")
        parts.append(customHost ?? "-")
        parts.append(characterName ?? "-")
        parts.append(characterSpeakingStyle ?? "-")
        parts.append(characterGreeting ?? "-")
        // Identity includes the advanced fields that demand a fresh session if they change.
        // Sliders like temperature / max-tokens don't need a reconnect — they're picked up
        // on the next session start naturally.
        let adv = effectiveAdvancedSettings
        parts.append(adv.realtimeModel)
        parts.append(adv.transcriptionModel.rawValue)
        parts.append(adv.inputLanguage ?? "-")
        parts.append(adv.turnDetection.mode.rawValue)
        parts.append(adv.allowTextOnly ? "text" : "audio+text")
        return parts.joined(separator: "|")
    }

    nonisolated var isConfigured: Bool {
        return credential?.isEmpty == false && modelID.isEmpty == false
    }

    nonisolated var maskedCredential: String {
        guard let credential, credential.count >= 8 else { return "Not connected" }
        return "\(credential.prefix(4))••••\(credential.suffix(4))"
    }

    nonisolated func normalized() -> OpenRockyRealtimeProviderConfiguration {
        OpenRockyRealtimeProviderConfiguration(
            provider: provider,
            modelID: modelID.trimmingCharacters(in: .whitespacesAndNewlines).ifEmpty(provider.defaultModel),
            credential: credential?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty,
            openaiVoice: openaiVoice?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty,
            customHost: customHost?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty,
            advancedSettings: advancedSettings,
            characterName: characterName?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty,
            characterSpeakingStyle: characterSpeakingStyle?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty,
            characterGreeting: characterGreeting?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty
        )
    }
}

private extension String {
    nonisolated func ifEmpty(_ fallback: String) -> String {
        isEmpty ? fallback : self
    }

    nonisolated var nilIfEmpty: String? {
        isEmpty ? nil : self
    }
}
