import test from "node:test";
import assert from "node:assert/strict";
import { buildTrendAxisTicks, buildTrendPolylines, panTrendViewport, sampleTrendPoints, sortUsageAccounts, sortWeeklySummaryAccounts, trendHistoryStart } from "./usage-trend.js";

test("trend points hold the last observation until the next sample", () => {
  const points = [
    { at: "2026-07-13T00:00:00.000Z", primaryRemainingPercent: 100, primaryResetsAt: "2026-07-14T12:00:00.000Z" },
    { at: "2026-07-13T12:00:00.000Z", primaryRemainingPercent: null, primaryResetsAt: null },
    { at: "2026-07-14T00:00:00.000Z", primaryRemainingPercent: 25, primaryResetsAt: "2026-07-14T12:00:00.000Z" }
  ];
  assert.deepEqual(buildTrendPolylines(points, "primaryRemainingPercent", "primaryResetsAt", {
    since: Date.parse("2026-07-13T00:00:00.000Z"),
    until: Date.parse("2026-07-14T00:00:00.000Z"),
    width: 320,
    height: 120,
    inset: 10
  }), ["10,10 310,10 310,85"]);
});

test("trend points split when a changed limit window raises the remaining value", () => {
  const points = [
    { at: "2026-07-13T00:00:00.000Z", secondaryRemainingPercent: 0, secondaryResetsAt: "2026-07-20T00:00:00.000Z" },
    { at: "2026-07-13T12:00:00.000Z", secondaryRemainingPercent: 49, secondaryResetsAt: "2026-07-20T12:00:00.000Z" },
    { at: "2026-07-14T00:00:00.000Z", secondaryRemainingPercent: 40, secondaryResetsAt: "2026-07-20T12:00:00.000Z" }
  ];

  assert.deepEqual(buildTrendPolylines(points, "secondaryRemainingPercent", "secondaryResetsAt", {
    since: Date.parse("2026-07-13T00:00:00.000Z"),
    until: Date.parse("2026-07-14T00:00:00.000Z"),
    width: 320,
    height: 120,
    inset: 10
  }), ["10,110 160,110", "160,61 310,61 310,70"]);
});

test("trend sampling keeps the first and latest point", () => {
  const points = Array.from({ length: 1000 }, (_, index) => ({ at: new Date(index * 60000).toISOString(), primaryRemainingPercent: index % 100 }));
  const sampled = sampleTrendPoints(points, 120);
  assert.equal(sampled.length <= 120, true);
  assert.equal(sampled[0], points[0]);
  assert.equal(sampled.at(-1), points.at(-1));
});

test("short trend ranges use whole-hour axis ticks regardless of the current minute", () => {
  const since = Date.parse("2026-07-14T03:37:00.000Z");
  const until = Date.parse("2026-07-14T09:37:00.000Z");
  const ticks = buildTrendAxisTicks(since, until);

  assert.deepEqual(ticks.map(({ at }) => new Date(at).toISOString()), [
    "2026-07-14T04:00:00.000Z",
    "2026-07-14T05:00:00.000Z",
    "2026-07-14T06:00:00.000Z",
    "2026-07-14T07:00:00.000Z",
    "2026-07-14T08:00:00.000Z",
    "2026-07-14T09:00:00.000Z"
  ]);
  assert.equal(ticks.every(({ at }) => new Date(at).getUTCMinutes() === 0), true);
});

test("three-day trend axes show each date at most once", () => {
  const since = Date.parse("2026-07-14T03:37:00.000Z");
  const until = since + 3 * 24 * 60 * 60 * 1000;
  const ticks = buildTrendAxisTicks(since, until);
  const dates = ticks.map(({ at }) => new Date(at).toISOString().slice(0, 10));

  assert.equal(new Set(dates).size, dates.length);
  assert.equal(ticks.every((tick, index) => index === 0 || tick.at - ticks[index - 1].at >= 24 * 60 * 60 * 1000), true);
});

test("trend viewport pans horizontally while staying inside thirty days of history", () => {
  const now = Date.parse("2026-07-14T00:00:00.000Z");
  assert.equal(panTrendViewport(now, 1, 160, 320, now), now - 12 * 60 * 60 * 1000);
  assert.equal(panTrendViewport(now, 1, -160, 320, now), now);
  assert.equal(panTrendViewport(now - 29 * 24 * 60 * 60 * 1000, 1, 320, 320, now), now - 29 * 24 * 60 * 60 * 1000);
});

test("trend viewport cannot pan past the earliest locally recorded sample", () => {
  const now = Date.parse("2026-07-14T00:00:00.000Z");
  const historyStart = now - 2 * 24 * 60 * 60 * 1000;
  assert.equal(panTrendViewport(now, 1, 640, 320, now, 30, historyStart), now - 24 * 60 * 60 * 1000);
  assert.equal(panTrendViewport(now, 1, 160, 320, now, 30, now - 10 * 60 * 1000), now);
  assert.equal(trendHistoryStart([{ at: "bad" }, { at: "2026-07-13T12:00:00.000Z" }, { at: "2026-07-12T12:00:00.000Z" }]), Date.parse("2026-07-12T12:00:00.000Z"));
  assert.equal(panTrendViewport(now, 0.125, 160, 320, now), now - 90 * 60 * 1000);
});

test("usage accounts sort by the requested remaining-limit signal without mutation", () => {
  const accounts = [{ id: "a" }, { id: "b" }, { id: "c" }, { id: "d" }];
  const entries = {
    a: { usage: { primary: { remainingPercent: 80 }, secondary: { remainingPercent: 20 } } },
    b: { usage: { primary: { remainingPercent: 60 }, secondary: { remainingPercent: 50 } } },
    c: { usage: { primary: { remainingPercent: 90 }, secondary: null } },
    d: { usage: null }
  };

  assert.deepEqual(sortUsageAccounts(accounts, entries, "availability").map(({ id }) => id), ["b", "a", "c", "d"]);
  assert.deepEqual(sortUsageAccounts(accounts, entries, "primary").map(({ id }) => id), ["c", "a", "b", "d"]);
  assert.deepEqual(sortUsageAccounts(accounts, entries, "secondary").map(({ id }) => id), ["b", "a", "c", "d"]);
  assert.deepEqual(sortUsageAccounts(accounts, entries, "configured", "ascending").map(({ id }) => id), ["a", "b", "c", "d"]);
  assert.deepEqual(sortUsageAccounts(accounts, entries, "primary", "ascending").map(({ id }) => id), ["b", "a", "c", "d"]);
  assert.deepEqual(sortUsageAccounts(accounts, entries, "configured", "descending").map(({ id }) => id), ["d", "c", "b", "a"]);
  assert.deepEqual(accounts.map(({ id }) => id), ["a", "b", "c", "d"]);
});

test("weekly summary always sorts highest remaining account first", () => {
  const accounts = [{ id: "a" }, { id: "b" }, { id: "c" }, { id: "d" }];
  const entries = {
    a: { usage: { secondary: { remainingPercent: 20 } } },
    b: { usage: { secondary: { remainingPercent: 80 } } },
    c: { usage: { secondary: { remainingPercent: 50 } } },
    d: { usage: { secondary: null } }
  };

  assert.deepEqual(sortWeeklySummaryAccounts(accounts, entries).map(({ id }) => id), ["b", "c", "a", "d"]);
  assert.deepEqual(accounts.map(({ id }) => id), ["a", "b", "c", "d"]);
});
