import test from "node:test";
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";

test("global notification monitor polls sessions and every registered account", async () => {
  const [hook, api] = await Promise.all([
    readFile(new URL("./useNotifications.js", import.meta.url), "utf8"),
    readFile(new URL("./notification-api.js", import.meta.url), "utf8")
  ]);

  assert.match(api, /request\("\/sessions"\)/);
  assert.match(api, /accounts\/\$\{encodeURIComponent\(accountId\)\}\/usage/);
  assert.match(hook, /const accountsRef = useRef\(accounts\)/);
  assert.match(hook, /accountsRef\.current = accounts/);
  assert.match(hook, /const latestAccounts = accountsRef\.current/);
  assert.match(hook, /Promise\.allSettled\(\[loadSessions\(\), \.\.\.latestAccounts\.map/);
  assert.match(hook, /accounts: latestAccounts/);
  assert.match(hook, /collectNotificationPoll/);
  assert.match(hook, /const \[refreshErrors, setRefreshErrors\] = useState\(\[\]\)/);
  assert.match(hook, /setRefreshErrors\(poll\.errors\)/);
  assert.match(hook, /refreshErrors,/);
  assert.match(hook, /previousUsage\.current = poll\.previousUsage/);
  assert.match(hook, /setInterval\(load, 60000\)/);
});

test("global notification monitor receives session status changes immediately over one shared event stream", async () => {
  const [hook, api] = await Promise.all([
    readFile(new URL("./useNotifications.js", import.meta.url), "utf8"),
    readFile(new URL("./notification-api.js", import.meta.url), "utf8")
  ]);

  assert.match(api, /sessionEventsUrl/);
  assert.match(hook, /createReconnectingEventSource/);
  assert.match(hook, /source\.addEventListener\("sessions\.snapshot"/);
  assert.match(hook, /source\.addEventListener\("session\.updated"/);
  assert.match(hook, /mergeSessionUpdate/);
  assert.match(hook, /return \(\) => connection\.stop\(\)/);
  assert.match(hook, /recoveryVersion/);
});

test("notification monitor exposes individual and bulk dismissal", async () => {
  const hook = await readFile(new URL("./useNotifications.js", import.meta.url), "utf8");

  assert.match(hook, /dismiss: \(id\)/);
  assert.match(hook, /dismissSession: \(sessionId\)/);
  assert.match(hook, /clear: \(\)/);
});

test("notification monitor persists unread notifications and dismissed ids", async () => {
  const hook = await readFile(new URL("./useNotifications.js", import.meta.url), "utf8");

  assert.match(hook, /readPersistedNotifications/);
  assert.match(hook, /writePersistedNotifications/);
  assert.match(hook, /cleanupNotificationState/);
  assert.match(hook, /const \[notificationState, setNotificationState\] = useState\(\(\) => readPersistedNotifications\(\)\)/);
  assert.match(hook, /writePersistedNotifications\(undefined, notificationState, \{ sessions \}\)/);
  assert.match(hook, /cleanupNotificationState\(current, \{ sessions: nextSessions \}\)/);
  assert.match(hook, /mergeNotificationState\(current, items\)/);
  assert.match(hook, /dismissedIds: current\.dismissedIds\.includes\(id\) \? current\.dismissedIds : \[id, \.\.\.current\.dismissedIds\]/);
});

test("notification monitor treats only the actually visible chat as current", async () => {
  const [app, hook] = await Promise.all([
    readFile(new URL("../../App.jsx", import.meta.url), "utf8"),
    readFile(new URL("./useNotifications.js", import.meta.url), "utf8")
  ]);

  assert.match(app, /const notificationCurrentSession = app\.tab === "chat" && !app\.newChatOpen && !app\.chatTransition \? displaySession : null/);
  assert.match(app, /useNotifications\(\{ accounts: app\.accounts, currentSession: notificationCurrentSession, recoveryVersion: connectivity\.recoveryVersion \}\)/);
  assert.match(hook, /useNotifications\(\{ accounts, currentSession, recoveryVersion = 0 \}\)/);
  assert.doesNotMatch(hook, /detectCurrentSessionCompletion/);
});

test("notification monitor restores the previous session snapshot and detects reconnect transitions", async () => {
  const hook = await readFile(new URL("./useNotifications.js", import.meta.url), "utf8");

  assert.match(hook, /readPersistedSessionSnapshot/);
  assert.match(hook, /useRef\(readPersistedSessionSnapshot\(\)\)/);
  assert.match(hook, /writePersistedSessionSnapshot\(undefined, nextSessions\)/);
  assert.doesNotMatch(hook, /sessions\.snapshot[\s\S]*?mergeSessionSnapshot[^\n]*\{ detect: false \}/);
});

test("clearing notifications also clears a failed-open error", async () => {
  const hook = await readFile(new URL("./useNotifications.js", import.meta.url), "utf8");

  assert.match(hook, /clear: \(\) => \{\s*setOpenError\(null\);/s);
});
