import test from "node:test";
import assert from "node:assert/strict";
import { resolveThreadRoute } from "./thread-routes.js";

test("DELETE /accounts/:accountId/threads/:threadId archives the owned thread", async () => {
  let archived;
  const result = await resolveThreadRoute(
    { method: "DELETE" },
    new URL("http://localhost/accounts/account-a/threads/thread-1"),
    {
      config: {},
      archiveAccountThread: async (_config, accountId, threadId) => {
        archived = { accountId, threadId };
        return { archived: true, ownerId: accountId, threadId };
      }
    }
  );

  assert.deepEqual(archived, { accountId: "account-a", threadId: "thread-1" });
  assert.deepEqual(result, {
    status: 200,
    body: { archived: true, ownerId: "account-a", threadId: "thread-1" }
  });
});

test("DELETE linked logical chat archives its remaining active thread refs", async () => {
  const archived = [];
  const result = await resolveThreadRoute(
    { method: "DELETE" },
    new URL("http://localhost/accounts/account-b/threads/thread-b"),
    {
      config: {
        accounts: [
          { id: "account-a" },
          { id: "account-b" }
        ]
      },
      listSessions: () => [{
        accountId: "account-b",
        codexThreadId: "thread-b",
        threadRefs: [
          { accountId: "account-a", threadId: "thread-a" },
          { accountId: "account-b", threadId: "thread-b" }
        ]
      }],
      listAccountThreads: async (_config, accountId, input) => {
        if (accountId !== "account-a") return { threads: [], nextCursor: null };
        if (!input.cursor) return { threads: [], nextCursor: "account-a-next" };
        return { threads: [{ id: "thread-a" }], nextCursor: null };
      },
      archiveAccountThread: async (_config, accountId, threadId) => {
        archived.push({ accountId, threadId });
        return { archived: true, ownerId: accountId, threadId };
      }
    }
  );

  assert.deepEqual(archived, [{ accountId: "account-a", threadId: "thread-a" }]);
  assert.deepEqual(result, {
    status: 200,
    body: { archived: true, ownerId: "account-b", threadId: "thread-b" }
  });
});

test("DELETE linked logical chat is idempotent when every thread ref is already archived", async () => {
  const archived = [];
  const result = await resolveThreadRoute(
    { method: "DELETE" },
    new URL("http://localhost/accounts/account-b/threads/thread-b"),
    {
      config: { accounts: [{ id: "account-a" }, { id: "account-b" }] },
      listSessions: () => [{
        accountId: "account-b",
        codexThreadId: "thread-b",
        threadRefs: [
          { accountId: "account-a", threadId: "thread-a" },
          { accountId: "account-b", threadId: "thread-b" }
        ]
      }],
      listAccountThreads: async () => ({ threads: [], nextCursor: null }),
      archiveAccountThread: async (_config, accountId, threadId) => {
        archived.push({ accountId, threadId });
        return { archived: true, ownerId: accountId, threadId };
      }
    }
  );

  assert.deepEqual(archived, []);
  assert.deepEqual(result, {
    status: 200,
    body: { archived: true, ownerId: "account-b", threadId: "thread-b" }
  });
});

test("thread route ignores unrelated requests", async () => {
  assert.equal(await resolveThreadRoute(
    { method: "GET" },
    new URL("http://localhost/threads"),
    {}
  ), null);
});

test("PATCH thread name reads a safe body and preserves owner identity", async () => {
  const titleUpdates = [];
  const result = await resolveThreadRoute(
    { method: "PATCH" },
    new URL("http://localhost/accounts/account-a/threads/thread-1/name"),
    {
      config: {},
      readJson: async () => ({ name: "Renamed" }),
      renameAccountThread: async (_config, ownerId, threadId, name) => ({ ownerId, threadId, name }),
      updateThreadSessionTitle: async (...args) => titleUpdates.push(args)
    }
  );
  assert.deepEqual(result, { status: 200, body: { ownerId: "account-a", threadId: "thread-1", name: "Renamed" } });
  assert.deepEqual(titleUpdates, [["account-a", "thread-1", "Renamed"]]);
});

test("PATCH thread pin persists the boolean for the owned thread", async () => {
  let saved;
  const result = await resolveThreadRoute(
    { method: "PATCH" },
    new URL("http://localhost/accounts/account-b/threads/thread-2/pin"),
    {
      config: {},
      readJson: async () => ({ pinned: true }),
      setAccountThreadPinned: async (_config, ownerId, threadId, pinned) => {
        saved = { ownerId, threadId, pinned };
        return saved;
      }
    }
  );
  assert.deepEqual(saved, { ownerId: "account-b", threadId: "thread-2", pinned: true });
  assert.deepEqual(result, { status: 200, body: saved });
});

test("DELETE local-delete tombstones archived thread and cleans local data", async () => {
  const calls = [];
  const result = await resolveThreadRoute(
    { method: "DELETE" },
    new URL("http://localhost/accounts/account-b/threads/thread-2/local-delete"),
    {
      config: {},
      deleteArchivedAccountThreadLocalData: async (_config, ownerId, threadId) => {
        calls.push(["tombstone", ownerId, threadId]);
        return { deleted: true, ownerId, threadId };
      },
      deleteThreadLocalData: async (_config, ownerId, threadId) => {
        calls.push(["cleanup", ownerId, threadId]);
        return { removedSessions: ["session-1"] };
      }
    }
  );

  assert.deepEqual(calls, [
    ["tombstone", "account-b", "thread-2"],
    ["cleanup", "account-b", "thread-2"]
  ]);
  assert.deepEqual(result, {
    status: 200,
    body: { deleted: true, ownerId: "account-b", threadId: "thread-2", removedSessions: ["session-1"] }
  });
});
