import { describe, it, expect, beforeEach } from "vitest";
import { defineFactory } from "@/factory/define-factory";

interface User {
  id: string;
  name: string;
  age: number;
  createdAt: Date;
}

describe("defineFactory", () => {
  const userFactory = defineFactory<User>(({ sequence }) => ({
    id: `user-${sequence}`,
    name: `User ${sequence}`,
    age: 30,
    createdAt: new Date("2026-01-01T00:00:00Z"),
  }));

  beforeEach(() => userFactory.reset());

  it("builds a default object", () => {
    const u = userFactory.build();
    expect(u).toEqual({
      id: "user-1",
      name: "User 1",
      age: 30,
      createdAt: new Date("2026-01-01T00:00:00Z"),
    });
  });

  it("increments sequence per build", () => {
    const a = userFactory.build();
    const b = userFactory.build();
    expect(a.id).toBe("user-1");
    expect(b.id).toBe("user-2");
  });

  it("applies overrides", () => {
    const u = userFactory.build({ name: "Alice", age: 25 });
    expect(u.name).toBe("Alice");
    expect(u.age).toBe(25);
    expect(u.id).toBe("user-1");
  });

  it("buildList builds N items with same overrides", () => {
    const list = userFactory.buildList(3, { age: 40 });
    expect(list).toHaveLength(3);
    expect(list.map((u) => u.id)).toEqual(["user-1", "user-2", "user-3"]);
    expect(list.every((u) => u.age === 40)).toBe(true);
  });

  it("reset() restarts the sequence", () => {
    userFactory.build();
    userFactory.build();
    userFactory.reset();
    expect(userFactory.build().id).toBe("user-1");
  });

  it("deep-merges nested object overrides without losing sibling keys", () => {
    const factory = defineFactory<{ id: string; meta: { source: string; tags: string[] } }>(({ sequence }) => ({
      id: `id-${sequence}`,
      meta: { source: "default", tags: ["a", "b"] },
    }));
    const result = factory.build({ meta: { source: "custom" } as never });
    expect(result.meta.source).toBe("custom");
    expect(result.meta.tags).toEqual(["a", "b"]); // sibling key preserved
  });

  it("replaces array overrides atomically (does not concat)", () => {
    const factory = defineFactory<{ tags: string[] }>(() => ({ tags: ["a", "b"] }));
    const result = factory.build({ tags: ["c"] });
    expect(result.tags).toEqual(["c"]);
  });

  it("replaces Date overrides atomically", () => {
    const factory = defineFactory<{ when: Date }>(() => ({ when: new Date("2026-01-01") }));
    const result = factory.build({ when: new Date("2030-12-31") });
    expect(result.when.getFullYear()).toBe(2030);
  });
});
