import Foundation
import Testing

@testable import NucleicProtocol

@Suite struct CBORTests {
    private func roundTrip<T: Codable & Equatable>(_ value: T) throws -> T {
        let data = try CBOREncoder().encode(value)
        return try CBORDecoder().decode(T.self, from: data)
    }

    @Test func primitivesRoundTrip() throws {
        #expect(try roundTrip(true) == true)
        #expect(try roundTrip("héllo • world") == "héllo • world")
        #expect(try roundTrip(Int(42)) == 42)
        #expect(try roundTrip(Int(-7)) == -7)
        #expect(try roundTrip(UInt64.max) == UInt64.max)
        #expect(try roundTrip(Double(3.14159)) == 3.14159)
        let ints: [Int] = [1, 2, 3]
        #expect(try roundTrip(ints) == ints)
        let dict: [String: Int] = ["a": 1, "b": 2]
        #expect(try roundTrip(dict) == dict)
    }

    @Test func optionalsAndNil() throws {
        struct Box: Codable, Equatable { var a: Int?; var b: String? }
        #expect(try roundTrip(Box(a: nil, b: "x")) == Box(a: nil, b: "x"))
        #expect(try roundTrip(Box(a: 5, b: nil)) == Box(a: 5, b: nil))
    }

    @Test func jsonValueRoundTripsThroughCBOR() throws {
        let v: JSONValue = [
            "tool": "Bash",
            "argv": ["git", "status"],
            "nested": ["count": 3, "ok": true, "ratio": 0.5, "missing": nil],
        ]
        #expect(try roundTrip(v) == v)
    }

    @Test func wholeNumbersDecodeAsIntegers() throws {
        // JSONValue encodes whole numbers as ints; ensure they probe back as .number.
        let v: JSONValue = ["exitCode": 0, "tokens": 12345]
        let back = try roundTrip(v)
        #expect(back["exitCode"]?.intValue == 0)
        #expect(back["tokens"]?.intValue == 12345)
    }

    @Test func agentEventRoundTrips() throws {
        let event = AgentEvent(
            sessionID: SessionID(rawValue: "s1"),
            seq: 99,
            at: Date(timeIntervalSince1970: 1_700_000_000),
            backend: .claudeCode,
            nativeType: "assistant",
            kind: .toolCallStarted(ToolCall(
                toolCallID: "t1", name: "Bash",
                input: ["command": "ls -la"], parentToolCallID: nil)))
        let back = try roundTrip(event)
        #expect(back == event)
    }

    @Test func mapKeyOrderIsDeterministic() throws {
        // Same logical object, different dict literal order → identical bytes.
        let a: JSONValue = ["z": 1, "a": 2, "m": 3]
        let b: JSONValue = ["a": 2, "m": 3, "z": 1]
        let ea = try CBOREncoder().encode(a)
        let eb = try CBOREncoder().encode(b)
        #expect(ea == eb)
    }

    @Test func framingReassemblesAcrossChunks() throws {
        let payloads = [Data("alpha".utf8), Data("bravo-bravo".utf8), Data([], )]
        var stream = Data()
        for p in payloads { stream.append(WireFraming.frame(p)) }

        let acc = FrameAccumulator()
        var got: [Data] = []
        // Feed one byte at a time to stress boundary handling.
        for byte in stream {
            got.append(contentsOf: try acc.push(Data([byte])))
        }
        #expect(got == payloads)
    }

    @Test func framingRejectsOversizeLength() throws {
        let acc = FrameAccumulator(maxFrameSize: 8)
        var bogus = Data([0x00, 0x00, 0x10, 0x00])  // claims 4096 bytes
        bogus.append(Data(repeating: 0, count: 10))
        #expect(throws: FrameAccumulator.FramingError.self) { try acc.push(bogus) }
    }
}
