// VoiceCatEvent — a Swift value type that is safe to hold/queue past the native callback's
// return. This is the Swift analog of the C# client's `VoiceCatEvent` record.
//
// CRITICAL (voicecat.h's vc_event doc comment): the native `vc_event.text` pointer is owned
// by the core and valid ONLY for the duration of the `on_event` callback. `from(_:)` copies
// it to a Swift `String` immediately — never hold the raw `vc_event` across the callback
// boundary, or `text` will be a dangling pointer by the time it's read. This is the #1
// lifetime rule carried over from the Windows client (NativeCallbacks.cs / VoiceCatEvent.cs).

import VoiceCatC

/// A Swift-safe copy of a `vc_event`. Produced inside the `on_event` callback (see
/// Callbacks.swift) — all pointer fields are converted to value types before the callback
/// returns.
public struct VoiceCatEvent: Sendable, Equatable {
    public let type: VoiceCatEventType
    public let connectionState: VoiceCatConnectionState
    public let result: VoiceCatResult
    public let userId: UInt32
    public let channelId: UInt32
    public let streamId: UInt32
    public let textScope: VoiceCatTextScope
    /// Generic small payload, meaning per event type. For `.serverIdentity` this is the
    /// `VoiceCatTofuStatus`; for `.genericResult` the server error code; for `.talkState`
    /// talking(0/1).
    public let u32a: UInt32
    /// Copied from the core's `vc_event.text` inside the callback. nil if the core passed NULL.
    public let text: String?
    public let timestampUnixMs: UInt64

    /// Convenience: the TOFU status, valid when `type == .serverIdentity` (maps `u32a`).
    public var tofuStatus: VoiceCatTofuStatus? {
        type == .serverIdentity ? VoiceCatTofuStatus(rawValue: u32a) : nil
    }

    /// Copy a native `vc_event` into a safe Swift value. MUST be called inside the callback
    /// while `ev.text` is still valid — `String(cString:)` copies the bytes here.
    @inline(__always)
    public static func from(_ ev: vc_event) -> VoiceCatEvent {
        let text: String?
        if let raw = ev.text {
            text = String(cString: raw)  // copies — safe to hold past callback return
        } else {
            text = nil
        }
        // ev.result is int32_t (not vc_result) per voicecat.h — bridge via bitPattern.
        // ev.u32a is uint32_t — matches VoiceCatTofuStatus's UInt32 raw value directly.
        return VoiceCatEvent(
            type: VoiceCatEventType(ev.type),
            connectionState: VoiceCatConnectionState(ev.connection_state),
            result: VoiceCatResult(rawValue: UInt32(bitPattern: ev.result)) ?? .internalError,
            userId: ev.user_id,
            channelId: ev.channel_id,
            streamId: ev.stream_id,
            textScope: VoiceCatTextScope(ev.text_scope),
            u32a: ev.u32a,
            text: text,
            timestampUnixMs: ev.timestamp_unix_ms
        )
    }
}
