import { describe, it, expect, expectTypeOf } from "vitest";
import { withConsent, type ConsentChecked } from "@/with-consent";
import type { IConsent } from "@/consent.interface";
import { isConsentChecked } from "@repo/core-shared/conformance";

function makeConsent(): IConsent {
  return {
    isGranted: () => true,
    grant: () => Promise.resolve(),
    withdraw: () => Promise.resolve(),
    getCategories: () => [],
  };
}

describe("withConsent", () => {
  it("returns a ConsentChecked<F>", () => {
    const consent = makeConsent();
    const fn = async (_input: { id: string }) => ({ ok: true });
    const wrapped = withConsent(consent, fn);
    expectTypeOf(wrapped).toMatchTypeOf<ConsentChecked<typeof fn>>();
  });

  it("attaches __consentChecked as a non-enumerable property on the wrapped function", () => {
    const consent = makeConsent();
    const fn = async () => ({ ok: true });
    const wrapped = withConsent(consent, fn);
    expect(isConsentChecked(wrapped)).toBe(true);
    expect(Object.keys(wrapped)).not.toContain("__consentChecked");
  });

  it("does NOT pollute the original input function with the brand", () => {
    const consent = makeConsent();
    const fn = async () => ({ ok: true });
    const wrapped = withConsent(consent, fn);
    expect(isConsentChecked(fn)).toBe(false);
    expect(wrapped).not.toBe(fn);
  });

  it("passes input and output through unchanged", async () => {
    const consent = makeConsent();
    const fn = async (input: { id: string }) => ({ ok: true, id: input.id });
    const wrapped = withConsent(consent, fn);
    const result = await wrapped({ id: "abc" });
    expect(result).toEqual({ ok: true, id: "abc" });
  });

  it("propagates errors", async () => {
    const consent = makeConsent();
    const err = new Error("boom");
    const wrapped = withConsent(consent, async () => {
      throw err;
    });
    await expect(wrapped()).rejects.toBe(err);
  });
});
