import test from "node:test";
import assert from "node:assert/strict";
import { collectNotificationPoll, detectCurrentSessionCompletion, detectSessionNotifications, detectWeeklyReset, mergeSessionSnapshot, mergeSessionUpdate } from "./notification-state.js";

test("session stream updates one row without dropping the others", () => {
  const sessions = [
    { id: "one", status: "running" },
    { id: "two", status: "completed" }
  ];

  assert.deepEqual(mergeSessionUpdate(sessions, { id: "two", status: "running" }), [
    { id: "one", status: "running" },
    { id: "two", status: "running" }
  ]);
});

test("late polling snapshots cannot replace a newer streamed session status", () => {
  const current = [{ id: "one", status: "completed", updatedAt: "2026-07-15T10:00:02.000Z" }];
  const stalePoll = [{ id: "one", status: "running", updatedAt: "2026-07-15T10:00:01.000Z" }];

  assert.deepEqual(mergeSessionSnapshot(current, stalePoll), current);
});

test("equal-time server snapshots restore title fields omitted by older persisted observations", () => {
  const current = [{ id: "one", status: "completed", updatedAt: "2026-07-15T10:00:02.000Z" }];
  const server = [{
    id: "one",
    status: "completed",
    updatedAt: "2026-07-15T10:00:02.000Z",
    codexThreadId: "thread-one",
    conversationTitle: "Actual chat title"
  }];

  assert.deepEqual(mergeSessionSnapshot(current, server), server);
});

test("detects the current session completion immediately from live state", () => {
  const previous = { id: "current", accountId: "a", projectLabel: "Alpha", status: "running" };
  const completed = { ...previous, conversationTitle: "Chat title", status: "completed", updatedAt: "2026-07-13T10:00:00.000Z" };

  assert.deepEqual(detectCurrentSessionCompletion(previous, completed), {
    id: "completed:current:2026-07-13T10:00:00.000Z",
    type: "completed",
    title: "Response complete",
    detail: "Alpha",
    accountId: "a",
    sessionId: "current",
    conversationTitle: "Chat title",
    createdAt: "2026-07-13T10:00:00.000Z"
  });
  assert.equal(detectCurrentSessionCompletion(completed, completed), null);
});

test("detects completion transitions only when the session is not currently visible", () => {
  const previous = [
    { id: "approval", accountId: "a", projectLabel: "Alpha", status: "running" },
    { id: "complete", accountId: "b", projectLabel: "Beta", status: "running" },
    { id: "current", accountId: "a", projectLabel: "Current", status: "running" }
  ];
  const next = [
    { ...previous[0], status: "waiting_approval", updatedAt: "2026-06-30T10:00:00.000Z" },
    { ...previous[1], status: "completed", updatedAt: "2026-06-30T10:01:00.000Z" },
    { ...previous[2], status: "completed", updatedAt: "2026-06-30T10:02:00.000Z" }
  ];

  assert.deepEqual(detectSessionNotifications(previous, next, "current"), [
    {
      id: "approval:approval:2026-06-30T10:00:00.000Z",
      type: "approval",
      title: "Approval needed",
      detail: "Alpha",
      accountId: "a",
      sessionId: "approval",
      createdAt: "2026-06-30T10:00:00.000Z"
    },
    {
      id: "completed:complete:2026-06-30T10:01:00.000Z",
      type: "completed",
      title: "Response complete",
      detail: "Beta",
      accountId: "b",
      sessionId: "complete",
      createdAt: "2026-06-30T10:01:00.000Z"
    }
  ]);
});

test("detects failed and interrupted transitions in other sessions", () => {
  const previous = [
    { id: "failed", accountId: "a", projectLabel: "Alpha", status: "running" },
    { id: "interrupted", accountId: "b", projectLabel: "Beta", status: "running" },
    { id: "current", accountId: "a", projectLabel: "Current", status: "running" }
  ];
  const next = [
    { ...previous[0], status: "failed", updatedAt: "2026-06-30T10:00:00.000Z" },
    { ...previous[1], status: "interrupted", updatedAt: "2026-06-30T10:01:00.000Z" },
    { ...previous[2], status: "failed", updatedAt: "2026-06-30T10:02:00.000Z" }
  ];

  assert.deepEqual(detectSessionNotifications(previous, next, "current").map((item) => item.type), ["failed", "interrupted"]);
});

test("detects usage-exhausted when a terminal session coincides with empty usage", () => {
  const previous = [{ id: "failed", accountId: "a", projectLabel: "Alpha", status: "running" }];
  const next = [{ ...previous[0], status: "failed", updatedAt: "2026-06-30T10:00:00.000Z" }];
  const usageByAccount = new Map([["a", { primary: { remainingPercent: 0 }, secondary: { remainingPercent: 2 } }]]);

  assert.deepEqual(detectSessionNotifications(previous, next, "", usageByAccount), [
    {
      id: "usage-exhausted:failed:2026-06-30T10:00:00.000Z",
      type: "usage-exhausted",
      title: "Usage exhausted",
      detail: "Alpha",
      accountId: "a",
      sessionId: "failed",
      createdAt: "2026-06-30T10:00:00.000Z"
    }
  ]);
});

test("detects a weekly reset only when reset time advances and remaining usage increases", () => {
  const account = { id: "a", label: "Personal" };
  const previous = { secondary: { remainingPercent: 18, resetsAt: "2026-07-05T00:00:00.000Z" } };
  const reset = { secondary: { remainingPercent: 100, resetsAt: "2026-07-12T00:00:00.000Z" } };
  const ordinaryUse = { secondary: { remainingPercent: 17, resetsAt: "2026-07-05T00:00:00.000Z" } };

  assert.deepEqual(detectWeeklyReset(account, previous, reset, "2026-07-05T00:00:01.000Z"), {
    id: "weekly-reset:a:2026-07-12T00:00:00.000Z",
    type: "weekly-reset",
    title: "Weekly usage reset",
    detail: "Personal · 100% 사용 가능",
    accountId: "a",
    sessionId: null,
    createdAt: "2026-07-05T00:00:01.000Z"
  });
  assert.equal(detectWeeklyReset(account, previous, ordinaryUse, "2026-07-05T00:00:01.000Z"), null);
});

test("does not announce a weekly reset before the previous reset time", () => {
  const account = { id: "a", label: "Personal" };
  const previous = { secondary: { remainingPercent: 18, resetsAt: "2026-07-05T00:00:00.000Z" } };
  const next = { secondary: { remainingPercent: 100, resetsAt: "2026-07-12T00:00:00.000Z" } };

  assert.equal(detectWeeklyReset(account, previous, next, "2026-07-04T23:59:59.000Z"), null);
});

test("notification polling detects low and critical usage threshold crossings", () => {
  const accounts = [{ id: "ok", label: "OK" }];
  const previousUsage = new Map([["ok", {
    primary: { remainingPercent: 30, resetsAt: "2026-07-05T00:00:00.000Z" },
    secondary: { remainingPercent: 10, resetsAt: "2026-07-12T00:00:00.000Z" }
  }]]);

  const result = collectNotificationPoll({
    accounts,
    previousSessions: [],
    previousUsage,
    currentSessionId: "",
    detectedAt: "2026-06-30T10:01:00.000Z",
    results: [
      { status: "fulfilled", value: { sessions: [] } },
      { status: "fulfilled", value: {
        primary: { remainingPercent: 19, resetsAt: "2026-07-05T00:00:00.000Z" },
        secondary: { remainingPercent: 4, resetsAt: "2026-07-12T00:00:00.000Z" }
      } }
    ]
  });

  assert.deepEqual(result.notifications.map((item) => item.type), ["usage-low", "usage-critical"]);
  assert.deepEqual(result.notifications.map((item) => item.id), [
    "usage-low:ok:primary:2026-07-05T00:00:00.000Z",
    "usage-critical:ok:secondary:2026-07-12T00:00:00.000Z"
  ]);
  assert.deepEqual(result.notifications.map((item) => item.detail), [
    "5h limit 19% remaining",
    "Weekly limit 4% remaining"
  ]);
});

test("notification polling does not repeat usage threshold alerts below the same threshold", () => {
  const accounts = [{ id: "ok", label: "OK" }];
  const previousUsage = new Map([["ok", {
    primary: { remainingPercent: 18, resetsAt: "2026-07-05T00:00:00.000Z" }
  }]]);

  const result = collectNotificationPoll({
    accounts,
    previousSessions: [],
    previousUsage,
    currentSessionId: "",
    detectedAt: "2026-06-30T10:01:00.000Z",
    results: [
      { status: "fulfilled", value: { sessions: [] } },
      { status: "fulfilled", value: {
        primary: { remainingPercent: 17, resetsAt: "2026-07-05T00:00:00.000Z" }
      } }
    ]
  });

  assert.equal(result.notifications.length, 0);
});

test("weekly reset notification prefers safe account identity over default label", () => {
  const account = { id: "a", label: "New account", displayName: "ansrb4134" };
  const previous = { secondary: { remainingPercent: 18, resetsAt: "2026-07-05T00:00:00.000Z" } };
  const reset = { secondary: { remainingPercent: 100, resetsAt: "2026-07-12T00:00:00.000Z" } };

  const notification = detectWeeklyReset(account, previous, reset, "2026-07-05T00:00:01.000Z");

  assert.match(notification.detail, /^ansrb4134 /);
  assert.doesNotMatch(notification.detail, /New account/);
});

test("weekly reset notification falls back to configured label", () => {
  const account = { id: "a", label: "Personal" };
  const previous = { secondary: { remainingPercent: 18, resetsAt: "2026-07-05T00:00:00.000Z" } };
  const reset = { secondary: { remainingPercent: 100, resetsAt: "2026-07-12T00:00:00.000Z" } };

  const notification = detectWeeklyReset(account, previous, reset, "2026-07-05T00:00:01.000Z");

  assert.match(notification.detail, /^Personal /);
});

test("notification polling preserves successful results when one source fails", () => {
  const accounts = [{ id: "ok", label: "OK" }, { id: "fail", label: "Fail" }];
  const previousSessions = [{ id: "approval", accountId: "ok", projectLabel: "Alpha", status: "running" }];
  const nextSessions = [{ ...previousSessions[0], status: "waiting_approval", updatedAt: "2026-06-30T10:00:00.000Z" }];
  const previousUsage = new Map([
    ["ok", { secondary: { remainingPercent: 10, resetsAt: "2026-06-29T00:00:00.000Z" } }],
    ["fail", { secondary: { remainingPercent: 20, resetsAt: "2026-07-05T00:00:00.000Z" } }]
  ]);

  const result = collectNotificationPoll({
    accounts,
    previousSessions,
    previousUsage,
    currentSessionId: "",
    detectedAt: "2026-06-30T10:01:00.000Z",
    results: [
      { status: "fulfilled", value: { sessions: nextSessions } },
      { status: "fulfilled", value: { secondary: { remainingPercent: 100, resetsAt: "2026-07-12T00:00:00.000Z" } } },
      { status: "rejected", reason: new Error("C:\\Users\\secret\\auth.json") }
    ]
  });

  assert.deepEqual(result.sessions, nextSessions);
  assert.deepEqual(result.previousUsage.get("fail"), previousUsage.get("fail"));
  assert.deepEqual(result.errors, [{ kind: "usage", accountId: "fail", message: "Notification refresh partially failed." }]);
  assert.equal(result.notifications.length, 2);
  assert.equal(result.notifications[0].type, "approval");
  assert.equal(result.notifications[1].type, "weekly-reset");
});

test("notification polling can classify stopped sessions as usage exhausted", () => {
  const accounts = [{ id: "ok", label: "OK" }];
  const previousSessions = [{ id: "failed", accountId: "ok", projectLabel: "Alpha", status: "running" }];
  const nextSessions = [{ ...previousSessions[0], status: "failed", updatedAt: "2026-06-30T10:00:00.000Z" }];
  const previousUsage = new Map([["ok", { primary: { remainingPercent: 12, resetsAt: "2026-07-05T00:00:00.000Z" } }]]);

  const result = collectNotificationPoll({
    accounts,
    previousSessions,
    previousUsage,
    currentSessionId: "",
    detectedAt: "2026-06-30T10:01:00.000Z",
    results: [
      { status: "fulfilled", value: { sessions: nextSessions } },
      { status: "fulfilled", value: { primary: { remainingPercent: 0, resetsAt: "2026-07-05T00:00:00.000Z" } } }
    ]
  });

  assert.deepEqual(result.notifications.map((notification) => notification.type), ["usage-exhausted"]);
});

test("notification polling upgrades a streamed failure after usage reports exhaustion", () => {
  const failedSession = {
    id: "failed",
    accountId: "ok",
    projectLabel: "Alpha",
    status: "failed",
    updatedAt: "2026-06-30T10:00:00.000Z"
  };
  const result = collectNotificationPoll({
    accounts: [{ id: "ok", label: "OK" }],
    previousSessions: [failedSession],
    previousUsage: new Map(),
    currentNotifications: [{
      id: "failed:failed:2026-06-30T10:00:00.000Z",
      type: "failed",
      title: "Work failed",
      detail: "Alpha",
      accountId: "ok",
      sessionId: "failed",
      createdAt: "2026-06-30T10:00:00.000Z"
    }],
    currentSessionId: "",
    detectedAt: "2026-06-30T10:01:00.000Z",
    results: [
      { status: "fulfilled", value: { sessions: [failedSession] } },
      { status: "fulfilled", value: { primary: { remainingPercent: 0 } } }
    ]
  });

  assert.deepEqual(result.notifications.map((notification) => notification.type), ["usage-exhausted"]);
});
