import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { test } from "bun:test";

import { createCloudApiServer } from "./server.ts";

test("preset user login keeps YAML nav claims when OpenFGA tuple sync is unavailable", async () => {
  const password = "preset-user-password";
  const server = createCloudApiServer({
    env: {
      HWLAB_ACCESS_CONTROL_REQUIRED: "1",
      HWLAB_ACCESS_CONTROL_USERS_JSON: JSON.stringify([
        {
          id: "usr_v03_huiggao",
          username: "huiggao@163.com",
          displayName: "huiggao@163.com",
          email: "huiggao@163.com",
          role: "user",
          status: "active",
          navProfile: "code-mdtodo-only",
          passwordHashEnv: "HWLAB_PRESET_USER_HUIGGAO_PASSWORD_HASH"
        }
      ]),
      HWLAB_ACCESS_CONTROL_NAV_PROFILES_JSON: JSON.stringify([
        { id: "code-mdtodo-only", allowedIds: ["workbench.code", "project.mdtodo"] }
      ]),
      HWLAB_PRESET_USER_HUIGGAO_PASSWORD_HASH: stablePasswordHash(password)
    },
    openFgaAuthorizer: throwingOpenFgaAuthorizer()
  });
  await listen(server);
  try {
    const { port } = server.address() as { port: number };
    const login = await fetch(`http://127.0.0.1:${port}/auth/login`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ username: "huiggao@163.com", password })
    });
    assert.equal(login.status, 200);
    const loginBody = await login.json();
    assert.deepEqual(loginBody.access.nav.allowedIds, ["workbench.code", "project.mdtodo"]);

    const cookie = login.headers.get("set-cookie")?.split(";")[0] ?? "";
    assert.match(cookie, /^hwlab_session=/u);
    const session = await fetch(`http://127.0.0.1:${port}/auth/session`, { headers: { cookie } });
    assert.equal(session.status, 200);
    const sessionBody = await session.json();
    assert.equal(sessionBody.actor.username, "huiggao@163.com");
    assert.deepEqual(sessionBody.access.nav.allowedIds, ["workbench.code", "project.mdtodo"]);
  } finally {
    await close(server);
  }
});

function stablePasswordHash(password: string) {
  const salt = "preset-user-test-salt";
  return `sha256:${salt}:${createHash("sha256").update(`${salt}:${password}`).digest("hex")}`;
}

function throwingOpenFgaAuthorizer() {
  const error = Object.assign(new Error("openfga unavailable"), { code: "openfga_unavailable" });
  return {
    async describe() { return { ok: false, code: "openfga_unavailable" }; },
    async check() { throw error; },
    async writeTuple() { throw error; },
    async deleteTuple() { throw error; }
  };
}

function listen(server: any) {
  return new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
}

function close(server: any) {
  return new Promise<void>((resolve, reject) => server.close((error: Error | undefined) => (error ? reject(error) : resolve())));
}
