import Foundation
import Testing

@testable import NucleicProtocol

@Suite struct HeartbeatTests {
    private func freshDefaults(_ name: String) -> UserDefaults {
        let d = UserDefaults(suiteName: name)!
        d.removePersistentDomain(forName: name)
        return d
    }

    @Test func dayKeyIsUTCFormatted() {
        // 2026-01-02T02:50:45Z — same calendar day in UTC regardless of local zone.
        let date = Date(timeIntervalSince1970: 1_767_322_245)
        #expect(HeartbeatSchedule.dayKey(date) == "2026-01-02")
    }

    @Test func dueGateFlipsAfterMarkSent() {
        let name = "heartbeat.test.gate"
        let d = freshDefaults(name)
        defer { d.removePersistentDomain(forName: name) }

        let now = Date(timeIntervalSince1970: 1_767_322_245)   // 2026-01-02Z
        #expect(HeartbeatSchedule.isDue(d, now: now) == true)

        HeartbeatSchedule.markSent(d, now: now)
        #expect(HeartbeatSchedule.isDue(d, now: now) == false)

        // Later the same UTC day: still not due.
        #expect(HeartbeatSchedule.isDue(d, now: now.addingTimeInterval(3600)) == false)
        // Next UTC day: due again.
        #expect(HeartbeatSchedule.isDue(d, now: now.addingTimeInterval(86_400)) == true)
    }

    @Test func optOutDefaultsOnAndRespectsFalse() {
        let name = "heartbeat.test.optout"
        let d = freshDefaults(name)
        defer { d.removePersistentDomain(forName: name) }

        #expect(HeartbeatSettings.isEnabled(d) == true)        // default on (opt-out posture)
        d.set(false, forKey: HeartbeatSettings.shareAnonymousUsageKey)
        #expect(HeartbeatSettings.isEnabled(d) == false)
    }

    @Test func encodesExactWireKeys() throws {
        let beat = Heartbeat(
            installId: "id", platform: "macos", osVersion: "26.0", channel: "beta",
            appVersion: "0.1.0", build: "42", arch: "arm64", locale: "en")
        let data = try JSONEncoder().encode(beat)
        let obj = try #require(try JSONSerialization.jsonObject(with: data) as? [String: Any])

        // The keys must match what the nucleic-edge Worker validates (cloud/nucleic-edge).
        #expect(Set(obj.keys) == [
            "installId", "platform", "osVersion", "channel", "appVersion", "build", "arch", "locale",
        ])
        #expect(obj["platform"] as? String == "macos")
        #expect(try JSONDecoder().decode(Heartbeat.self, from: data) == beat)
    }
}
