import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import {
  ASSIGNMENTS_STORAGE_KEY,
  VISITOR_ID_STORAGE_KEY,
  dataLayerExperimentAttribution,
  deterministicVariant,
  leadExperimentAttribution,
  resolveAndPersistAssignment,
} from "../lib/experiments.ts";
import { toPublicTicketTiers } from "../lib/public-ticket-tiers.ts";

const routes = JSON.parse(await readFile(new URL("../data/routes.json", import.meta.url), "utf8"));
const p0Routes = ["/", "/diagnostico", "/empresarios-e-gestores", "/masterclass", "/imersao"];

class MemoryStorage {
  values = new Map();
  getItem(key) { return this.values.get(key) ?? null; }
  setItem(key, value) { this.values.set(key, String(value)); }
}

function variantsFor(route) {
  return routes.ab_variants.filter((variant) => variant.route === route);
}

test("allocates each P0 experiment deterministically with a balanced 50/50 hash", () => {
  for (const route of p0Routes) assert.equal(variantsFor(route).length, 2);
  const experimentId = "AB01";
  let a = 0;
  let b = 0;
  for (let index = 0; index < 10_000; index += 1) {
    const visitor = `00000000-0000-4000-8000-${String(index).padStart(12, "0")}`;
    const first = deterministicVariant(visitor, experimentId);
    assert.equal(first, deterministicVariant(visitor, experimentId));
    if (first === "A") a += 1;
    else b += 1;
  }
  assert.ok(Math.abs(a - b) / (a + b) < 0.02, `split outside tolerance: A=${a}, B=${b}`);
});

test("persists an anonymous assignment and honours a query override", () => {
  const storage = new MemoryStorage();
  const args = {
    route: "/",
    variants: variantsFor("/"),
    storage,
    createVisitorId: () => "11111111-1111-4111-8111-111111111111",
    now: () => "2026-08-31T00:00:00.000Z",
  };
  const allocated = resolveAndPersistAssignment(args);
  assert.equal(allocated.assignment_method, "deterministic_50_50");
  assert.equal(storage.getItem(VISITOR_ID_STORAGE_KEY), "11111111-1111-4111-8111-111111111111");

  const repeated = resolveAndPersistAssignment(args);
  assert.equal(repeated.variant, allocated.variant);
  assert.equal(repeated.assignment_method, "persisted");

  const overrideVariant = allocated.variant === "A" ? "B" : "A";
  const overridden = resolveAndPersistAssignment({ ...args, requestedVariant: overrideVariant });
  assert.equal(overridden.variant, overrideVariant);
  assert.equal(overridden.assignment_method, "query_override");

  const afterOverride = resolveAndPersistAssignment(args);
  assert.equal(afterOverride.variant, overrideVariant);
  assert.equal(afterOverride.assignment_method, "persisted");
  assert.match(storage.getItem(ASSIGNMENTS_STORAGE_KEY), new RegExp(`"variant":"${overrideVariant}"`));
});

test("adds experiment attribution to events and lead payloads without visitor IDs", () => {
  const assignment = { experiment_id: "AB03", route: "/empresarios-e-gestores", variant: "B", assignment_method: "query_override" };
  assert.deepEqual(leadExperimentAttribution(assignment), {
    experimentId: "AB03",
    experimentVariant: "B",
    assignmentMethod: "query_override",
  });
  assert.deepEqual(dataLayerExperimentAttribution(assignment), {
    experiment_id: "AB03",
    variant: "B",
    assignment_method: "query_override",
  });
  assert.doesNotMatch(JSON.stringify({ ...leadExperimentAttribution(assignment), ...dataLayerExperimentAttribution(assignment) }), /visitor/i);
});

test("only serializes the public ticket-tier allow-list to the client renderer", () => {
  const gated = routes.ticket_model.tiers;
  const publicPayload = toPublicTicketTiers(gated);
  assert.deepEqual(Object.keys(publicPayload[0]).sort(), ["draft_inclusions", "id", "label"]);
  const serialized = JSON.stringify(publicPayload);
  for (const privateField of ["founders", "batch_1", "batch_2", "final", "currency"]) {
    assert.doesNotMatch(serialized, new RegExp(`"${privateField}"`));
  }
});
