import { describe, expect, test } from "bun:test";
import {
  assertSafeObject,
  createTrustedHtml,
  isPrivateAddress,
  isTrustedHtml,
  secureJsonStringify,
  setSecureCookie,
  unwrapTrustedHtml,
  validateUrl,
} from "../src/index.ts";

describe("@wrnexus/security", () => {
  test("escapes HTML-significant JSON and redacts secrets", () => {
    const json = secureJsonStringify({ html: "</script><img>", token: "secret", count: 1 });
    expect(json).toContain("\\u003c/script\\u003e");
    expect(json).toContain("[REDACTED]");
    expect(json).not.toContain("secret");
  });

  test("rejects prototype-pollution keys and unsafe URL protocols", () => {
    const unsafe = JSON.parse('{"__proto__":{"admin":true}}');
    expect(() => assertSafeObject(unsafe)).toThrow();
    expect(() => validateUrl("javascript:alert(1)")).toThrow();
  });

  test("identifies private IP ranges", () => {
    expect(isPrivateAddress("127.0.0.1")).toBe(true);
    expect(isPrivateAddress("10.1.2.3")).toBe(true);
    expect(isPrivateAddress("8.8.8.8")).toBe(false);
  });

  test("enforces __Host cookie rules", () => {
    const writes: unknown[] = [];
    const ctx = {
      url: new URL("https://example.com"),
      cookies: { set: (...args: unknown[]) => writes.push(args) },
    } as any;
    setSecureCookie(ctx, "__Host-session", "value");
    expect(writes).toHaveLength(1);
    expect(writes[0]).toEqual([
      "__Host-session",
      "value",
      expect.objectContaining({ secure: true, httpOnly: true, path: "/", domain: undefined }),
    ]);
  });

  test("accepts repeated references while still rejecting cycles", () => {
    const shared = { value: 1 };
    expect(() => assertSafeObject({ first: shared, second: shared })).not.toThrow();
    const cyclic: Record<string, unknown> = {};
    cyclic.self = cyclic;
    expect(() => assertSafeObject(cyclic)).toThrow();
  });

  test("trusted HTML requires an explicit sanitizer policy", () => {
    const value = createTrustedHtml('<p onclick="bad()">Hello</p>', {
      name: "test-policy",
      sanitize: (input) => input.replace(/\s+onclick="[^"]*"/g, ""),
    });
    expect(isTrustedHtml(value)).toBe(true);
    expect(unwrapTrustedHtml(value)).toBe("<p>Hello</p>");
    expect(() => unwrapTrustedHtml({ value: "<b>unsafe</b>" } as any)).toThrow();
  });
});
