import Foundation
import Testing
@testable import Notes

struct AudioNotePayloadTests {
    @Test func metadataRoundTrips() throws {
        let payload = AudioNotePayload(
            id: "01J8Z3M9K7QW2R4T6Y8B0C1D2E",
            recordedAt: Date(timeIntervalSince1970: 1_700_000_000),
            duration: 12.5,
            timeZoneID: "Europe/Berlin"
        )
        let decoded = try #require(AudioNotePayload(metadata: payload.metadata()))
        #expect(decoded == payload)
    }

    @Test func metadataRoundTripsWithoutTimeZone() throws {
        let payload = AudioNotePayload(
            id: "01J8Z3M9K7QW2R4T6Y8B0C1D2E",
            recordedAt: Date(timeIntervalSince1970: 1_700_000_100),
            duration: 4,
            timeZoneID: nil
        )
        let dict = payload.metadata()
        #expect(dict["tz"] == nil)
        let decoded = try #require(AudioNotePayload(metadata: dict))
        #expect(decoded == payload)
    }

    /// A survivable trip through actual plist serialization — the metadata really
    /// crosses WatchConnectivity as a plist dictionary.
    @Test func metadataSurvivesPlistSerialization() throws {
        let payload = AudioNotePayload(
            id: "01J8Z3M9K7QW2R4T6Y8B0C1D2E",
            recordedAt: Date(timeIntervalSince1970: 1_700_000_050),
            duration: 7.25,
            timeZoneID: "America/Los_Angeles"
        )
        let data = try PropertyListSerialization.data(
            fromPropertyList: payload.metadata(), format: .binary, options: 0)
        let dict = try #require(
            try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: Any])
        let decoded = try #require(AudioNotePayload(metadata: dict))
        #expect(decoded.id == payload.id)
        #expect(decoded.duration == payload.duration)
        #expect(decoded.timeZoneID == payload.timeZoneID)
        #expect(abs(decoded.recordedAt.timeIntervalSince(payload.recordedAt)) < 0.001)
    }

    @Test func decodeRejectsUnknownNewerVersion() {
        var dict = AudioNotePayload(
            id: "01J8Z3M9K7QW2R4T6Y8B0C1D2E",
            recordedAt: Date(),
            duration: 3,
            timeZoneID: nil
        ).metadata()
        dict["v"] = AudioNotePayload.currentVersion + 1
        #expect(AudioNotePayload(metadata: dict) == nil)
    }

    @Test func decodeRejectsMissingVersion() {
        let dict: [String: Any] = [
            "id": "01J8Z3M9K7QW2R4T6Y8B0C1D2E",
            "recordedAt": Date(),
            "duration": 3.0,
        ]
        #expect(AudioNotePayload(metadata: dict) == nil)
    }

    @Test func decodeRejectsMissingRequiredField() {
        // Version present, but no duration.
        let dict: [String: Any] = [
            "v": AudioNotePayload.currentVersion,
            "id": "01J8Z3M9K7QW2R4T6Y8B0C1D2E",
            "recordedAt": Date(),
        ]
        #expect(AudioNotePayload(metadata: dict) == nil)
    }

    @Test func decodeRejectsEmptyMetadata() {
        #expect(AudioNotePayload(metadata: [:]) == nil)
    }

    // MARK: - IngestAck

    @Test func ackRoundTrips() {
        let ack = IngestAck(id: "01J8Z3M9K7QW2R4T6Y8B0C1D2E")
        let decoded = IngestAck(userInfo: ack.userInfo())
        #expect(decoded == ack)
    }

    @Test func ackRejectsNewerVersionAndForeignUserInfo() {
        var dict = IngestAck(id: "01J8Z3M9K7QW2R4T6Y8B0C1D2E").userInfo()
        dict["ackV"] = IngestAck.currentVersion + 1
        #expect(IngestAck(userInfo: dict) == nil)
        #expect(IngestAck(userInfo: [:]) == nil)
        #expect(IngestAck(userInfo: ["unrelated": true]) == nil)
    }
}
