import test from "node:test";
import assert from "node:assert/strict";

const {
  generateSessionId,
  touchSession,
  getSessionInfo,
  getSessionConnection,
  getActiveSessionCount,
  getActiveSessions,
  extractExternalSessionId,
  checkSessionLimit,
  registerKeySession,
  unregisterKeySession,
  isSessionRegisteredForKey,
  clearSessions,
} = await import("../../open-sse/services/sessionManager.ts");

// Reset between tests
test.beforeEach(() => clearSessions());

// ─── Session ID Generation ──────────────────────────────────────────────────

test("generateSessionId: produces stable ID for same request", () => {
  const body = {
    model: "claude-sonnet-4-20250514",
    messages: [
      { role: "system", content: "You are helpful." },
      { role: "user", content: "hello" },
    ],
  };
  const id1 = generateSessionId(body);
  const id2 = generateSessionId(body);
  assert.equal(id1, id2);
  assert.equal(id1.length, 16);
});

test("generateSessionId: different model = different ID", () => {
  const body1 = { model: "claude-sonnet-4-20250514", messages: [{ role: "user", content: "hi" }] };
  const body2 = { model: "gpt-4o", messages: [{ role: "user", content: "hi" }] };
  assert.notEqual(generateSessionId(body1), generateSessionId(body2));
});

test("generateSessionId: different system prompt = different ID", () => {
  const body1 = {
    model: "claude-sonnet-4-20250514",
    messages: [
      { role: "system", content: "A" },
      { role: "user", content: "hi" },
    ],
  };
  const body2 = {
    model: "claude-sonnet-4-20250514",
    messages: [
      { role: "system", content: "B" },
      { role: "user", content: "hi" },
    ],
  };
  assert.notEqual(generateSessionId(body1), generateSessionId(body2));
});

test("generateSessionId: tools contribute to fingerprint", () => {
  const body1 = {
    model: "claude-sonnet-4-20250514",
    messages: [{ role: "user", content: "hi" }],
    tools: [{ name: "search" }],
  };
  const body2 = {
    model: "claude-sonnet-4-20250514",
    messages: [{ role: "user", content: "hi" }],
    tools: [{ name: "different" }],
  };
  assert.notEqual(generateSessionId(body1), generateSessionId(body2));
});

test("generateSessionId: same tools in different order = same ID", () => {
  const body1 = {
    model: "m1",
    messages: [{ role: "user", content: "hi" }],
    tools: [{ name: "a" }, { name: "b" }],
  };
  const body2 = {
    model: "m1",
    messages: [{ role: "user", content: "hi" }],
    tools: [{ name: "b" }, { name: "a" }],
  };
  assert.equal(generateSessionId(body1), generateSessionId(body2));
});

test("generateSessionId: Claude body.system format", () => {
  const body = {
    model: "claude-sonnet-4-20250514",
    system: "You are helpful.",
    messages: [{ role: "user", content: "hi" }],
  };
  const id = generateSessionId(body);
  assert.ok(id);
  assert.equal(id.length, 16);
});

test("generateSessionId: empty body returns null", () => {
  assert.equal(generateSessionId({}), null);
});

test("generateSessionId: provider option changes ID", () => {
  const body = { model: "m1", messages: [{ role: "user", content: "hi" }] };
  const id1 = generateSessionId(body, { provider: "claude" });
  const id2 = generateSessionId(body, { provider: "openai" });
  assert.notEqual(id1, id2);
});

// ─── Session Tracking ───────────────────────────────────────────────────────

test("touchSession + getSessionInfo: creates and updates session", () => {
  touchSession("abc123", "conn1");
  const info = getSessionInfo("abc123");
  assert.ok(info);
  assert.equal(info.requestCount, 1);
  assert.equal(info.connectionId, "conn1");

  touchSession("abc123");
  const updated = getSessionInfo("abc123");
  assert.equal(updated.requestCount, 2);
});

test("getSessionConnection: returns bound connection", () => {
  touchSession("sess1", "conn-x");
  assert.equal(getSessionConnection("sess1"), "conn-x");
});

test("getSessionConnection: null for unknown session", () => {
  assert.equal(getSessionConnection("nonexistent"), null);
});

test("getActiveSessionCount: tracks count", () => {
  assert.equal(getActiveSessionCount(), 0);
  touchSession("s1");
  touchSession("s2");
  assert.equal(getActiveSessionCount(), 2);
});

test("getActiveSessions: returns session list", () => {
  touchSession("s1", "c1");
  touchSession("s2", "c2");
  const all = getActiveSessions();
  assert.equal(all.length, 2);
  assert.ok(all[0].sessionId);
  assert.ok(all[0].ageMs >= 0);
});

test("clearSessions: empties store", () => {
  touchSession("s1");
  clearSessions();
  assert.equal(getActiveSessionCount(), 0);
});

test("touchSession with null sessionId: no-op", () => {
  touchSession(null);
  assert.equal(getActiveSessionCount(), 0);
});

test("extractExternalSessionId accepts hyphen and underscore variants", () => {
  const h1 = new Headers({ "x-session-id": "abc-123" });
  const h2 = new Headers({ x_session_id: "def-456" });

  assert.equal(extractExternalSessionId(h1), "ext:abc-123");
  assert.equal(extractExternalSessionId(h2), "ext:def-456");
});

test("checkSessionLimit enforces max_sessions for new sessions only", () => {
  const keyId = "key-1";
  registerKeySession(keyId, "ext:sess-a");
  assert.equal(isSessionRegisteredForKey(keyId, "ext:sess-a"), true);

  const violation = checkSessionLimit(keyId, 1);
  assert.ok(violation);
  assert.equal(violation.code, "SESSION_LIMIT_EXCEEDED");

  unregisterKeySession(keyId, "ext:sess-a");
  assert.equal(isSessionRegisteredForKey(keyId, "ext:sess-a"), false);
  assert.equal(checkSessionLimit(keyId, 1), null);
});
