// 协议常量与消息模型，字段与 server/pkg/protocol/ws.go（唯一事实来源）逐项对齐。
// 14C 阶段只搭上屏框架不联网；TODO(14D): 接 WSS /v1/asr/stream 真实推流。
import Foundation

public enum DuduProtocol {
    // 音频参数：二进制帧为 16kHz / 16bit / mono PCM，每 100ms 一帧
    public static let sampleRate = 16000
    public static let frameMillis = 100
    public static let frameBytes = sampleRate * 2 * frameMillis / 1000 // 3200

    /// 单会话上限的真相源是服务端：到点服务端自动截断定稿并下发 SESSION_LIMIT 帧。
    /// 此常量仅作参考（与服务端 MaxSessionSeconds 对齐），iOS 不再据此主动早于服务端掐断。
    public static let maxSessionSeconds = 180
    /// 本地防御性安全上限（秒）：仅防服务端不下发 SESSION_LIMIT 时无限录音；
    /// 故意比服务端上限宽松，确保正常情况下永远是服务端先截断（见 21E）。
    public static let localSafetyCapSeconds = 200
    /// 每日免费试用时长（秒）
    public static let trialDailySeconds = 180

    public static let wsPath = "/v1/asr/stream"

    // 上行文本帧类型
    public enum ClientMsgType {
        public static let start = "start"
        public static let stop = "stop"
        public static let cancel = "cancel"
    }

    // 下行文本帧类型
    public enum ServerMsgType {
        public static let partial = "partial"
        public static let final = "final"
        public static let usage = "usage"
        public static let error = "error"
    }

    /// 客户端上行控制消息（文本帧），音频走二进制帧。对齐 ws.go ClientMsg。
    public struct ClientMsg: Codable {
        public var type: String // start | stop | cancel
        public var sessionID: String
        public var sampleRate: Int? // start 必填，当前仅支持 16000
        public var format: String?  // start 必填，当前仅支持 pcm16

        enum CodingKeys: String, CodingKey {
            case type
            case sessionID = "session_id"
            case sampleRate = "sample_rate"
            case format
        }

        public init(type: String, sessionID: String, sampleRate: Int? = nil, format: String? = nil) {
            self.type = type
            self.sessionID = sessionID
            self.sampleRate = sampleRate
            self.format = format
        }
    }

    /// 服务端下行消息（文本帧）。对齐 ws.go ServerMsg。
    public struct ServerMsg: Codable {
        public var type: String // partial | final | usage | error
        public var sessionID: String?

        // partial / final
        public var text: String?

        // usage：识别中每 2s 及 stop 后下发，驱动 UI 实时刷新余额
        public var sessionSeconds: Int?
        public var balanceSeconds: Int64?
        public var trialRemaining: Int?

        // error
        public var code: String?
        public var message: String?

        enum CodingKeys: String, CodingKey {
            case type
            case sessionID = "session_id"
            case text
            case sessionSeconds = "session_seconds"
            case balanceSeconds = "balance_seconds"
            case trialRemaining = "trial_remaining"
            case code
            case message
        }

        public init(type: String,
                    sessionID: String? = nil,
                    text: String? = nil,
                    sessionSeconds: Int? = nil,
                    balanceSeconds: Int64? = nil,
                    trialRemaining: Int? = nil,
                    code: String? = nil,
                    message: String? = nil) {
            self.type = type
            self.sessionID = sessionID
            self.text = text
            self.sessionSeconds = sessionSeconds
            self.balanceSeconds = balanceSeconds
            self.trialRemaining = trialRemaining
            self.code = code
            self.message = message
        }
    }
}
