import Foundation
import Testing
@testable import CodexBarCore

struct WarpUsageFetcherTests {
    @Test
    func `parses snapshot and aggregates bonus credits`() throws {
        let json = """
        {
          "data": {
            "user": {
              "__typename": "UserOutput",
              "user": {
                "requestLimitInfo": {
                  "isUnlimited": false,
                  "nextRefreshTime": "2026-02-28T19:16:33.462988Z",
                  "requestLimit": 1500,
                  "requestsUsedSinceLastRefresh": 5
                },
                "bonusGrants": [
                  {
                    "requestCreditsGranted": 20,
                    "requestCreditsRemaining": 10,
                    "expiration": "2026-03-01T10:00:00Z"
                  }
                ],
                "workspaces": [
                  {
                    "bonusGrantsInfo": {
                      "grants": [
                        {
                          "requestCreditsGranted": "15",
                          "requestCreditsRemaining": "5",
                          "expiration": "2026-03-15T10:00:00Z"
                        }
                      ]
                    }
                  }
                ]
              }
            }
          }
        }
        """

        let snapshot = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let expectedRefresh = formatter.date(from: "2026-02-28T19:16:33.462988Z")
        let expectedExpiry = ISO8601DateFormatter().date(from: "2026-03-01T10:00:00Z")

        #expect(snapshot.requestLimit == 1500)
        #expect(snapshot.requestsUsed == 5)
        #expect(snapshot.isUnlimited == false)
        #expect(snapshot.nextRefreshTime != nil)
        #expect(abs((snapshot.nextRefreshTime?.timeIntervalSince1970 ?? 0) -
                (expectedRefresh?.timeIntervalSince1970 ?? 0))
            < 0.5)
        #expect(snapshot.bonusCreditsTotal == 35)
        #expect(snapshot.bonusCreditsRemaining == 15)
        #expect(snapshot.bonusNextExpirationRemaining == 10)
        #expect(abs((snapshot.bonusNextExpiration?.timeIntervalSince1970 ?? 0) -
                (expectedExpiry?.timeIntervalSince1970 ?? 0))
            < 0.5)
    }

    @Test
    func `graph QL errors throw API error`() {
        let json = """
        {
          "errors": [
            { "message": "Unauthorized" }
          ]
        }
        """

        #expect {
            _ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
        } throws: { error in
            guard case let WarpUsageError.apiError(code, message) = error else { return false }
            return code == 200 && message.contains("Unauthorized")
        }
    }

    @Test
    func `null unlimited and string numerics parse safely`() throws {
        let json = """
        {
          "data": {
            "user": {
              "__typename": "UserOutput",
              "user": {
                "requestLimitInfo": {
                  "isUnlimited": null,
                  "nextRefreshTime": "2026-02-28T19:16:33Z",
                  "requestLimit": "1500",
                  "requestsUsedSinceLastRefresh": "5"
                }
              }
            }
          }
        }
        """

        let snapshot = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))

        #expect(snapshot.isUnlimited == false)
        #expect(snapshot.requestLimit == 1500)
        #expect(snapshot.requestsUsed == 5)
        #expect(snapshot.nextRefreshTime != nil)
    }

    @Test
    func `unexpected typename returns parse error`() {
        let json = """
        {
          "data": {
            "user": {
              "__typename": "AuthError"
            }
          }
        }
        """

        #expect {
            _ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
        } throws: { error in
            guard case let WarpUsageError.parseFailed(message) = error else { return false }
            return message.contains("Unexpected user type")
        }
    }

    @Test
    func `missing request limit info returns parse error`() {
        let json = """
        {
          "data": {
            "user": {
              "__typename": "UserOutput",
              "user": {}
            }
          }
        }
        """

        #expect {
            _ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
        } throws: { error in
            guard case let WarpUsageError.parseFailed(message) = error else { return false }
            return message.contains("requestLimitInfo")
        }
    }

    @Test
    func `invalid root returns parse error`() {
        let json = """
        [{ "data": {} }]
        """

        #expect {
            _ = try WarpUsageFetcher._parseResponseForTesting(Data(json.utf8))
        } throws: { error in
            guard case let WarpUsageError.parseFailed(message) = error else { return false }
            return message == "Root JSON is not an object."
        }
    }

    @Test
    func `to usage snapshot omits secondary when no bonus credits`() {
        let source = WarpUsageSnapshot(
            requestLimit: 100,
            requestsUsed: 10,
            nextRefreshTime: Date().addingTimeInterval(3600),
            isUnlimited: false,
            updatedAt: Date(),
            bonusCreditsRemaining: 0,
            bonusCreditsTotal: 0,
            bonusNextExpiration: nil,
            bonusNextExpirationRemaining: 0)

        let snapshot = source.toUsageSnapshot()
        #expect(snapshot.secondary == nil)
    }

    @Test
    func `to usage snapshot keeps bonus window when bonus exists`() throws {
        let source = WarpUsageSnapshot(
            requestLimit: 100,
            requestsUsed: 10,
            nextRefreshTime: Date().addingTimeInterval(3600),
            isUnlimited: false,
            updatedAt: Date(),
            bonusCreditsRemaining: 0,
            bonusCreditsTotal: 20,
            bonusNextExpiration: nil,
            bonusNextExpirationRemaining: 0)

        let snapshot = source.toUsageSnapshot()
        let secondary = try #require(snapshot.secondary)
        #expect(secondary.usedPercent == 100)
    }

    @Test
    func `to usage snapshot unlimited primary does not show reset date`() throws {
        let source = WarpUsageSnapshot(
            requestLimit: 0,
            requestsUsed: 0,
            nextRefreshTime: Date().addingTimeInterval(3600),
            isUnlimited: true,
            updatedAt: Date(),
            bonusCreditsRemaining: 0,
            bonusCreditsTotal: 0,
            bonusNextExpiration: nil,
            bonusNextExpirationRemaining: 0)

        let snapshot = source.toUsageSnapshot()
        let primary = try #require(snapshot.primary)
        #expect(primary.resetsAt == nil)
        #expect(primary.resetDescription == "Unlimited")
    }

    @Test
    func `api error summary includes plain text bodies`() {
        // Regression: Warp edge returns 429 with a non-JSON body ("Rate exceeded.") when User-Agent is missing/wrong.
        let summary = WarpUsageFetcher._apiErrorSummaryForTesting(
            statusCode: 429,
            data: Data("Rate exceeded.".utf8))
        #expect(summary.contains("Rate exceeded."))
    }
}
