import Foundation

/// Typed Int32 mirrors of AviationExtensionCode (the pure-C enum imports
/// with a UInt32 rawValue, which is awkward at call sites).
enum AviationExtCode {
    static let adBreakStarted = Int32(AVIATION_EXT_AD_BREAK_STARTED.rawValue)
    static let adBreakEnded = Int32(AVIATION_EXT_AD_BREAK_ENDED.rawValue)
    static let adStarted = Int32(AVIATION_EXT_AD_STARTED.rawValue)
    static let adCompleted = Int32(AVIATION_EXT_AD_COMPLETED.rawValue)
    static let adProgress = Int32(AVIATION_EXT_AD_PROGRESS.rawValue)
    static let adSkipped = Int32(AVIATION_EXT_AD_SKIPPED.rawValue)
    static let adTapped = Int32(AVIATION_EXT_AD_TAPPED.rawValue)
    static let adError = Int32(AVIATION_EXT_AD_ERROR.rawValue)
    static let adBuffering = Int32(AVIATION_EXT_AD_BUFFERING.rawValue)
    static let allAdsCompleted = Int32(AVIATION_EXT_ALL_ADS_COMPLETED.rawValue)
    static let cacheHit = Int32(AVIATION_EXT_CACHE_HIT.rawValue)
    static let cacheMiss = Int32(AVIATION_EXT_CACHE_MISS.rawValue)
    static let preloadStarted = Int32(AVIATION_EXT_PRELOAD_STARTED.rawValue)
    static let preloadCompleted = Int32(AVIATION_EXT_PRELOAD_COMPLETED.rawValue)
    static let ttff = Int32(AVIATION_EXT_TTFF.rawValue)

    static let flagAdSkippable = Int32(AVIATION_EXT_FLAG_AD_SKIPPABLE)

    /// Inclusive bounds of the AD_* code family.
    static let adFamilyFirst = adBreakStarted
    static let adFamilyLast = allAdsCompleted
}

/// Helpers for emitting adapter-layer feature telemetry through the C
/// domain bus under AVIATION_EVT_EXTENSION. Codes and slot conventions
/// live in aviation_extension_events.h; both platform bridges must
/// encode/decode identically (pinned by crossLanguageParity.test.ts).
enum AviationExt {
    /// Build a zero-initialized extension domain event.
    static func event(
        code: Int32,
        flags: Int32 = 0,
        values: [Int64] = [],
        text: String = "",
        detail: String = "",
        aux: String = ""
    ) -> AviationDomainEvent {
        var event = AviationDomainEvent()
        memset(&event, 0, MemoryLayout<AviationDomainEvent>.size)
        event.type = Int32(AVIATION_EVT_EXTENSION.rawValue)
        event.extension.code = code
        event.extension.flags = flags

        // Fixed-size C arrays import as tuples; access through memory.
        withUnsafeMutableBytes(of: &event.extension.values) { buffer in
            let slots = buffer.baseAddress!.assumingMemoryBound(to: Int64.self)
            let count = min(values.count, Int(AVIATION_EXT_VALUE_COUNT))
            for i in 0 ..< count {
                slots[i] = values[i]
            }
        }

        copy(text, into: &event.extension.text)
        copy(detail, into: &event.extension.detail)
        copy(aux, into: &event.extension.aux)
        return event
    }

    /// Read slot `i` from an extension payload.
    static func value(_ ext: AviationExtensionEvent, _ i: Int32) -> Int64 {
        withUnsafeBytes(of: ext.values) { buffer in
            buffer.load(fromByteOffset: Int(i) * MemoryLayout<Int64>.size, as: Int64.self)
        }
    }

    /// Emit onto the coordinator's bus from any adapter holding an eventBus pointer.
    static func emit(_ event: AviationDomainEvent, on bus: OpaquePointer?) {
        var copy = event
        aviation_event_bus_emit_domain(bus, &copy)
    }

    /// Double -> Int64 bit pattern (doubles travel in the value vector).
    static func bits(_ d: Double) -> Int64 { Int64(bitPattern: d.bitPattern) }

    private static func copy(_ source: String, into dest: UnsafeMutableRawPointer) {
        source.withCString { ptr in
            strncpy(
                dest.assumingMemoryBound(to: CChar.self),
                ptr,
                Int(AVIATION_EXT_STR_LEN) - 1
            )
            dest.advanced(by: Int(AVIATION_EXT_STR_LEN) - 1)
                .assumingMemoryBound(to: CChar.self).pointee = 0
        }
    }
}
