import test from "node:test";
import assert from "node:assert/strict";
import { consumeAccountRateLimitResetCredit, publicAccountTokenUsage, publicRateLimits, publicTokenUsage, readAccountRateLimits, readAccountTokenUsage } from "./usage-status.js";

test("rate limits expose remaining percent, reset time, and safe reset-credit metadata only", () => {
  const result = publicRateLimits({ rateLimits: { primary: { usedPercent: 57, resetsAt: 1782702784 }, secondary: { usedPercent: 52, resetsAt: 1783244906 }, credits: { balance: "secret" } }, rateLimitResetCredits: { availableCount: 2 } });
  assert.deepEqual(result, {
    primary: { remainingPercent: 43, resetsAt: "2026-06-29T03:13:04.000Z" },
    secondary: { remainingPercent: 48, resetsAt: "2026-07-05T09:48:26.000Z" },
    resetCreditsAvailable: 2,
    resetCredits: null
  });
  assert.equal(JSON.stringify(result).includes("secret"), false);
});

test("reset-credit details expose timestamps without opaque ids or descriptions", () => {
  const result = publicRateLimits({
    rateLimits: {},
    rateLimitResetCredits: {
      availableCount: 2,
      credits: [{
        id: "private-credit-id",
        resetType: "codexRateLimits",
        status: "available",
        grantedAt: 1781654400,
        expiresAt: 1784246400,
        title: "private title",
        description: "private description"
      }]
    }
  });

  assert.deepEqual(result, {
    primary: null,
    secondary: null,
    resetCreditsAvailable: 2,
    resetCredits: [{
      resetType: "codexRateLimits",
      status: "available",
      grantedAt: "2026-06-17T00:00:00.000Z",
      expiresAt: "2026-07-17T00:00:00.000Z"
    }]
  });
  assert.equal(JSON.stringify(result).includes("private"), false);
});

test("rate limits preserve app-server remaining percent when supplied", () => {
  const result = publicRateLimits({
    rateLimits: {
      primary: { remainingPercent: 0, usedPercent: 0, resetsAt: 1782702784 },
      secondary: { remainingPercent: 100, usedPercent: 0, resetsAt: 1783244906 }
    }
  });

  assert.deepEqual(result, {
    primary: { remainingPercent: 0, resetsAt: "2026-06-29T03:13:04.000Z" },
    secondary: { remainingPercent: 100, resetsAt: "2026-07-05T09:48:26.000Z" },
    resetCreditsAvailable: null
  });
});

test("rate limits classify a lone weekly window by its duration", () => {
  const result = publicRateLimits({
    rateLimits: {
      primary: { usedPercent: 1, resetsAt: 1784500596, windowDurationMins: 10080 },
      secondary: null
    }
  });

  assert.deepEqual(result, {
    primary: null,
    secondary: { remainingPercent: 99, resetsAt: "2026-07-19T22:36:36.000Z" },
    resetCreditsAvailable: null
  });
});

test("rate limits classify a lone long-running legacy window by its reset time", () => {
  const result = publicRateLimits({
    rateLimits: {
      primary: { usedPercent: 3, resetsAt: 1784500596 },
      secondary: null
    }
  }, Date.parse("2026-07-13T00:00:00.000Z"));

  assert.deepEqual(result, {
    primary: null,
    secondary: { remainingPercent: 97, resetsAt: "2026-07-19T22:36:36.000Z" },
    resetCreditsAvailable: null
  });
});

test("rate limits classify reversed windows by duration", () => {
  const result = publicRateLimits({
    rateLimits: {
      primary: { usedPercent: 52, resetsAt: 1783244906, windowDurationMins: 10080 },
      secondary: { usedPercent: 57, resetsAt: 1782702784, windowDurationMins: 300 }
    }
  });

  assert.deepEqual(result, {
    primary: { remainingPercent: 43, resetsAt: "2026-06-29T03:13:04.000Z" },
    secondary: { remainingPercent: 48, resetsAt: "2026-07-05T09:48:26.000Z" },
    resetCreditsAvailable: null
  });
});

test("token usage exposes context consumption without hidden data", () => {
  assert.deepEqual(publicTokenUsage({
    last: { totalTokens: 1200 },
    total: { totalTokens: 10000 },
    modelContextWindow: 4000
  }), { totalTokens: 1200, contextWindow: 4000, usedPercent: 30, remainingPercent: 70 });
  assert.equal(publicTokenUsage(null), null);
});

test("account token usage exposes safe daily buckets and summary values only", () => {
  const result = publicAccountTokenUsage({
    dailyUsageBuckets: [
      { startDate: "2026-07-13", tokens: 1234, privateField: "secret" },
      { startDate: "not-a-date", tokens: 99 },
      { startDate: "2026-07-14", tokens: -4 }
    ],
    summary: {
      lifetimeTokens: 50000,
      peakDailyTokens: 1234,
      currentStreakDays: 3,
      longestStreakDays: 8,
      longestRunningTurnSec: 90,
      privateField: "secret"
    }
  });

  assert.deepEqual(result, {
    dailyUsageBuckets: [{ startDate: "2026-07-13", tokens: 1234 }],
    summary: {
      lifetimeTokens: 50000,
      peakDailyTokens: 1234,
      currentStreakDays: 3,
      longestStreakDays: 8,
      longestRunningTurnSec: 90
    }
  });
  assert.equal(JSON.stringify(result).includes("secret"), false);
});

test("reads account token usage from the selected account app-server", async () => {
  const calls = [];
  const result = await readAccountTokenUsage({
    codexCliPath: "codex",
    accounts: [{ id: "a", codexHome: "HOME" }]
  }, "a", async (request) => {
    calls.push(request);
    return { summary: { lifetimeTokens: 42 }, dailyUsageBuckets: [] };
  });

  assert.deepEqual(calls, [{ codexCliPath: "codex", codexHome: "HOME", method: "account/usage/read" }]);
  assert.equal(result.summary.lifetimeTokens, 42);
});

test("history-only sources cannot invoke account usage operations", async () => {
  let requested = false;
  const config = {
    codexCliPath: "codex",
    accounts: [{ id: "history", codexHome: "HISTORY_HOME", historyOnly: true }]
  };
  const requester = async () => {
    requested = true;
    return {};
  };

  for (const operation of [
    () => readAccountRateLimits(config, "history", requester),
    () => readAccountTokenUsage(config, "history", requester),
    () => consumeAccountRateLimitResetCredit(config, "history", requester)
  ]) {
    await assert.rejects(
      operation(),
      (error) => error.message === "account_not_available_for_execution" && error.status === 403
    );
  }
  assert.equal(requested, false);
});

test("reset-credit count defaults to unavailable when app-server omits it", () => {
  assert.equal(publicRateLimits({ rateLimits: {} }).resetCreditsAvailable, null);
});

test("consumes one reset credit for the requested account with an idempotency key", async () => {
  const calls = [];
  const requester = async (request) => {
    calls.push(request);
    return { outcome: "reset" };
  };

  const result = await consumeAccountRateLimitResetCredit({
    codexCliPath: "codex",
    accounts: [{ id: "a", codexHome: "HOME" }]
  }, "a", requester, () => "reset-attempt-1");

  assert.deepEqual(result, { outcome: "reset" });
  assert.deepEqual(calls, [{
    codexCliPath: "codex",
    codexHome: "HOME",
    method: "account/rateLimitResetCredit/consume",
    params: { idempotencyKey: "reset-attempt-1" }
  }]);
});

test("rejects unknown reset-credit outcomes", async () => {
  await assert.rejects(
    consumeAccountRateLimitResetCredit({
      codexCliPath: "codex",
      accounts: [{ id: "a", codexHome: "HOME" }]
    }, "a", async () => ({ outcome: "private_server_value" })),
    (error) => error.message === "reset_credit_unavailable" && error.status === 502
  );
});

test("rate limit auth failures become safe login-required errors", async () => {
  const requester = async () => {
    throw new Error("failed to fetch codex rate limits: token_invalidated Bearer secret-token");
  };

  await assert.rejects(
    readAccountRateLimits({
      codexCliPath: "codex",
      accounts: [{ id: "a", codexHome: "HOME" }]
    }, "a", requester),
    (error) => error.message === "account_login_required" && error.status === 401
  );
});
