//! Tests for the pure core. Everything here runs offline: the core has no I/O to fake.
const std = @import("std");
const usage = @import("usage");
const iso8601 = usage.iso8601;

const jwt = "eyJhbGciOiJSUzI1NiJ9.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL2F1dGgiOnsiY2hhdGdwdF9hY2NvdW50X2lkIjoiYWNjdF90ZXN0XzEifSwiaHR0cHM6Ly9hcGkub3BlbmFpLmNvbS9wcm9maWxlIjp7ImVtYWlsIjoiVGVzdC5BY2NvdW50QEV4YW1wbGUuQ09NIn19.c2ln";

fn advanceWithState(alloc: std.mem.Allocator, state: ?*usage.State, provider: []const u8, cred: []const u8, resp: []const u8, now_ms: i64) ![]const u8 {
    var w = usage.Writer.init(alloc);
    const outcome = try usage.advance(alloc, state, provider, cred, resp, now_ms);
    try usage.writeStep(&w, outcome);
    return w.items();
}

fn advance(alloc: std.mem.Allocator, provider: []const u8, cred: []const u8, resp: []const u8, now_ms: i64) ![]const u8 {
    return advanceWithState(alloc, null, provider, cred, resp, now_ms);
}

test "iso-8601 parser formats" {
    const cases = [_]struct { s: []const u8, expected: i64 }{
        .{ .s = "2026-09-16T08:00:00.037593+00:00", .expected = 1789545600037 },
        .{ .s = "2026-09-15T23:46:00Z", .expected = 1789515960000 },
        .{ .s = "2026-06-09T11:28:39.481128Z", .expected = 1781004519481 },
        .{ .s = "2026-01-01T00:00:00+05:30", .expected = 1767205800000 },
        .{ .s = "2026-01-01T00:00:00-08:00", .expected = 1767254400000 },
        .{ .s = "2026-01-01T00:00:00.5Z", .expected = 1767225600500 },
        .{ .s = "2026-01-01T00:00:00.12Z", .expected = 1767225600120 },
        .{ .s = "2026-01-01T00:00:00.123Z", .expected = 1767225600123 },
        .{ .s = "2026-01-01T00:00:00.1234Z", .expected = 1767225600123 },
    };

    for (cases) |c| {
        const got = iso8601.parseIso8601(c.s);
        try std.testing.expectEqual(c.expected, got.?);
    }

    try std.testing.expect(iso8601.parseIso8601("") == null);
    try std.testing.expect(iso8601.parseIso8601("not-a-date") == null);
}

test "window labels match the reference formatting" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();
    try std.testing.expectEqualStrings("5h window", (try usage.formatWindowLabel(a, 18_000)).?);
    try std.testing.expectEqualStrings("7d window", (try usage.formatWindowLabel(a, 604_800)).?);
    try std.testing.expectEqualStrings("1h window", (try usage.formatWindowLabel(a, 60)).?);
    try std.testing.expect((try usage.formatWindowLabel(a, 0)) == null);
    try std.testing.expect((try usage.formatWindowLabel(a, -60)) == null);
    try std.testing.expect((try usage.formatWindowLabel(a, null)) == null);
}

test "status thresholds: exhausted at 100, warning at 90" {
    try std.testing.expectEqualStrings("exhausted", usage.statusFor(100));
    try std.testing.expectEqualStrings("exhausted", usage.statusFor(120));
    try std.testing.expectEqualStrings("warning", usage.statusFor(90));
    try std.testing.expectEqualStrings("ok", usage.statusFor(89.9));
    try std.testing.expectEqualStrings("unknown", usage.statusFor(null));
}

test "codex first step requests" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"test-token\"}";
    const text = try advance(a, "openai-codex", cred, "[]", 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"url\":\"https://chatgpt.com/backend-api/wham/usage\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "[\"Authorization\",\"Bearer test-token\"]") != null);
}

test "codex second step report with windows and additional limits" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"test-token\",\"email\":\"user@example.com\"}";
    const body =
        \\[{"status":200,"body":"{\"plan_type\":\"prolite\",\"rate_limit\":{\"primary_window\":{\"used_percent\":100,\"limit_window_seconds\":604800},\"secondary_window\":{\"used_percent\":32.5,\"limit_window_seconds\":18000,\"reset_after_seconds\":3600}},\"additional_rate_limits\":[{\"limit_name\":\"Spark\",\"rate_limit\":{\"primary_window\":{\"used_percent\":0,\"limit_window_seconds\":18000}}}]}"}]
    ;
    const now: i64 = 1_700_000_000_000;
    const text = try advance(a, "openai-codex", cred, body, now);

    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"prolite\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"primary\",\"label\":\"Primary\",\"usedPercent\":100,\"windowLabel\":\"7d window\",\"windowSeconds\":604800,\"status\":\"exhausted\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"Spark.primary\",\"label\":\"Spark · Primary\",\"usedPercent\":0,\"windowLabel\":\"5h window\",\"windowSeconds\":18000,\"scope\":\"Spark\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"resetsAt\":1700003600000") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"email\":\"user@example.com\"") != null);
}

test "anthropic first step requests" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"ant-token\"}";
    const text = try advance(a, "anthropic", cred, "[]", 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"https://api.anthropic.com/api/oauth/usage\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"https://api.anthropic.com/api/oauth/profile\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "[\"anthropic-beta\",\"oauth-2025-04-20\"]") != null);
}

test "anthropic second step report" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"ant-token\"}";
    const body =
        \\[{"status":200,"body":"{\"limits\":[{\"kind\":\"session\",\"percent\":0,\"severity\":\"normal\"},{\"kind\":\"weekly_all\",\"percent\":94,\"severity\":\"critical\",\"resets_at\":\"2026-09-16T08:00:00.037593+00:00\"}],\"extra_usage\":{\"is_enabled\":false,\"credits_ever_enabled\":true}}"},{"status":200,"body":"{\"account\":{\"email\":\"User@Example.Com\",\"uuid\":\"uuid-123\"},\"organization\":{\"seat_tier\":\"team_standard\"}}"}]
    ;
    const text = try advance(a, "anthropic", cred, body, 0);

    try std.testing.expect(std.mem.indexOf(u8, text, "\"email\":\"user@example.com\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"accountId\":\"uuid-123\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"team_standard\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"session\",\"label\":\"Session\",\"usedPercent\":0,\"windowLabel\":\"5h window\",\"windowSeconds\":18000,\"status\":\"ok\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"weekly_all\",\"label\":\"Weekly\",\"usedPercent\":94,\"windowLabel\":\"7d window\",\"windowSeconds\":604800,\"resetsAt\":1789545600037,\"status\":\"warning\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"notes\":[\"Extra usage is off\"]") != null);
}

test "anthropic extra usage enabled" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"ant-token\"}";
    const body =
        \\[{"status":200,"body":"{\"limits\":[],\"extra_usage\":{\"is_enabled\":true,\"monthly_limit\":100,\"used_credits\":25}}"},{"status":200,"body":"{}"}]
    ;
    const text = try advance(a, "anthropic", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"extra_usage\",\"label\":\"Extra usage\",\"usedPercent\":25") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"detail\":\"25 of 100 credits\"") != null);
}

test "anthropic scoped windows carry their scope, and the body's scope wins" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"ant-token\"}";
    const body =
        \\[{"status":200,"body":"{\"limits\":[{\"kind\":\"weekly_opus\",\"percent\":40},{\"kind\":\"daily_all\",\"percent\":5},{\"kind\":\"weekly_sonnet\",\"percent\":1,\"scope\":\"claude-sonnet\"}]}"},{"status":200,"body":"{}"}]
    ;
    const text = try advance(a, "anthropic", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"weekly_opus\",\"label\":\"Weekly (Opus)\",\"usedPercent\":40,\"windowLabel\":\"7d window\",\"windowSeconds\":604800,\"scope\":\"opus\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"daily_all\",\"label\":\"Daily (All)\",\"usedPercent\":5,\"windowLabel\":\"24h window\",\"windowSeconds\":86400,\"status\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"weekly_sonnet\",\"label\":\"Weekly (Sonnet)\",\"usedPercent\":1,\"windowLabel\":\"7d window\",\"windowSeconds\":604800,\"scope\":\"claude-sonnet\"") != null);
}

test "codex additional limits are keyed by their metered feature" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"test-token\"}";
    const body =
        \\[{"status":200,"body":"{\"additional_rate_limits\":[{\"limit_name\":\"GPT-5.3-Codex-Spark\",\"metered_feature\":\"codex_bengalfox\",\"rate_limit\":{\"secondary_window\":{\"used_percent\":3,\"limit_window_seconds\":604800}}}]}"}]
    ;
    const text = try advance(a, "openai-codex", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"codex_bengalfox.secondary\",\"label\":\"GPT-5.3-Codex-Spark · Secondary\",\"usedPercent\":3,\"windowLabel\":\"7d window\",\"windowSeconds\":604800,\"scope\":\"GPT-5.3-Codex-Spark\"") != null);
}

test "zai keys a limit by type and period, and counts unit 3 in hours" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"key\"}";
    const body =
        \\[{"status":200,"body":"{\"code\":200,\"success\":true,\"data\":{\"limits\":[{\"type\":\"TOKENS_LIMIT\",\"unit\":3,\"number\":5,\"percentage\":12},{\"type\":\"TIME_LIMIT\",\"unit\":5,\"number\":1,\"usage\":100,\"currentValue\":0,\"percentage\":0}]}}"}]
    ;
    const text = try advance(a, "zai", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"tokens_limit:3:5\",\"label\":\"Tokens\",\"usedPercent\":12,\"windowLabel\":\"5h window\",\"windowSeconds\":18000,") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"time_limit:5:1\",\"label\":\"Requests\",\"usedPercent\":0,\"detail\"") != null);
}

test "access token claims give account id and a normalized email" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    var w = usage.Writer.init(a);
    const outcome = try usage.claimsFromAccessToken(a, jwt, &w);
    try std.testing.expect(outcome == .ok);
    try std.testing.expectEqualStrings("{\"accountId\":\"acct_test_1\",\"email\":\"test.account@example.com\"}", w.items());

    var broken = usage.Writer.init(a);
    try std.testing.expect((try usage.claimsFromAccessToken(a, "not-a-jwt", &broken)) == .err);
}

test "antigravity first step and report conversion" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"ya29.test\",\"extra\":{\"projectId\":\"test-proj\"}}";
    const req_text = try advance(a, "google-antigravity", cred, "[]", 0);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "https://daily-cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "antigravity/hub/2.8.0") != null);

    // Remaining fraction 0.16592255 converted to 83.4% used and 17% free
    const body =
        \\[{"status":200,"body":"{\"buckets\":[{\"modelId\":\"gemini-2.5-flash\",\"remainingFraction\":0.16592255,\"resetTime\":\"2026-09-15T23:46:00Z\"},{\"modelId\":\"chat_tab_drop\",\"remainingFraction\":1.0}]}"}]
    ;
    const now: i64 = 1_789_514_000_000;
    const rep_text = try advance(a, "google-antigravity", cred, body, now);
    try std.testing.expect(std.mem.indexOf(u8, rep_text, "\"id\":\"gemini\",\"label\":\"Gemini\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, rep_text, "\"scope\":\"Gemini\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, rep_text, "\"usedPercent\":83.4") != null);
    try std.testing.expect(std.mem.indexOf(u8, rep_text, "\"detail\":\"17% free\"") != null);
    // The feed gives a reset time, not a window length, so no window is claimed.
    try std.testing.expect(std.mem.indexOf(u8, rep_text, "\"windowLabel\"") == null);
    try std.testing.expect(std.mem.indexOf(u8, rep_text, "\"notes\":[\"Project: test-proj\"]") != null);
}

test "antigravity HTTP error message unwrapping" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"ya29.test\"}";
    const body =
        \\[{"status":401,"body":"{\"error\":{\"code\":401,\"message\":\"Invalid token\"}}"}]
    ;
    const rep_text = try advance(a, "google-antigravity", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, rep_text, "\"error\":\"HTTP 401: Invalid token\"") != null);
}

test "hyper two-step flow and unit conversion" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    var state = usage.State.init(a);
    defer state.deinit();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"sk-hyper-123\"}";

    // Step 0 -> /v1/teams
    const step0 = try advanceWithState(a, &state, "hyper", cred, "[]", 0);
    try std.testing.expect(std.mem.indexOf(u8, step0, "https://hyper.charm.land/v1/teams") != null);

    // Step 1 -> receives teams, asks for billing and members
    const teams_resp =
        \\[{"status":200,"body":"{\"items\":[{\"id\":\"team_123\",\"name\":\"My Team\"}]}"}]
    ;
    const step1 = try advanceWithState(a, &state, "hyper", cred, teams_resp, 0);
    try std.testing.expect(std.mem.indexOf(u8, step1, "https://hyper.charm.land/v1/teams/team_123/billing") != null);
    try std.testing.expect(std.mem.indexOf(u8, step1, "https://hyper.charm.land/v1/teams/team_123/members") != null);

    // Step 2 -> billing: 237 remaining of 250 credits -> (250-237)/250 = 5.2% used
    const billing_resp =
        \\[{"status":200,"body":"{\"status\":\"active\",\"plan\":{\"name\":\"Shred\",\"hypercredits\":250},\"balances\":{\"plan\":{\"hypercredits\":\"237\"},\"total\":{\"usd\":\"11.83\"}},\"hypercredit_refresh\":{\"last_at\":\"2026-09-15T22:26:09Z\",\"next_at\":\"2026-09-16T22:26:09Z\"}}"},{"status":200,"body":"{\"items\":[{\"email\":\"user@example.com\"}]}"}]
    ;
    const step2 = try advanceWithState(a, &state, "hyper", cred, billing_resp, 0);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"planType\":\"shred\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"accountId\":\"team_123\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"email\":\"user@example.com\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"usedPercent\":5.2") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"detail\":\"237 of 250 credits\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"id\":\"hypercredits\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"windowLabel\":\"24h window\",\"windowSeconds\":86400") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"balance\":\"11.83\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, step2, "\"notes\":[\"Team: My Team\"]") != null);
}

test "opencode-go status mapping and windows" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"opencode-key\"}";
    const body =
        \\[{"status":200,"body":"{\"usage\":{\"rolling\":{\"status\":\"ok\",\"percent\":10,\"resetsAt\":\"2026-09-16T04:40:14Z\"},\"monthly\":{\"status\":\"rate-limited\",\"percent\":50,\"resetsAt\":\"2026-09-29T07:01:56Z\"}}}"}]
    ;
    const text = try advance(a, "opencode-go", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"Go\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"rolling\",\"label\":\"Session\",\"usedPercent\":10,\"windowLabel\":\"5h window\",\"windowSeconds\":18000,\"resetsAt\":1789533614000,\"status\":\"ok\"") != null);
    // rate-limited status -> exhausted even though percent is 50
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"monthly\",\"label\":\"Monthly\",\"usedPercent\":50,\"windowLabel\":\"30d window\",\"windowSeconds\":2592000,\"resetsAt\":1790665316000,\"status\":\"exhausted\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"notes\":[\"OpenCode Go unified models\"]") != null);

    const oauth_cred = "{\"kind\":\"oauth\",\"access\":\"opencode-oauth-tok\",\"email\":\"go@example.com\"}";
    const oauth_req = try advance(a, "opencode-go", oauth_cred, "[]", 0);
    try std.testing.expect(std.mem.indexOf(u8, oauth_req, "[\"Authorization\",\"Bearer opencode-oauth-tok\"]") != null);
    const oauth_rep = try advance(a, "opencode-go", oauth_cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, oauth_rep, "\"email\":\"go@example.com\"") != null);
}

test "zai authorization without Bearer, epoch seconds/ms, and ratio fallback" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"raw-zai-key\"}";
    const req_text = try advance(a, "zai", cred, "[]", 0);
    // Authorization MUST NOT have Bearer prefix
    try std.testing.expect(std.mem.indexOf(u8, req_text, "[\"Authorization\",\"raw-zai-key\"]") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "Bearer") == null);

    // Case 1: TIME_LIMIT with epoch ms (> 1e11)
    // Case 2: TOKENS_LIMIT with epoch seconds (< 1e11) and ratio fallback (cur 25 / total 100 = 25%)
    const body =
        \\[{"status":200,"body":"{\"code\":200,\"success\":true,\"data\":{\"level\":\"lite\",\"limits\":[{\"type\":\"TIME_LIMIT\",\"unit\":5,\"currentValue\":10,\"usage\":100,\"percentage\":10,\"nextResetTime\":1792027143998},{\"type\":\"TOKENS_LIMIT\",\"currentValue\":25,\"number\":100,\"nextResetTime\":1792027143}]}}"}]
    ;
    const text = try advance(a, "zai", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"Lite\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"time_limit\",\"label\":\"Requests\",\"usedPercent\":10,\"resetsAt\":1792027143998,\"detail\":\"10 / 100 reqs\",\"status\":\"ok\"") != null);
    // 1792027143 (< 1e11) converted from epoch seconds to 1792027143000 ms, ratio 25%
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"tokens_limit\",\"label\":\"Tokens\",\"usedPercent\":25,\"windowLabel\":\"5h window\",\"resetsAt\":1792027143000,\"status\":\"ok\"") != null);
}

test "zai error return when code != 200" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"key\"}";
    const body =
        \\[{"status":200,"body":"{\"code\":401,\"success\":false,\"msg\":\"Token invalid\"}"}]
    ;
    const text = try advance(a, "zai", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"error\":\"Token invalid\"") != null);
}

test "deepseek balance formatting and unavailable account note" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"ds-key\"}";
    const body =
        \\[{"status":200,"body":"{\"is_available\":false,\"balance_infos\":[{\"currency\":\"USD\",\"total_balance\":\"0.03\"}]}"}]
    ;
    const text = try advance(a, "deepseek", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"Pay-as-you-go\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"balance\":\"$0.03 USD\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"limits\":[]") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"notes\":[\"Account is unavailable\"]") != null);
}

test "alibaba CLI answer parse: fraction to percent and epoch ms kept" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"cli\",\"config\":{\"region\":\"intl-personal\"},\"accountId\":\"custom-id\",\"email\":\"dev@example.com\"}";
    const cli_body =
        \\[{"status":0,"body":"{\"per5HourPercentage\":0.5,\"per1WeekPercentage\":0.83603712139856,\"per1WeekResetTime\":1790021160000}"}]
    ;
    const text = try advance(a, "alibaba", cred, cli_body, 1_700_000_000_000);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"id\":\"five_hour\",\"label\":\"Session\",\"usedPercent\":50,\"windowLabel\":\"5h window\",\"windowSeconds\":18000,\"status\":\"ok\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"label\":\"Weekly\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"resetsAt\":1790021160000") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"windowLabel\":\"7d window\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"notes\":[\"read through the bailian CLI (intl-personal)\"]") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"accountId\":\"custom-id\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"email\":\"dev@example.com\"") != null);
}
test "alibaba wrapped console parse" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const raw_json =
        \\{"data":{"DataV2":{"data":{"data":{"per1WeekPercentage":0.83603712139856,"per1WeekResetTime":1790021160000}}}}}
    ;
    const parsed_val = try std.json.parseFromSlice(std.json.Value, a, raw_json, .{});
    const parsed = try usage.alibaba.parseAlibabaUsage(a, parsed_val.value);
    try std.testing.expectEqual(@as(usize, 1), parsed.limits.len);
    try std.testing.expectEqualStrings("Weekly", parsed.limits[0].label);
    try std.testing.expectEqualStrings("7d window", parsed.limits[0].windowLabel.?);
    try std.testing.expectEqual(@as(i64, 1790021160000), parsed.limits[0].resetsAt.?);
    try std.testing.expect(parsed.limits[0].usedPercent.? > 83.6 and parsed.limits[0].usedPercent.? < 83.7);
}

test "alibaba argument vector for two regions" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    // intl-personal
    const cred_intl = "{\"kind\":\"cli\",\"config\":{\"region\":\"intl-personal\"}}";
    const req_intl = try advance(a, "alibaba", cred_intl, "[]", 0);
    try std.testing.expect(std.mem.indexOf(u8, req_intl, "\"kind\":\"command\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_intl, "\"command\":\"bl\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_intl, "\"ap-southeast-1\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_intl, "\"international\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_intl, "\"timeoutMs\":15000") != null);

    // cn-personal
    const cred_cn = "{\"kind\":\"cli\",\"config\":{\"region\":\"cn-personal\"}}";
    const req_cn = try advance(a, "alibaba", cred_cn, "[]", 0);
    try std.testing.expect(std.mem.indexOf(u8, req_cn, "\"cn-beijing\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_cn, "\"domestic\"") != null);
}

test "alibaba CLI failure quotes a JSON error body's message and hint" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"cli\",\"config\":{\"region\":\"intl-personal\"}}";
    const body = "[{\"status\":3,\"body\":\"{\\n  \\\"error\\\": {\\n    \\\"code\\\": 3,\\n    \\\"message\\\": \\\"No console access token found.\\\",\\n    \\\"hint\\\": \\\"Run `bl auth login --console`.\\\"\\n  }\\n}\"}]";
    const text = try advance(a, "alibaba", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "bailian CLI failed (exit 3)") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "No console access token found. Run `bl auth login --console`.") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, ": {") == null);
}

test "alibaba CLI failure stays the bare exit code when the JSON body has no message" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"cli\",\"config\":{\"region\":\"intl-personal\"}}";
    const body = "[{\"status\":3,\"body\":\"{\\\"trace\\\":\\\"abc\\\"}\"}]";
    const text = try advance(a, "alibaba", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "bailian CLI failed (exit 3)\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "trace") == null);
}

test "alibaba 127 with session fallback" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"session\",\"session\":{\"secToken\":\"sec123\",\"region\":\"ap-southeast-1\"}}";
    const resp_127 = "[{\"status\":127,\"body\":\"this host does not run commands (bl)\"}]";
    const req_text = try advance(a, "alibaba", cred, resp_127, 0);

    // 4 console HTTP requests issued
    try std.testing.expect(std.mem.indexOf(u8, req_text, "\"kind\":\"http\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "zeldaHttp.apikeyMgr.%2Ftokenplan%2Fpersonal%2Fapi%2Fv2%2Fusage") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "zeldaHttp.apikeyMgr.%2Ftokenplan%2Fpersonal%2Fapi%2Fv2%2Fsubscription") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "zeldaHttp.apikeyMgr.%2Ftokenplan%2Fpersonal%2Fapi%2Fv2%2Fquota-config") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "zeldaHttp.apikeyMgr.%2Ftokenplan%2Fpersonal%2Fapi%2Fv2%2Freset-card%2Flist") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "sec_token=sec123") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "region=ap-southeast-1") != null);
}

test "alibaba 127 without session error" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"cli\"}";
    const resp_127 = "[{\"status\":127,\"body\":\"this host does not run commands (bl)\"}]";
    const text = try advance(a, "alibaba", cred, resp_127, 0);

    try std.testing.expect(std.mem.indexOf(u8, text, "capture-alibaba-session.mjs") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "prime-agent") != null);
}

test "alibaba missing sec_token is omitted from form body" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"session\",\"session\":{\"region\":\"ap-southeast-1\"}}";
    const resp_127 = "[{\"status\":127,\"body\":\"no commands\"}]";
    const req_text = try advance(a, "alibaba", cred, resp_127, 0);

    try std.testing.expect(std.mem.indexOf(u8, req_text, "\"kind\":\"http\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "params=") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "region=ap-southeast-1") != null);
    try std.testing.expect(std.mem.indexOf(u8, req_text, "sec_token") == null);
}

test "alibaba console answers: plan, remaining days, tier credits and reset cards" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"session\",\"session\":{\"secToken\":\"sec123\",\"region\":\"ap-southeast-1\"}}";
    const body =
        \\[{"status":200,"body":"{\"data\":{\"DataV2\":{\"data\":{\"data\":{\"per1WeekPercentage\":0.83603712139856,\"per1WeekResetTime\":1790021160000}}}}}"},{"status":200,"body":"{\"data\":{\"DataV2\":{\"data\":{\"data\":{\"specCode\":\"pro\",\"instanceCode\":\"inst-123\",\"remainingDays\":29}}}}}"},{"status":200,"body":"{\"data\":{\"DataV2\":{\"data\":{\"data\":{\"pro\":{\"weekly\":40000,\"five_hour\":12000}}}}}}"},{"status":200,"body":"{\"data\":{\"DataV2\":{\"data\":{\"data\":[{\"cardId\":\"c1\"}]}}}}"}]
    ;

    const text = try advance(a, "alibaba", cred, body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"pro\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"accountId\":\"inst-123\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"label\":\"Weekly\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"resetsAt\":1790021160000") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"windowLabel\":\"7d window\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "Token plan pro: weekly 40000, 5h 12000 credits") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "29 days remaining in the plan") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, "1 reset cards available") != null);
}


test "hyper state persists team across rounds even when arena is freed" {
    var state = usage.State.init(std.testing.allocator);
    defer state.deinit();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"sk-hyper-123\"}";
    const teams_resp =
        \\[{"status":200,"body":"{\"items\":[{\"id\":\"team_live_123\",\"name\":\"Hyper Squad\"}]}"}]
    ;

    // Round 1 in its own arena, which is deinitialized immediately after
    {
        var arena1 = std.heap.ArenaAllocator.init(std.testing.allocator);
        defer arena1.deinit();
        const step1 = try advanceWithState(arena1.allocator(), &state, "hyper", cred, teams_resp, 0);
        try std.testing.expect(std.mem.indexOf(u8, step1, "team_live_123/billing") != null);
    }
    // arena1 is now dead and its memory is released

    // Round 2 in a fresh arena: must read team_id and team_name from state, not freed memory
    {
        var arena2 = std.heap.ArenaAllocator.init(std.testing.allocator);
        defer arena2.deinit();
        const billing_resp =
            \\[{"status":200,"body":"{\"status\":\"active\",\"plan\":{\"name\":\"Pro\",\"hypercredits\":500},\"balances\":{\"plan\":{\"hypercredits\":\"450\"},\"total\":{\"usd\":\"25.00\"}}}"},{"status":200,"body":"{\"items\":[{\"email\":\"member@example.com\"}]}"}]
        ;
        const step2 = try advanceWithState(arena2.allocator(), &state, "hyper", cred, billing_resp, 0);
        try std.testing.expect(std.mem.indexOf(u8, step2, "\"accountId\":\"team_live_123\"") != null);
        try std.testing.expect(std.mem.indexOf(u8, step2, "Team: Hyper Squad") != null);
        try std.testing.expect(std.mem.indexOf(u8, step2, "\"planType\":\"pro\"") != null);
    }

    // Passing NULL state to round 2 does not panic, but cannot emit team name
    {
        var arena3 = std.heap.ArenaAllocator.init(std.testing.allocator);
        defer arena3.deinit();
        const billing_resp =
            \\[{"status":200,"body":"{\"status\":\"active\",\"plan\":{\"name\":\"Pro\",\"hypercredits\":500},\"balances\":{\"plan\":{\"hypercredits\":\"450\"}}}"},{"status":200,"body":"{\"items\":[]}"}]
        ;
        const step_no_state = try advanceWithState(arena3.allocator(), null, "hyper", cred, billing_resp, 0);
        try std.testing.expect(std.mem.indexOf(u8, step_no_state, "Team: Hyper Squad") == null);
    }
}

test "number safety: NaN does not panic integer cast or emit invalid JSON" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"tok\"}";
    const nan_body =
        \\[{"status":200,"body":"{\"plan_type\":\"plus\",\"rate_limit\":{\"primary_window\":{\"used_percent\":\"nan\",\"reset_at\":\"nan\"}}}"}]
    ;
    const text = try advance(a, "openai-codex", cred, nan_body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"plus\"") != null);
    try std.testing.expect(std.mem.indexOf(u8, text, ":nan") == null);
    try std.testing.expect(std.mem.indexOf(u8, text, ":NaN") == null);
    _ = try std.json.parseFromSlice(std.json.Value, a, text, .{});
}

test "number safety: inf does not serialize as usedPercent:inf" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"oauth\",\"access\":\"tok\"}";
    const inf_body =
        \\[{"status":200,"body":"{\"plan_type\":\"plus\",\"rate_limit\":{\"primary_window\":{\"used_percent\":\"inf\"}}}"}]
    ;
    const text = try advance(a, "openai-codex", cred, inf_body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, ":inf") == null);
    try std.testing.expect(std.mem.indexOf(u8, text, ":Infinity") == null);
    _ = try std.json.parseFromSlice(std.json.Value, a, text, .{});
}

test "number safety: out-of-range 1e30 does not panic integer cast" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"key\"}";
    const big_body =
        \\[{"status":200,"body":"{\"code\":200,\"success\":true,\"data\":{\"level\":\"pro\",\"limits\":[{\"type\":\"TIME_LIMIT\",\"percentage\":50,\"nextResetTime\":1e30,\"currentValue\":1e30,\"usage\":1e30}]}}"}]
    ;
    const text = try advance(a, "zai", cred, big_body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"Pro\"") != null);
    _ = try std.json.parseFromSlice(std.json.Value, a, text, .{});
}

test "number safety: non-number string in numeric field does not panic" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    const cred = "{\"kind\":\"apiKey\",\"apiKey\":\"key\"}";
    const string_body =
        \\[{"status":200,"body":"{\"code\":200,\"success\":true,\"data\":{\"level\":\"pro\",\"limits\":[{\"type\":\"TIME_LIMIT\",\"percentage\":\"not-a-number\",\"currentValue\":\"bad\"}]}}"}]
    ;
    const text = try advance(a, "zai", cred, string_body, 0);
    try std.testing.expect(std.mem.indexOf(u8, text, "\"planType\":\"Pro\"") != null);
    _ = try std.json.parseFromSlice(std.json.Value, a, text, .{});
}

test "json escaping: a quote or backslash in a value stays parseable" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    var w = usage.Writer.init(a);
    try usage.writeJsonString(&w, "Team: Hyper \"Squad\" \\ back");
    const text = w.items();
    // The whole point of the escape table: a value the provider controls must not be able to end
    // the JSON string early.
    const parsed = try std.json.parseFromSlice([]const u8, a, text, .{});
    try std.testing.expectEqualStrings("Team: Hyper \"Squad\" \\ back", parsed.value);
}

test "reset already in the past keeps the reference's 1h window" {
    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
    defer arena.deinit();
    const a = arena.allocator();

    // driver.ts epochToWindowLabel is `Math.max(1, Math.round((resetsAt - nowMs) / 1000))`, so a
    // past reset still names a window instead of vanishing from the report. Parity depends on this.
    const overdue = try usage.epochToWindowLabel(a, 1_000_000, 9_000_000);
    try std.testing.expect(overdue != null);
    try std.testing.expectEqualStrings("1h window", overdue.?);
}
