import test from "node:test";
import assert from "node:assert/strict";
import { Readable } from "node:stream";
import { resolveAccountRoute } from "./account-routes.js";

function request(method, body) {
  const req = Readable.from(body === undefined ? [] : [JSON.stringify(body)]);
  req.method = method;
  return req;
}

test("POST /accounts creates account and returns safe projection", async () => {
  const config = { dataDir: "data", accounts: [{ id: "a", label: "A", codexHome: "missing-a" }] };
  const result = await resolveAccountRoute(
    request("POST", { label: "Work" }),
    new URL("http://localhost/accounts"),
    { config, configPath: "config.local.json", createAccount: async () => ({
      account: { id: "work-1", label: "Work", status: "needs_login" },
      accounts: [{ id: "a", label: "A", status: "needs_login" }, { id: "work-1", label: "Work", status: "needs_login" }]
    }) }
  );
  assert.equal(result.status, 201);
  assert.equal(JSON.stringify(result.body).includes("codexHome"), false);
});

test("POST /accounts forwards the final entitlement capacity assertion", async () => {
  const assertion = () => {};
  let receivedDependencies;
  await resolveAccountRoute(
    request("POST", {}),
    new URL("http://localhost/accounts"),
    {
      config: { accounts: [] },
      configPath: "config.local.json",
      assertCanAddExecutionAccount: assertion,
      createAccount: async (_config, _path, _input, dependencies) => {
        receivedDependencies = dependencies;
        return { account: {}, accounts: [] };
      },
    }
  );

  assert.equal(receivedDependencies.assertCanAddExecutionAccount, assertion);
});

test("DELETE /accounts/:id removes account configuration", async () => {
  let removedId;
  const result = await resolveAccountRoute(
    request("DELETE"),
    new URL("http://localhost/accounts/work-1"),
    { config: {}, configPath: "config.local.json", removeAccount: async (_config, _path, id) => {
      removedId = id;
      return { accounts: [{ id: "a", label: "A", status: "logged_in" }] };
    } }
  );
  assert.equal(removedId, "work-1");
  assert.deepEqual(result, { status: 200, body: { accounts: [{ id: "a", label: "A", status: "logged_in" }] } });
});

test("POST /accounts/:id/login starts safe device-code login", async () => {
  let accountId;
  const result = await resolveAccountRoute(
    request("POST"),
    new URL("http://localhost/accounts/work-1/login"),
    { config: {}, startAccountLogin: async (_config, id) => {
      accountId = id;
      return { loginId: "login-1", verificationUrl: "https://example.com/device", userCode: "ABCD-EFGH" };
    } }
  );

  assert.equal(accountId, "work-1");
  assert.deepEqual(result, {
    status: 200,
    body: { loginId: "login-1", verificationUrl: "https://example.com/device", userCode: "ABCD-EFGH" }
  });
});

test("DELETE /accounts removes every configured account", async () => {
  let called = false;
  const result = await resolveAccountRoute(
    request("DELETE"),
    new URL("http://localhost/accounts"),
    {
      config: {},
      configPath: "config.local.json",
      removeAllAccounts: async () => {
        called = true;
        return { accounts: [] };
      }
    }
  );

  assert.equal(called, true);
  assert.deepEqual(result, { status: 200, body: { accounts: [] } });
});

test("POST /accounts/deduplicate is no longer an account mutation route", async () => {
  const result = await resolveAccountRoute(
    request("POST"),
    new URL("http://localhost/accounts/deduplicate"),
    {}
  );

  assert.equal(result, null);
});

test("account login attempt routes start, complete, and cancel an uncommitted account", async () => {
  const calls = [];
  const accountLoginAttempts = {
    async start() {
      calls.push(["start"]);
      return { attemptId: "attempt-1", loginId: "login-1", verificationUrl: "https://example.com/device", userCode: "ABCD-EFGH" };
    },
    async complete(id) {
      calls.push(["complete", id]);
      return { connected: true, accounts: [{ id: "account-1", label: "New account", status: "logged_in" }] };
    },
    async cancel(id) {
      calls.push(["cancel", id]);
      return { canceled: true };
    }
  };

  const started = await resolveAccountRoute(
    request("POST", {}),
    new URL("http://localhost/account-login-attempts"),
    { accountLoginAttempts }
  );
  const completed = await resolveAccountRoute(
    request("POST"),
    new URL("http://localhost/account-login-attempts/attempt-1/complete"),
    { accountLoginAttempts }
  );
  const canceled = await resolveAccountRoute(
    request("DELETE"),
    new URL("http://localhost/account-login-attempts/attempt-1"),
    { accountLoginAttempts }
  );

  assert.equal(started.status, 201);
  assert.equal(completed.body.connected, true);
  assert.deepEqual(canceled, { status: 200, body: { canceled: true } });
  assert.deepEqual(calls, [["start"], ["complete", "attempt-1"], ["cancel", "attempt-1"]]);
});

test("POST /accounts/:id/login/complete finalizes login and deduplicates safely", async () => {
  let accountId;
  const result = await resolveAccountRoute(
    request("POST"),
    new URL("http://localhost/accounts/new/login/complete"),
    { config: {}, configPath: "config.local.json", completeAccountLogin: async (_config, _path, id) => {
      accountId = id;
      return {
        connected: true,
        deduplicated: true,
        existingAccountId: "existing",
        accounts: [{ id: "existing", label: "Existing", status: "logged_in" }]
      };
    } }
  );

  assert.equal(accountId, "new");
  assert.equal(result.status, 200);
  assert.equal(result.body.existingAccountId, "existing");
  assert.equal(JSON.stringify(result.body).includes("codexHome"), false);
});

test("PATCH /accounts/:id updates account label", async () => {
  let updated;
  const result = await resolveAccountRoute(
    request("PATCH", { label: "person@example.com" }),
    new URL("http://localhost/accounts/work-1"),
    { config: {}, configPath: "config.local.json", updateAccountLabel: async (_config, _path, id, input) => {
      updated = { id, input };
      return { account: { id, label: input.label, status: "needs_login" }, accounts: [{ id, label: input.label, status: "needs_login" }] };
    } }
  );
  assert.deepEqual(updated, { id: "work-1", input: { label: "person@example.com" } });
  assert.equal(result.status, 200);
  assert.equal(result.body.account.label, "person@example.com");
  assert.equal(JSON.stringify(result.body).includes("codexHome"), false);
});

test("PUT /accounts/:id updates account label", async () => {
  const result = await resolveAccountRoute(
    request("PUT", { label: "work@example.com" }),
    new URL("http://localhost/accounts/work-1"),
    { config: {}, configPath: "config.local.json", updateAccountLabel: async () => ({
      account: { id: "work-1", label: "work@example.com", status: "needs_login" },
      accounts: [{ id: "work-1", label: "work@example.com", status: "needs_login" }]
    }) }
  );
  assert.equal(result.status, 200);
  assert.equal(result.body.account.label, "work@example.com");
});

test("ignores unrelated routes", async () => {
  assert.equal(await resolveAccountRoute(request("GET"), new URL("http://localhost/threads"), {}), null);
});
