import Foundation
import Testing
@testable import CodexBarCore

/// Coverage for `RateWindow.isSyntheticPlaceholder` — the boundary marker that lets lane classifiers
/// distinguish Claude web's `five_hour: null` placeholder from a real zero-usage session. Verifies the
/// marker is set at the web boundary, survives Codable (with backward compatibility), and survives the
/// reset backfill that previously defeated a shape-only heuristic.
struct RateWindowSyntheticPlaceholderTests {
    @Test
    func `synthetic placeholder flag round-trips through Codable`() throws {
        let window = RateWindow(
            usedPercent: 0,
            windowMinutes: 300,
            resetsAt: nil,
            resetDescription: nil,
            isSyntheticPlaceholder: true)

        let data = try JSONEncoder().encode(window)
        let decoded = try JSONDecoder().decode(RateWindow.self, from: data)

        #expect(decoded.isSyntheticPlaceholder == true)
        #expect(decoded.usedPercent == 0)
        #expect(decoded.windowMinutes == 300)
    }

    @Test
    func `older payload without the flag decodes as not a placeholder`() throws {
        // Cached payloads written before the flag existed have no `isSyntheticPlaceholder` key.
        let json = #"{"usedPercent": 50, "windowMinutes": 300}"#
        let decoded = try JSONDecoder().decode(RateWindow.self, from: Data(json.utf8))

        #expect(decoded.isSyntheticPlaceholder == false)
        #expect(decoded.usedPercent == 50)
        #expect(decoded.windowMinutes == 300)
    }

    @Test
    func `a real window omits the placeholder flag when encoded`() throws {
        let window = RateWindow(usedPercent: 50, windowMinutes: 300, resetsAt: nil, resetDescription: nil)

        let data = try JSONEncoder().encode(window)
        let json = String(bytes: data, encoding: .utf8) ?? ""

        // The flag is only persisted when true, so real windows keep their prior on-disk shape.
        #expect(json.contains("isSyntheticPlaceholder") == false)
    }

    @Test
    func `backfilling a reset preserves the synthetic placeholder flag`() {
        // Regression: backfilling a still-future cached reset onto the placeholder (which has no reset)
        // must NOT let it masquerade as a real session — the marker has to survive the backfill.
        let now = Date(timeIntervalSince1970: 1_800_000_000)
        let cached = RateWindow(
            usedPercent: 12,
            windowMinutes: 300,
            resetsAt: now.addingTimeInterval(3600),
            resetDescription: "Resets in 1h")
        let placeholder = RateWindow(
            usedPercent: 0,
            windowMinutes: 300,
            resetsAt: nil,
            resetDescription: nil,
            isSyntheticPlaceholder: true)

        let result = placeholder.backfillingResetTime(from: cached, now: now)

        #expect(result.resetsAt == now.addingTimeInterval(3600))
        #expect(result.isSyntheticPlaceholder == true)
    }

    @Test
    func `web mapping flags the null five-hour session as a synthetic placeholder`() throws {
        let json = """
        {
          "five_hour": null,
          "seven_day": { "utilization": 42, "resets_at": "2025-12-29T23:00:00.000Z" }
        }
        """
        let webData = try ClaudeWebAPIFetcher._parseUsageResponseForTesting(Data(json.utf8))
        let primary = ClaudeUsageFetcher.webPrimaryWindow(from: webData)

        #expect(primary.isSyntheticPlaceholder == true)
        #expect(primary.usedPercent == 0)
        #expect(primary.windowMinutes == 300)
        #expect(primary.resetsAt == nil)
    }

    @Test
    func `web mapping keeps a real five-hour session unflagged`() throws {
        let json = """
        {
          "five_hour": { "utilization": 11, "resets_at": "2025-12-29T20:00:00.000Z" },
          "seven_day": { "utilization": 42, "resets_at": "2025-12-29T23:00:00.000Z" }
        }
        """
        let webData = try ClaudeWebAPIFetcher._parseUsageResponseForTesting(Data(json.utf8))
        let primary = ClaudeUsageFetcher.webPrimaryWindow(from: webData)

        #expect(primary.isSyntheticPlaceholder == false)
        #expect(primary.usedPercent == 11)
    }

    @Test
    func `web mapping keeps fractional session and weekly utilization`() throws {
        let json = """
        {
          "five_hour": { "utilization": 45.5, "resets_at": "2025-12-29T20:00:00.000Z" },
          "seven_day": { "utilization": 12.25, "resets_at": "2025-12-29T23:00:00.000Z" }
        }
        """
        let webData = try ClaudeWebAPIFetcher._parseUsageResponseForTesting(Data(json.utf8))
        #expect(webData.sessionPercentUsed == 45.5)
        #expect(webData.weeklyPercentUsed == 12.25)
        #expect(webData.hasLiveSessionWindow == true)

        let primary = ClaudeUsageFetcher.webPrimaryWindow(from: webData)
        #expect(primary.isSyntheticPlaceholder == false)
        #expect(primary.usedPercent == 45.5)
        #expect(primary.remainingPercent == 54.5)
    }

    @Test
    func `web mapping keeps a real zero-usage session that omits a reset`() throws {
        // A reported `five_hour` object at 0% with no `resets_at` is a real idle session, not the
        // `five_hour: null` placeholder. The flag keys off object presence (not percent/reset), so this
        // must stay unflagged — otherwise the combined metric would hide a genuine empty session.
        let json = """
        {
          "five_hour": { "utilization": 0 },
          "seven_day": { "utilization": 42, "resets_at": "2025-12-29T23:00:00.000Z" }
        }
        """
        let webData = try ClaudeWebAPIFetcher._parseUsageResponseForTesting(Data(json.utf8))
        let primary = ClaudeUsageFetcher.webPrimaryWindow(from: webData)

        #expect(primary.isSyntheticPlaceholder == false)
        #expect(primary.usedPercent == 0)
        #expect(primary.resetsAt == nil)
    }
}
