import { DataSource } from "typeorm";
import { TenantContext, TenantContextStorage } from "./tenant-context.storage";

function fakeContext(tenantId: number): TenantContext {
  return {
    tenantId,
    identidadId: 1,
    usuarioLocalId: 1,
    rolTenant: "admin",
    tiendaId: 1,
    rolPlataforma: "ninguno",
    via: "tenant",
    dataSource: {} as DataSource,
  };
}

describe("TenantContextStorage", () => {
  let storage: TenantContextStorage;

  beforeEach(() => {
    storage = new TenantContextStorage();
  });

  it("populate() fuera de runEmpty() lanza — no hay contexto abierto", () => {
    expect(() => storage.populate(fakeContext(1))).toThrow(/sin un contexto abierto/);
  });

  it("getOrThrow() sin populate() lanza — no hay tenant 'por defecto'", async () => {
    await storage.runEmpty(async () => {
      expect(() => storage.getOrThrow()).toThrow(/No hay contexto de tenant resuelto/);
    });
  });

  it("populate() una vez, dentro de runEmpty(), deja el contexto disponible via getOrThrow()", async () => {
    await storage.runEmpty(async () => {
      storage.populate(fakeContext(42));
      expect(storage.getOrThrow().tenantId).toBe(42);
    });
  });

  it("DE UNA SOLA ESCRITURA: un segundo populate() en el mismo request LANZA, no pisa el valor", async () => {
    await storage.runEmpty(async () => {
      storage.populate(fakeContext(1));
      expect(() => storage.populate(fakeContext(2))).toThrow(/ya fue llamado en este request/);
      // El valor original sigue intacto — el intento fallido no lo corrompio.
      expect(storage.getOrThrow().tenantId).toBe(1);
    });
  });

  it("dos requests concurrentes (dos runEmpty distintos) no se pisan entre si", async () => {
    const resultados: number[] = [];

    await Promise.all([
      storage.runEmpty(async () => {
        storage.populate(fakeContext(100));
        await new Promise((resolve) => setTimeout(resolve, 20));
        resultados.push(storage.getOrThrow().tenantId);
      }),
      storage.runEmpty(async () => {
        storage.populate(fakeContext(200));
        await new Promise((resolve) => setTimeout(resolve, 5));
        resultados.push(storage.getOrThrow().tenantId);
      }),
    ]);

    expect(resultados.sort()).toEqual([100, 200]);
  });
});
