import Foundation
import Testing
@testable import CodexBarCore

struct OpenCodeUsageParserTests {
    @Test
    func `parses workspace I ds`() {
        let text = ";0x00000089;((self.$R=self.$R||{})[\"codexbar\"]=[]," +
            "($R=>$R[0]=[$R[1]={id:\"wrk_01K6AR1ZET89H8NB691FQ2C2VB\",name:\"Default\",slug:null}])" +
            "($R[\"codexbar\"]))"
        let ids = OpenCodeUsageFetcher.parseWorkspaceIDs(text: text)
        #expect(ids == ["wrk_01K6AR1ZET89H8NB691FQ2C2VB"])
    }

    @Test
    func `parses subscription usage`() throws {
        let text = "$R[16]($R[30],$R[41]={rollingUsage:$R[42]={status:\"ok\",resetInSec:5944,usagePercent:17}," +
            "weeklyUsage:$R[43]={status:\"ok\",resetInSec:278201,usagePercent:75}});"
        let now = Date(timeIntervalSince1970: 0)
        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)
        #expect(snapshot.rollingUsagePercent == 17)
        #expect(snapshot.weeklyUsagePercent == 75)
        #expect(snapshot.rollingResetInSec == 5944)
        #expect(snapshot.weeklyResetInSec == 278_201)
    }

    @Test
    func `parses subscription from JSON with reset at`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let resetAt = now.addingTimeInterval(3600)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let payload: [String: Any] = [
            "usage": [
                "rollingUsage": [
                    "usagePercent": 0.25,
                    "resetAt": formatter.string(from: resetAt),
                ],
                "weeklyUsage": [
                    "usagePercent": 75,
                    "resetInSec": 7200,
                ],
            ],
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.rollingUsagePercent == 25)
        #expect(snapshot.weeklyUsagePercent == 75)
        #expect(snapshot.rollingResetInSec == 3600)
        #expect(snapshot.weeklyResetInSec == 7200)
    }

    @Test(arguments: ["1e309", "1e308"])
    func `ignores reset timestamps outside integer range`(resetAt: String) throws {
        let text = """
        {
          "rollingUsage": { "usagePercent": 17, "resetAt": "\(resetAt)" },
          "weeklyUsage": { "usagePercent": 75, "resetInSec": 7200 }
        }
        """

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(
            text: text,
            now: Date(timeIntervalSince1970: 1_700_000_000))

        #expect(snapshot.rollingUsagePercent == 17)
        #expect(snapshot.weeklyUsagePercent == 75)
        #expect(snapshot.rollingResetInSec == 0)
        #expect(snapshot.weeklyResetInSec == 7200)
    }

    @Test
    func `parses subscription from candidate windows`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let payload: [String: Any] = [
            "windows": [
                "primaryWindow": [
                    "percent": 0.1,
                    "resetInSec": 300,
                ],
                "secondaryWindow": [
                    "percent": 0.5,
                    "resetInSec": 1200,
                ],
            ],
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.rollingUsagePercent == 10)
        #expect(snapshot.weeklyUsagePercent == 50)
        #expect(snapshot.rollingResetInSec == 300)
        #expect(snapshot.weeklyResetInSec == 1200)
    }

    @Test
    func `computes usage percent from totals`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let payload: [String: Any] = [
            "rollingUsage": [
                "used": 25,
                "limit": 100,
                "resetInSec": 600,
            ],
            "weeklyUsage": [
                "used": 50,
                "limit": 200,
                "resetInSec": 3600,
            ],
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.rollingUsagePercent == 25)
        #expect(snapshot.weeklyUsagePercent == 25)
    }

    @Test
    func `parse subscription throws when fields missing`() {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let text = "{\"ok\":true}"

        #expect(throws: OpenCodeUsageError.self) {
            _ = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)
        }
    }

    @Test
    func `renewsAt parses from ISO8601 renewAt key`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let renewAt = now.addingTimeInterval(86400 * 30)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let payload: [String: Any] = [
            "rollingUsage": ["usagePercent": 10, "resetInSec": 600],
            "weeklyUsage": ["usagePercent": 50, "resetInSec": 3600],
            "renewAt": formatter.string(from: renewAt),
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.renewsAt != nil)
        #expect(snapshot.renewsAt == renewAt)
    }

    @Test
    func `renewsAt parses from renew_at key`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let renewAt = now.addingTimeInterval(86400 * 30)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let payload: [String: Any] = [
            "rollingUsage": ["usagePercent": 10, "resetInSec": 600],
            "weeklyUsage": ["usagePercent": 50, "resetInSec": 3600],
            "renew_at": formatter.string(from: renewAt),
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.renewsAt != nil)
        #expect(snapshot.renewsAt == renewAt)
    }

    @Test
    func `renewsAt is nil when absent`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let payload: [String: Any] = [
            "rollingUsage": ["usagePercent": 10, "resetInSec": 600],
            "weeklyUsage": ["usagePercent": 50, "resetInSec": 3600],
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.renewsAt == nil)
    }

    @Test
    func `top level renewAt is preserved for nested usage object`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let renewAt = now.addingTimeInterval(86400 * 30)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let payload: [String: Any] = [
            "renewAt": formatter.string(from: renewAt),
            "usage": [
                "rollingUsage": ["usagePercent": 10, "resetInSec": 600],
                "weeklyUsage": ["usagePercent": 50, "resetInSec": 3600],
            ],
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.renewsAt == renewAt)
    }

    @Test
    func `top level renew_at is preserved for nested usage object`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let renewAt = now.addingTimeInterval(86400 * 30)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let payload: [String: Any] = [
            "renew_at": formatter.string(from: renewAt),
            "usage": [
                "rollingUsage": ["usagePercent": 10, "resetInSec": 600],
                "weeklyUsage": ["usagePercent": 50, "resetInSec": 3600],
            ],
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.renewsAt == renewAt)
    }

    @Test
    func `child renewAt overrides parent renewAt`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let parentRenewAt = now.addingTimeInterval(86400 * 30)
        let childRenewAt = now.addingTimeInterval(86400 * 45)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let payload: [String: Any] = [
            "renewAt": formatter.string(from: parentRenewAt),
            "usage": [
                "renewAt": formatter.string(from: childRenewAt),
                "rollingUsage": ["usagePercent": 10, "resetInSec": 600],
                "weeklyUsage": ["usagePercent": 50, "resetInSec": 3600],
            ],
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)

        #expect(snapshot.renewsAt == childRenewAt)
    }

    @Test
    func `toUsageSnapshot includes renewal NamedRateWindow when renewsAt present`() throws {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let renewAt = now.addingTimeInterval(86400 * 30)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let payload: [String: Any] = [
            "rollingUsage": ["usagePercent": 10, "resetInSec": 600],
            "weeklyUsage": ["usagePercent": 50, "resetInSec": 3600],
            "renewAt": formatter.string(from: renewAt),
        ]
        let data = try JSONSerialization.data(withJSONObject: payload)
        let text = String(data: data, encoding: .utf8) ?? ""

        let snapshot = try OpenCodeUsageFetcher.parseSubscription(text: text, now: now)
        let usage = snapshot.toUsageSnapshot()

        #expect(usage.extraRateWindows != nil)
        #expect(usage.extraRateWindows?.count == 1)
        #expect(usage.extraRateWindows?[0].id == "renewal")
        #expect(usage.extraRateWindows?[0].title == "Renews")
        #expect(usage.extraRateWindows?[0].window.resetsAt == renewAt)
    }

    @Test
    func `toUsageSnapshot maps pay as you go spend to the primary window and cost`() {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let snapshot = OpenCodeUsageSnapshot.payAsYouGo(
            OpenCodeUsageSnapshot.PayAsYouGoUsage(
                monthlyUsageUSD: 15,
                monthlyLimitUSD: 20,
                balanceUSD: 12.5),
            updatedAt: now)

        let usage = snapshot.toUsageSnapshot()

        #expect(usage.primary?.usedPercent == 75)
        #expect(usage.primary?.windowMinutes == 30 * 24 * 60)
        #expect(usage.secondary == nil)
        #expect(usage.providerCost?.used == 15)
        #expect(usage.providerCost?.limit == 20)
        #expect(usage.providerCost?.balance == 12.5)
        #expect(usage.providerCost?.period == "Monthly")
        #expect(usage.providerCost?.currencyCode == "USD")
    }

    @Test
    func `toUsageSnapshot omits the usage window when no monthly limit is configured`() {
        let now = Date(timeIntervalSince1970: 1_700_000_000)
        let snapshot = OpenCodeUsageSnapshot.payAsYouGo(
            OpenCodeUsageSnapshot.PayAsYouGoUsage(
                monthlyUsageUSD: 3,
                monthlyLimitUSD: nil,
                balanceUSD: 1),
            updatedAt: now)

        let usage = snapshot.toUsageSnapshot()

        #expect(usage.primary == nil)
        #expect(usage.providerCost?.used == 3)
        #expect(usage.providerCost?.limit == 0)
        #expect(usage.providerCost?.balance == 1)
    }

    @Test
    func `pay as you go spend above the monthly limit clamps to 100 percent`() {
        let usage = OpenCodeUsageSnapshot.PayAsYouGoUsage(
            monthlyUsageUSD: 25,
            monthlyLimitUSD: 20,
            balanceUSD: 0)

        #expect(usage.usedPercent == 100)
    }
}
