/**
 * Offices, visibility, and the 404 rule.
 *
 * The assertion that matters is the negative one: a private office and an office
 * that was never created must be indistinguishable from outside. If they differ
 * — by status code, by body, by timing anybody could measure — the endpoint
 * becomes a way to enumerate tenants. CONTRACT.md §6.
 *
 * HS256 is exercised directly here because it is the primary path: the issuer
 * this runs against signs `{"alg":"HS256"}`, and a JWKS-only implementation
 * would reject every real token.
 */

import assert from "node:assert/strict";
import { createHmac } from "node:crypto";
import { mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { after, before, describe, it } from "node:test";
import { buildApp } from "../app.ts";
import { loadConfig } from "../config.ts";
import type { OfficeDoc } from "../../../src/server/wire.ts";

const SECRET = "not-a-real-secret-and-never-was";

/** A minimal floor. `Plan` is what makes sense of it; the API only carries it. */
const floor = { id: "hq", name: "HQ", levels: [], viewpoints: [] };

let dir = "";

before(async () => {
  dir = await mkdtemp(join(tmpdir(), "tera-offices-"));
  await writeFile(
    join(dir, "open.json"),
    JSON.stringify({ id: "open", name: "Open office", visibility: "public", floor }),
  );
  await writeFile(
    join(dir, "closed.json"),
    JSON.stringify({ id: "closed", name: "Closed office", visibility: "private", floor }),
  );
  await writeFile(
    join(dir, "quiet.json"),
    JSON.stringify({ id: "quiet", name: "Unlisted office", visibility: "unlisted", floor }),
  );
  // No `visibility` at all: the fail-closed reading is `private`.
  await writeFile(join(dir, "vague.json"), JSON.stringify({ id: "vague", name: "?", floor }));
});

function appWith(env: Record<string, string>) {
  const config = loadConfig({ TERA_OFFICES_DIR: dir, ...env });
  config.logLevel = "silent";
  return buildApp(config);
}

function hs256(claims: Record<string, unknown>): string {
  const encode = (value: unknown): string =>
    Buffer.from(JSON.stringify(value)).toString("base64url");
  const signed = `${encode({ alg: "HS256", typ: "JWT" })}.${encode(claims)}`;
  return `${signed}.${createHmac("sha256", SECRET).update(signed).digest("base64url")}`;
}

describe("with auth off", () => {
  it("serves a public office and lets it be cached", async () => {
    const app = appWith({});
    after(() => app.close());

    const res = await app.inject({ method: "GET", url: "/api/v1/offices/open" });
    assert.equal(res.statusCode, 200);
    assert.equal(res.json<OfficeDoc>().floor.id, "hq");
    assert.match(String(res.headers["cache-control"]), /^public, max-age=/);
  });

  it("serves an unlisted office but never lets a shared cache keep it", async () => {
    const app = appWith({});
    after(() => app.close());

    const res = await app.inject({ method: "GET", url: "/api/v1/offices/quiet" });
    assert.equal(res.statusCode, 200);
    assert.equal(res.headers["cache-control"], "private, no-store");
  });

  it("answers 404 for a private office, identically to one that does not exist", async () => {
    const app = appWith({});
    after(() => app.close());

    const priv = await app.inject({ method: "GET", url: "/api/v1/offices/closed" });
    const absent = await app.inject({ method: "GET", url: "/api/v1/offices/no-such-office" });

    assert.equal(priv.statusCode, 404);
    assert.equal(absent.statusCode, 404);
    assert.deepEqual(priv.json(), absent.json());
  });

  it("treats a pack that forgot to declare visibility as private", async () => {
    const app = appWith({});
    after(() => app.close());
    assert.equal((await app.inject({ method: "GET", url: "/api/v1/offices/vague" })).statusCode, 404);
  });

  it("refuses an id that could climb out of the directory", async () => {
    const app = appWith({});
    after(() => app.close());
    for (const id of ["..", "..%2f..%2fetc%2fpasswd", "Open", "open.json"]) {
      const res = await app.inject({ method: "GET", url: `/api/v1/offices/${id}` });
      assert.equal(res.statusCode, 404, `${id} must not resolve`);
    }
  });
});

describe("with TERA_AUTH_MODE=jwt", () => {
  const env = { TERA_AUTH_MODE: "jwt", TERA_AUTH_JWT_SECRET: SECRET };

  it("opens a private office to a valid HS256 token", async () => {
    const app = appWith(env);
    after(() => app.close());

    const res = await app.inject({
      method: "GET",
      url: "/api/v1/offices/closed",
      headers: { authorization: `Bearer ${hs256({ sub: "someone", exp: now() + 600 })}` },
    });
    assert.equal(res.statusCode, 200);
    assert.equal(res.headers["cache-control"], "private, no-store");
  });

  it("still answers 404 to an expired token, a wrong secret, or alg: none", async () => {
    const app = appWith(env);
    after(() => app.close());

    const expired = hs256({ sub: "someone", exp: now() - 3600 });
    const wrong = `${hs256({ sub: "someone" }).split(".").slice(0, 2).join(".")}.deadbeef`;
    const none = `${Buffer.from(JSON.stringify({ alg: "none" })).toString("base64url")}.${Buffer.from(
      JSON.stringify({ sub: "someone" }),
    ).toString("base64url")}.`;

    for (const token of [expired, wrong, none, "not-a-jwt"]) {
      const res = await app.inject({
        method: "GET",
        url: "/api/v1/offices/closed",
        headers: { authorization: `Bearer ${token}` },
      });
      assert.equal(res.statusCode, 404);
    }
  });
});

function now(): number {
  return Math.floor(Date.now() / 1000);
}
