import Foundation
import Testing

@testable import NucleicProtocol

/// `CodexUsage` — parsing the `codex` `account/rateLimits/updated` snapshot, the stale-window
/// correction, window labels, and Codable round-tripping (it rides both `AgentEvent` and
/// `DashboardSnapshot`).
@Suite struct CodexUsageTests {
    private let now = Date(timeIntervalSince1970: 1_700_000_000)

    @Test func parsesCamelCaseAppServerShape() throws {
        let params: JSONValue = ["rateLimits": [
            "primary": [
                "usedPercent": 12, "windowDurationMins": 10080,
                "resetsAt": 1_700_003_600],
            "secondary": nil]]
        let usage = try #require(CodexUsage.fromRateLimitsNotification(params, now: now))
        #expect(usage.weekly?.utilization == 12)
        #expect(usage.weekly?.windowMinutes == 10080)
        #expect(usage.weekly?.resetsAt == Date(timeIntervalSince1970: 1_700_003_600))
        #expect(usage.peakUtilization == 12)
        #expect(usage.hasData)
    }

    @Test func parsesSnakeCaseCoreShape() throws {
        // The exec/core wire uses snake_case and may nest under `rate_limits`.
        let params: JSONValue = ["rate_limits": [
            "secondary": ["used_percent": 5, "window_minutes": 10080, "resets_in_seconds": 60]]]
        let usage = try #require(CodexUsage.fromRateLimitsNotification(params, now: now))
        #expect(usage.weekly?.utilization == 5)
        #expect(usage.weekly?.resetsAt == now.addingTimeInterval(60))
    }

    @Test func ignoresLegacyShortPrimaryWindow() {
        // On the old two-window schema, `primary` alone is the retired short window.
        let params: JSONValue = ["rateLimits": [
            "primary": ["usedPercent": 42.5, "windowDurationMins": 300]]]
        #expect(CodexUsage.fromRateLimitsNotification(params, now: now) == nil)
    }

    @Test func choosesWeeklyWindowAcrossLegacyTwoWindowShape() throws {
        let params: JSONValue = ["rateLimits": [
            "primary": ["usedPercent": 80, "windowDurationMins": 300],
            "secondary": ["usedPercent": 20, "windowDurationMins": 10080]]]
        let usage = try #require(CodexUsage.fromRateLimitsNotification(params, now: now))
        #expect(usage.weekly?.utilization == 20)
        #expect(usage.weekly?.windowMinutes == 10080)
    }

    @Test func parsesMultiBucketResponseWhenCompatibilityViewIsAbsent() throws {
        let params: JSONValue = ["rateLimitsByLimitId": ["codex": [
            "primary": ["usedPercent": 8, "windowDurationMins": 10080],
            "secondary": nil]]]
        let usage = try #require(CodexUsage.fromRateLimitsNotification(params, now: now))
        #expect(usage.weekly?.utilization == 8)
    }

    @Test func clampsUtilizationAndDropsEmptySnapshots() {
        let over: JSONValue = ["rateLimits": ["secondary": ["usedPercent": 140]]]
        #expect(CodexUsage.fromRateLimitsNotification(over, now: now)?.weekly?.utilization == 100)
        // No usable window ⇒ nil, so a malformed push never displaces a good snapshot.
        #expect(CodexUsage.fromRateLimitsNotification(["rateLimits": [:]], now: now) == nil)
        #expect(CodexUsage.fromRateLimitsNotification(["rateLimits": ["secondary": ["windowMinutes": 10080]]], now: now) == nil)
    }

    @Test func lapsedWindowReadsAsEmpty() {
        let window = CodexUsageWindow(utilization: 80, windowMinutes: 300, resetsAt: now.addingTimeInterval(60))
        #expect(window.utilization(at: now) == 80)                       // before reset: as-is
        #expect(window.utilization(at: now.addingTimeInterval(120)) == 0)  // past reset: freed
        // A window with no reset is always reported as-is.
        let noReset = CodexUsageWindow(utilization: 30, windowMinutes: nil, resetsAt: nil)
        #expect(noReset.utilization(at: now.addingTimeInterval(1_000_000)) == 30)
    }

    @Test func windowLabelsFromLength() {
        #expect(CodexUsageWindow(utilization: 0, windowMinutes: 300, resetsAt: nil).label == "5-hour")
        #expect(CodexUsageWindow(utilization: 0, windowMinutes: 10080, resetsAt: nil).label == "Weekly")
        #expect(CodexUsageWindow(utilization: 0, windowMinutes: nil, resetsAt: nil).label == nil)
    }

    @Test func codableRoundTrips() throws {
        let usage = CodexUsage(
            weekly: CodexUsageWindow(utilization: 12, windowMinutes: 10080, resetsAt: nil))
        let data = try JSONEncoder().encode(usage)
        #expect(try JSONDecoder().decode(CodexUsage.self, from: data) == usage)
    }
}
