import Foundation
import Testing
import WireProtocol

// T-iOS-3 · TimelineEvent（GET /live-sessions/:id/events 条目，src/types.ts:428-433 镜像）。
// 服务器是不可信输入源：decodeList 永不 throw，非法/未知 class 条目静默丢弃。

@Test("TimelineEvent：合法条目全字段解码")
func decodesValidEntryWithAllFields() throws {
    // Arrange
    let json = Data("{\"at\":1751600000000,\"class\":\"tool\",\"toolName\":\"Bash\",\"label\":\"ran Bash\"}".utf8)

    // Act
    let event = try JSONDecoder().decode(TimelineEvent.self, from: json)

    // Assert
    #expect(event == TimelineEvent(at: 1_751_600_000_000, class: "tool", toolName: "Bash", label: "ran Bash"))
    #expect(event.hasKnownClass)
}

@Test("TimelineEvent：toolName 可选缺省")
func decodesEntryWithoutToolName() throws {
    // Arrange
    let json = Data("{\"at\":1,\"class\":\"waiting\",\"label\":\"waiting for approval\"}".utf8)

    // Act
    let event = try JSONDecoder().decode(TimelineEvent.self, from: json)

    // Assert
    #expect(event.toolName == nil)
    #expect(event.hasKnownClass)
}

@Test("knownClasses 与服务器 TimelineClass 全集一致（src/types.ts:423）")
func knownClassesMirrorServerUnion() {
    #expect(TimelineEvent.knownClasses == ["tool", "waiting", "done", "stuck", "user"])
}

@Test("decodeList：未知 class 条目被丢弃，合法条目保留（消费方丢弃语义）")
func decodeListDropsUnknownClassEntries() {
    // Arrange — 1 合法 + 1 未知 class + 1 缺 label + 1 形状完全错误
    let json = Data("""
    [{"at":1,"class":"tool","label":"ran Bash"},
     {"at":2,"class":"alien","label":"???"},
     {"at":3,"class":"done"},
     42]
    """.utf8)

    // Act
    let events = TimelineEvent.decodeList(from: json)

    // Assert
    #expect(events == [TimelineEvent(at: 1, class: "tool", toolName: nil, label: "ran Bash")])
}

@Test("decodeList：5 个已知 class 全部保留")
func decodeListKeepsAllKnownClasses() {
    // Arrange
    let json = Data("""
    [{"at":1,"class":"tool","label":"a"},{"at":2,"class":"waiting","label":"b"},
     {"at":3,"class":"done","label":"c"},{"at":4,"class":"stuck","label":"d"},
     {"at":5,"class":"user","label":"e"}]
    """.utf8)

    // Act
    let events = TimelineEvent.decodeList(from: json)

    // Assert
    #expect(events.count == 5)
    #expect(events.allSatisfy { $0.hasKnownClass })
}

@Test("decodeList：坏 JSON / 顶层非数组 → 空数组，永不 throw",
      arguments: ["not json", "{\"at\":1}", "42", ""])
func decodeListNeverThrowsOnMalformedInput(text: String) {
    #expect(TimelineEvent.decodeList(from: Data(text.utf8)) == [])
}
