//! Alibaba Model Studio usage feed implementation.
//!
//! Mirrors prime-agent:packages/ai/src/utils/usage/providers/alibaba.ts field for field.
const std = @import("std");
const usage = @import("../usage.zig");

pub const AlibabaRegion = enum {
    intl,
    cn,
    @"intl-personal",
    @"cn-personal",

    pub fn toString(self: AlibabaRegion) []const u8 {
        return switch (self) {
            .intl => "intl",
            .cn => "cn",
            .@"intl-personal" => "intl-personal",
            .@"cn-personal" => "cn-personal",
        };
    }

    pub fn fromString(str: []const u8) ?AlibabaRegion {
        if (std.mem.eql(u8, str, "intl")) return .intl;
        if (std.mem.eql(u8, str, "cn")) return .cn;
        if (std.mem.eql(u8, str, "intl-personal")) return .@"intl-personal";
        if (std.mem.eql(u8, str, "cn-personal")) return .@"cn-personal";
        return null;
    }
};

pub const RegionInfo = struct {
    currentRegionId: []const u8,
    cliConsoleSite: []const u8,
    quotaHost: []const u8,
    productCode: []const u8,
};

pub fn getRegionInfo(region: AlibabaRegion) RegionInfo {
    return switch (region) {
        .intl => .{
            .currentRegionId = "ap-southeast-1",
            .cliConsoleSite = "international",
            .quotaHost = "https://modelstudio.console.alibabacloud.com",
            .productCode = "sfm_tokenplanteams_dp_intl",
        },
        .cn => .{
            .currentRegionId = "cn-beijing",
            .cliConsoleSite = "domestic",
            .quotaHost = "https://bailian.console.aliyun.com",
            .productCode = "sfm_tokenplanteams_dp_cn",
        },
        .@"intl-personal" => .{
            .currentRegionId = "ap-southeast-1",
            .cliConsoleSite = "international",
            .quotaHost = "https://bailian-singapore-cs.alibabacloud.com",
            .productCode = "sfm_tokenplansolo_public_intl",
        },
        .@"cn-personal" => .{
            .currentRegionId = "cn-beijing",
            .cliConsoleSite = "domestic",
            .quotaHost = "https://bailian-cs.console.aliyun.com",
            .productCode = "sfm_tokenplansolo_public_cn",
        },
    };
}

pub const ALIBABA_CLI_TIMEOUT_MS: i64 = 15_000;

pub fn alibabaCliArguments(alloc: std.mem.Allocator, region: AlibabaRegion) ![]const []const u8 {
    const info = getRegionInfo(region);
    const args = try alloc.alloc([]const u8, 8);
    args[0] = "usage";
    args[1] = "token-plan";
    args[2] = "--console-region";
    args[3] = info.currentRegionId;
    args[4] = "--console-site";
    args[5] = info.cliConsoleSite;
    args[6] = "--output";
    args[7] = "json";
    return args;
}

pub const ALIBABA_USAGE_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage";
pub const ALIBABA_SUBSCRIPTION_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/subscription";
pub const ALIBABA_QUOTA_CONFIG_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/quota-config";
pub const ALIBABA_RESET_CARD_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/reset-card/list";



/// Unwrap the console's envelope (`data.DataV2.data.data`) when present. The CLI answers with the
/// inner payload directly, so one parser covers both.
pub fn unwrapEnvelope(value: ?std.json.Value) ?std.json.Value {
    const root = usage.asObject(value) orelse return value;
    const data = usage.asObject(root.get("data"));
    if (data) |d| {
        const data_v2 = usage.asObject(d.get("DataV2"));
        if (data_v2) |v2| {
            const v2_data = usage.asObject(v2.get("data"));
            if (v2_data) |v2d| {
                if (v2d.get("data")) |inner| return inner;
            }
        }
        if (d.get("data")) |inner| return inner;
    }
    return value;
}

pub const ParseUsageResult = struct {
    limits: []const usage.UsageLimit = &[_]usage.UsageLimit{},
    notes: []const []const u8 = &[_][]const u8{},
};

pub fn parseAlibabaUsage(alloc: std.mem.Allocator, payload: ?std.json.Value) !ParseUsageResult {
    const inner_val = unwrapEnvelope(payload);
    const inner = usage.asObject(inner_val);
    var limits = std.ArrayList(usage.UsageLimit).empty;

    if (inner) |ud| {
        // Session: per5HourPercentage (0..1 fraction) -> percent
        if (usage.asNumber(ud.get("per5HourPercentage"))) |sf| {
            const used_pct = sf * 100.0;
            const reset = usage.asNumber(ud.get("per5HourResetTime"));
            const reset_ms = usage.toInt(i64, reset);
            try limits.append(alloc, .{
                .id = "five_hour",
                .label = "Session",
                .usedPercent = used_pct,
                .windowLabel = "5h window",
                .windowSeconds = 5 * 3600,
                .resetsAt = reset_ms,
                .status = usage.statusFor(used_pct),
            });
        }

        // Weekly: per1WeekPercentage (0..1 fraction) -> percent
        if (usage.asNumber(ud.get("per1WeekPercentage"))) |wf| {
            const used_pct = wf * 100.0;
            const reset = usage.asNumber(ud.get("per1WeekResetTime"));
            const reset_ms = usage.toInt(i64, reset);
            try limits.append(alloc, .{
                .id = "weekly",
                .label = "Weekly",
                .usedPercent = used_pct,
                .windowLabel = "7d window",
                .windowSeconds = 7 * 86_400,
                .resetsAt = reset_ms,
                .status = usage.statusFor(used_pct),
            });
        }
    }

    return .{
        .limits = limits.items,
        .notes = &[_][]const u8{},
    };
}

pub fn parseAlibabaQuotaConfig(
    alloc: std.mem.Allocator,
    payload: ?std.json.Value,
    plan_type: ?[]const u8,
) ![]const []const u8 {
    const inner_val = unwrapEnvelope(payload);
    const tiers = usage.asObject(inner_val) orelse return &[_][]const u8{};
    const pt = plan_type orelse return &[_][]const u8{};

    const lowered = try alloc.alloc(u8, pt.len);
    _ = std.ascii.lowerString(lowered, pt);

    const tier = usage.asObject(tiers.get(lowered)) orelse return &[_][]const u8{};
    const weekly = usage.asNumber(tier.get("weekly"));
    const five_hour = usage.asNumber(tier.get("five_hour"));

    if (weekly == null and five_hour == null) return &[_][]const u8{};

    var note_buf = std.ArrayList(u8).empty;
    try note_buf.appendSlice(alloc, "Token plan ");
    try note_buf.appendSlice(alloc, pt);
    try note_buf.appendSlice(alloc, ": ");

    var parts_wrote = false;
    if (usage.toInt(i64, weekly)) |w| {
        const w_str = try std.fmt.allocPrint(alloc, "weekly {d}", .{w});
        try note_buf.appendSlice(alloc, w_str);
        parts_wrote = true;
    }
    if (usage.toInt(i64, five_hour)) |fh| {
        if (parts_wrote) try note_buf.appendSlice(alloc, ", ");
        const fh_str = try std.fmt.allocPrint(alloc, "5h {d}", .{fh});
        try note_buf.appendSlice(alloc, fh_str);
    }
    try note_buf.appendSlice(alloc, " credits");

    const notes = try alloc.alloc([]const u8, 1);
    notes[0] = note_buf.items;
    return notes;
}

pub const ConsoleSession = struct {
    host: []const u8,
    region: []const u8,
    secToken: ?[]const u8 = null,
    headers: ?std.json.ObjectMap = null,
};

pub fn parseConsoleSession(session_val: ?std.json.Value, default_host: []const u8) ?ConsoleSession {
    const session = usage.asObject(session_val) orelse return null;
    const region = usage.stringOf(session.get("region")) orelse return null;
    const host = usage.stringOf(session.get("url")) orelse default_host;
    const secToken = usage.stringOf(session.get("secToken"));
    const headers = usage.asObject(session.get("headers"));
    return .{
        .host = host,
        .region = region,
        .secToken = secToken,
        .headers = headers,
    };
}

pub fn regionFor(cred_root: ?std.json.ObjectMap, session: ?ConsoleSession) AlibabaRegion {
    const root = cred_root orelse return .@"intl-personal";
    const kind = usage.asString(root.get("kind")) orelse "";
    if (std.mem.eql(u8, kind, "cli")) {
        const config = usage.asObject(root.get("config"));
        if (config) |c| {
            if (usage.stringOf(c.get("region"))) |r_str| {
                if (AlibabaRegion.fromString(r_str)) |r| return r;
            }
        }
    }
    if (std.mem.eql(u8, kind, "session") and session != null) {
        if (std.mem.startsWith(u8, session.?.region, "cn")) {
            return .@"cn-personal";
        } else {
            return .@"intl-personal";
        }
    }
    return .@"intl-personal";
}

pub fn buildGatewayUrl(alloc: std.mem.Allocator, host: []const u8, api: []const u8) ![]const u8 {
    const base = if (std.mem.indexOf(u8, host, "/data/api.json") != null)
        host
    else
        try std.fmt.allocPrint(alloc, "{s}/data/api.json", .{host});

    const clean_base = if (std.mem.indexOfScalar(u8, base, '?')) |idx| base[0..idx] else base;
    const enc_api = try usage.urlEncode(alloc, api);
    return try std.fmt.allocPrint(alloc, "{s}?action=IntlBroadScopeAspnGateway&product=sfm_bailian&api={s}&_v=", .{ clean_base, enc_api });
}

pub fn buildFormBody(
    alloc: std.mem.Allocator,
    api: []const u8,
    region: AlibabaRegion,
    session: ConsoleSession,
) ![]const u8 {
    const info = getRegionInfo(region);
    var pw = usage.Writer.init(alloc);
    try pw.raw("{\"Api\":");
    try pw.string(api);
    try pw.raw(",\"V\":\"1.0\",\"Data\":{\"cornerstoneParam\":{\"protocol\":\"V2\",\"console\":\"ONE_CONSOLE\",\"productCode\":\"p_efm\",\"domain\":\"modelstudio.console.alibabacloud.com\",\"consoleSite\":\"MODELSTUDIO_ALBABACLOUD\",\"xsp_lang\":\"en-US\",\"feUrl\":");
    const fe_url = try std.fmt.allocPrint(alloc, "https://modelstudio.console.alibabacloud.com/{s}/subscription/token-plan/personal", .{info.currentRegionId});
    try pw.string(fe_url);
    try pw.raw("}");

    if (std.mem.eql(u8, api, ALIBABA_SUBSCRIPTION_API)) {
        try pw.raw(",\"queryInstanceInfoRequest\":{\"commodityCode\":");
        try pw.string(info.productCode);
        try pw.raw("}");
    }
    try pw.raw("}}");

    const enc_params = try usage.urlEncode(alloc, pw.items());
    const enc_region = try usage.urlEncode(alloc, session.region);

    if (session.secToken) |tok| {
        if (tok.len > 0) {
            const enc_tok = try usage.urlEncode(alloc, tok);
            return try std.fmt.allocPrint(alloc, "params={s}&region={s}&sec_token={s}", .{ enc_params, enc_region, enc_tok });
        }
    }
    return try std.fmt.allocPrint(alloc, "params={s}&region={s}", .{ enc_params, enc_region });
}

pub fn consoleRequests(
    alloc: std.mem.Allocator,
    session: ConsoleSession,
    region: AlibabaRegion,
) ![]const usage.UsageRequest {
    const apis = [_][]const u8{
        ALIBABA_USAGE_API,
        ALIBABA_SUBSCRIPTION_API,
        ALIBABA_QUOTA_CONFIG_API,
        ALIBABA_RESET_CARD_API,
    };

    var base_headers = std.ArrayList([2][]const u8).empty;
    if (session.headers) |sh| {
        var it = sh.iterator();
        while (it.next()) |entry| {
            if (usage.asString(entry.value_ptr.*)) |val| {
                const key_lower = try alloc.alloc(u8, entry.key_ptr.*.len);
                _ = std.ascii.lowerString(key_lower, entry.key_ptr.*);
                if (!std.mem.eql(u8, key_lower, "content-type") and !std.mem.eql(u8, key_lower, "accept") and !std.mem.eql(u8, key_lower, "origin") and !std.mem.eql(u8, key_lower, "referer")) {
                    try base_headers.append(alloc, .{ key_lower, val });
                }
            }
        }
    }

    try base_headers.append(alloc, .{ "content-type", "application/x-www-form-urlencoded" });
    try base_headers.append(alloc, .{ "accept", "*/*" });
    try base_headers.append(alloc, .{ "origin", "https://modelstudio.console.alibabacloud.com" });
    const referer = try std.fmt.allocPrint(alloc, "https://modelstudio.console.alibabacloud.com/{s}/subscription/token-plan/personal", .{session.region});
    try base_headers.append(alloc, .{ "referer", referer });

    const requests = try alloc.alloc(usage.UsageRequest, apis.len);
    for (apis, 0..) |api, i| {
        const url = try buildGatewayUrl(alloc, session.host, api);
        const body = try buildFormBody(alloc, api, region, session);
        requests[i] = .{
            .kind = .http,
            .method = "POST",
            .url = url,
            .headers = base_headers.items,
            .body = body,
        };
    }
    return requests;
}

fn getResetCardsCount(body_str: ?[]const u8, alloc: std.mem.Allocator) ?usize {
    const bs = body_str orelse return null;
    const parsed = std.json.parseFromSlice(std.json.Value, alloc, bs, .{}) catch return null;
    if (usage.asArray(parsed.value)) |arr| {
        return arr.items.len;
    }
    const unwrapped = unwrapEnvelope(parsed.value);
    if (usage.asArray(unwrapped)) |arr| {
        return arr.items.len;
    }
    return null;
}

pub fn reportFromConsole(
    alloc: std.mem.Allocator,
    cred_root: ?std.json.ObjectMap,
    resp_array: std.json.Array,
) !usage.Outcome {
    const cred_email = if (cred_root) |cr| usage.stringOf(cr.get("email")) else null;

    if (resp_array.items.len == 0) {
        return .{ .report = .{ .error_message = "console gateway HTTP no response" } };
    }

    // Match responses: first try by api in envelope, otherwise positional
    var usage_resp: ?std.json.ObjectMap = null;
    var sub_resp: ?std.json.ObjectMap = null;
    var quota_resp: ?std.json.ObjectMap = null;
    var card_resp: ?std.json.ObjectMap = null;

    for (resp_array.items) |r_val| {
        const r_obj = usage.asObject(r_val) orelse continue;
        const b_str = usage.asString(r_obj.get("body")) orelse continue;
        if (std.json.parseFromSlice(std.json.Value, alloc, b_str, .{})) |parsed| {
            if (usage.asObject(parsed.value)) |root| {
                const data_obj = usage.asObject(root.get("data"));
                const api = if (data_obj) |d| usage.asString(d.get("api")) else usage.asString(root.get("api"));
                if (api) |a| {
                    if (std.mem.eql(u8, a, ALIBABA_USAGE_API)) {
                        usage_resp = r_obj;
                    } else if (std.mem.eql(u8, a, ALIBABA_SUBSCRIPTION_API)) {
                        sub_resp = r_obj;
                    } else if (std.mem.eql(u8, a, ALIBABA_QUOTA_CONFIG_API)) {
                        quota_resp = r_obj;
                    } else if (std.mem.eql(u8, a, ALIBABA_RESET_CARD_API)) {
                        card_resp = r_obj;
                    }
                }
            }
        } else |_| {}
    }

    if (usage_resp == null and resp_array.items.len > 0) usage_resp = usage.asObject(resp_array.items[0]);
    if (sub_resp == null and resp_array.items.len > 1) sub_resp = usage.asObject(resp_array.items[1]);
    if (quota_resp == null and resp_array.items.len > 2) quota_resp = usage.asObject(resp_array.items[2]);
    if (card_resp == null and resp_array.items.len > 3) card_resp = usage.asObject(resp_array.items[3]);

    const status_num = if (usage_resp) |ro| usage.asNumber(ro.get("status")) else null;
    const status = usage.toInt(i64, status_num);
    if (status == null or status.? != 200) {
        const err_text = if (status) |st|
            try std.fmt.allocPrint(alloc, "console gateway HTTP {d}", .{st})
        else
            "console gateway HTTP no response";
        return .{ .report = .{ .error_message = err_text } };
    }

    const usage_b_str = if (usage_resp) |ro| usage.asString(ro.get("body")) else null;
    if (usage_b_str == null) {
        return .{ .report = .{ .error_message = "console gateway HTTP no response" } };
    }
    const usage_parsed = std.json.parseFromSlice(std.json.Value, alloc, usage_b_str.?, .{}) catch {
        return .{ .report = .{ .error_message = "console gateway HTTP no response" } };
    };

    const parsed = try parseAlibabaUsage(alloc, usage_parsed.value);

    // Subscription response
    var sub_val: ?std.json.Value = null;
    if (sub_resp) |ro| {
        if (usage.asString(ro.get("body"))) |bs| {
            if (std.json.parseFromSlice(std.json.Value, alloc, bs, .{})) |p| {
                sub_val = p.value;
            } else |_| {}
        }
    }

    const sub_unwrapped = unwrapEnvelope(sub_val);
    const sub_obj = usage.asObject(sub_unwrapped);
    const plan_type = if (sub_obj) |so| usage.stringOf(so.get("specCode")) else null;
    const instance_code = if (sub_obj) |so| usage.stringOf(so.get("instanceCode")) else null;
    const remaining_days = if (sub_obj) |so| usage.asNumber(so.get("remainingDays")) else null;

    // Quota config response
    var quota_val: ?std.json.Value = null;
    if (quota_resp) |ro| {
        if (usage.asString(ro.get("body"))) |bs| {
            if (std.json.parseFromSlice(std.json.Value, alloc, bs, .{})) |p| {
                quota_val = p.value;
            } else |_| {}
        }
    }
    const quota_notes = try parseAlibabaQuotaConfig(alloc, quota_val, plan_type);

    var notes = std.ArrayList([]const u8).empty;
    for (parsed.notes) |n| try notes.append(alloc, n);
    for (quota_notes) |n| try notes.append(alloc, n);

    if (usage.toInt(i64, remaining_days)) |rd| {
        const note = try std.fmt.allocPrint(alloc, "{d} days remaining in the plan", .{rd});
        try notes.append(alloc, note);
    }

    if (card_resp) |ro| {
        const card_b_str = usage.asString(ro.get("body"));
        if (getResetCardsCount(card_b_str, alloc)) |count| {
            if (count > 0) {
                const note = try std.fmt.allocPrint(alloc, "{d} reset cards available", .{count});
                try notes.append(alloc, note);
            }
        }
    }

    const err_msg: ?[]const u8 = if (parsed.limits.len == 0) "the token plan reported no windows" else null;

    return .{
        .report = .{
            .limits = parsed.limits,
            .planType = plan_type,
            .accountId = instance_code,
            .email = cred_email,
            .notes = notes.items,
            .error_message = err_msg,
        },
    };
}

pub const CliReportOutcome = struct {
    report: ?usage.ReportOutcome = null,
    reason: ?[]const u8 = null,
};

/// The human part of a CLI failure. A JSON error body carries its own message and usually a hint —
/// "No console access token found. Run `bl auth login --console`." — which is the action the user
/// needs; anything else explains itself in its first non-empty line. JSON without a message says
/// nothing useful, so the reason stays the bare exit code rather than quoting a brace.
fn cliFailureDetail(alloc: std.mem.Allocator, text: []const u8) !?[]const u8 {
    if (std.json.parseFromSlice(std.json.Value, alloc, text, .{})) |parsed_val| {
        const root = usage.asObject(parsed_val.value);
        const err_obj = if (root) |r| usage.asObject(r.get("error")) else null;
        const message = if (err_obj) |e| usage.stringOf(e.get("message")) else null;
        if (message) |msg| {
            if (msg.len == 0) return null;
            if (err_obj) |e| {
                if (usage.stringOf(e.get("hint"))) |hint| {
                    if (hint.len > 0) {
                        const join: []const u8 = if (msg[msg.len - 1] == '.') " " else ". ";
                        const joined: ?[]const u8 = try std.fmt.allocPrint(alloc, "{s}{s}{s}", .{ msg, join, hint });
                        return joined;
                    }
                }
            }
            const bare: ?[]const u8 = msg;
            return bare;
        }
        return null;
    } else |_| {}
    var lines = std.mem.splitScalar(u8, text, '\n');
    while (lines.next()) |raw| {
        const line = std.mem.trim(u8, raw, " \t\r");
        if (line.len > 0) {
            const found: ?[]const u8 = line[0..@min(line.len, 120)];
            return found;
        }
    }
    return null;
}

/// A CLI that wrote to stderr before failing has already explained itself, and that explanation is
/// more useful than the exit code: "bl is not on PATH" beats "exit 126". The body is provider-visible
/// text, so it is trimmed to one bounded line before it goes into a report.
fn cliFailureReason(alloc: std.mem.Allocator, status: i64, resp0: std.json.ObjectMap) ![]const u8 {
    if (usage.stringOf(resp0.get("body"))) |text| {
        if (try cliFailureDetail(alloc, text)) |detail| {
            const capped = detail[0..@min(detail.len, 160)];
            return std.fmt.allocPrint(alloc, "bailian CLI failed (exit {d}): {s}", .{ status, capped });
        }
    }
    return std.fmt.allocPrint(alloc, "bailian CLI failed (exit {d})", .{status});
}

fn reportFromCli(
    alloc: std.mem.Allocator,
    cred_root: ?std.json.ObjectMap,
    resp0: std.json.ObjectMap,
    region: AlibabaRegion,
) !CliReportOutcome {
    const cred_account_id = if (cred_root) |cr| usage.stringOf(cr.get("accountId")) else null;
    const cred_email = if (cred_root) |cr| usage.stringOf(cr.get("email")) else null;

    const status = usage.toInt(i64, usage.asNumber(resp0.get("status"))) orelse -1;

    if (status != 0 and status != 200) {
        const reason = try cliFailureReason(alloc, status, resp0);
        return .{ .reason = reason };
    }

    const body_str = usage.asString(resp0.get("body"));
    if (body_str) |bs| {
        if (std.json.parseFromSlice(std.json.Value, alloc, bs, .{})) |parsed_val| {
            const parsed = try parseAlibabaUsage(alloc, parsed_val.value);
            if (parsed.limits.len > 0) {
                const note = try std.fmt.allocPrint(alloc, "read through the bailian CLI ({s})", .{region.toString()});
                const notes = try alloc.alloc([]const u8, 1);
                notes[0] = note;
                return .{
                    .report = .{
                        .limits = parsed.limits,
                        .accountId = cred_account_id,
                        .email = cred_email,
                        .notes = notes,
                        .error_message = null,
                    },
                };
            }
        } else |_| {}
    }

    return .{ .reason = "the bailian CLI output carried no token-plan windows" };
}

pub fn advance(
    alloc: std.mem.Allocator,
    credential_json: []const u8,
    responses_json: []const u8,
    _now_ms: i64,
) !usage.Outcome {
    _ = _now_ms;
    const cred_parsed = std.json.parseFromSlice(std.json.Value, alloc, credential_json, .{}) catch {
        return .{ .report = .{ .error_message = "invalid credential JSON" } };
    };
    const cred_root = usage.asObject(cred_parsed.value);

    const default_host = "https://bailian-singapore-cs.alibabacloud.com";
    const session_val = if (cred_root) |cr| cr.get("session") else null;
    const session = parseConsoleSession(session_val, default_host);
    const region = regionFor(cred_root, session);

    const resp_parsed = std.json.parseFromSlice(std.json.Value, alloc, responses_json, .{}) catch {
        return .{ .report = .{ .error_message = "invalid responses JSON" } };
    };
    const resp_array = usage.asArray(resp_parsed.value) orelse {
        return .{ .report = .{ .error_message = "invalid responses JSON" } };
    };

    if (resp_array.items.len == 0) {
        // Round 1: Official CLI first
        const cli_args = try alibabaCliArguments(alloc, region);
        const requests = try alloc.alloc(usage.UsageRequest, 1);
        requests[0] = .{
            .kind = .command,
            .command = "bl",
            .args = cli_args,
            .timeoutMs = ALIBABA_CLI_TIMEOUT_MS,
        };
        return .{ .requests = requests };
    }

    if (resp_array.items.len == 1) {
        const resp0_obj = usage.asObject(resp_array.items[0]);
        const status = if (resp0_obj) |ro| (usage.toInt(i64, usage.asNumber(ro.get("status"))) orelse -1) else -1;
        const unavailable = (status == 127);

        if (!unavailable) {
            if (resp0_obj) |ro| {
                const cli_outcome = try reportFromCli(alloc, cred_root, ro, region);
                if (cli_outcome.report) |rep| {
                    return .{ .report = rep };
                }
                if (session == null) {
                    const notes = try alloc.alloc([]const u8, 1);
                    notes[0] = "a captured console session would be the fallback";
                    return .{
                        .report = .{
                            .limits = &[_]usage.UsageLimit{},
                            .error_message = cli_outcome.reason orelse "the bailian CLI gave no usable output",
                            .notes = notes,
                        },
                    };
                }
            } else if (session == null) {
                const notes = try alloc.alloc([]const u8, 1);
                notes[0] = "a captured console session would be the fallback";
                return .{
                    .report = .{
                        .limits = &[_]usage.UsageLimit{},
                        .error_message = "the bailian CLI gave no usable output",
                        .notes = notes,
                    },
                };
            }
        } else if (session == null) {
            return .{
                .report = .{
                    .limits = &[_]usage.UsageLimit{},
                    .error_message = "needs a session credential when the host runs no commands; capture a console HAR with prime-agent's scripts/capture-alibaba-session.mjs",
                },
            };
        }

        return .{ .requests = try consoleRequests(alloc, session.?, region) };
    }

    if (session == null) {
        return .{
            .report = .{
                .limits = &[_]usage.UsageLimit{},
                .error_message = "alibaba needs a cli or session credential",
            },
        };
    }

    return try reportFromConsole(alloc, cred_root, resp_array);
}
