import { BlueprintTarget, FieldType, type BlueprintFieldDefinition } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { describe, expect, it, vi } from "vitest";
import { assertBlueprintDynamicFields } from "../router/blueprint-validation.js";

function createDbMock(result: { fieldDefs: unknown; target: BlueprintTarget } | null) {
  return {
    blueprint: {
      findUnique: vi.fn().mockResolvedValue(result),
    },
  };
}

describe("assertBlueprintDynamicFields", () => {
  it("returns early when no blueprint is set", async () => {
    const db = createDbMock(null);

    await expect(
      assertBlueprintDynamicFields({
        db,
        blueprintId: undefined,
        dynamicFields: {},
        target: BlueprintTarget.PROJECT,
      }),
    ).resolves.toBeUndefined();

    expect(db.blueprint.findUnique).not.toHaveBeenCalled();
  });

  it("rejects a missing blueprint", async () => {
    const db = createDbMock(null);

    await expect(
      assertBlueprintDynamicFields({
        db,
        blueprintId: "bp_missing",
        dynamicFields: {},
        target: BlueprintTarget.PROJECT,
      }),
    ).rejects.toMatchObject({ code: "NOT_FOUND" } satisfies Partial<TRPCError>);
  });

  it("rejects a blueprint with the wrong target", async () => {
    const db = createDbMock({ fieldDefs: [], target: BlueprintTarget.RESOURCE });

    await expect(
      assertBlueprintDynamicFields({
        db,
        blueprintId: "bp_resource",
        dynamicFields: {},
        target: BlueprintTarget.PROJECT,
      }),
    ).rejects.toMatchObject({ code: "BAD_REQUEST" } satisfies Partial<TRPCError>);
  });

  it("rejects invalid dynamic field values", async () => {
    const fieldDefs: BlueprintFieldDefinition[] = [
      {
        id: "cost-center",
        key: "costCenter",
        label: "Cost Center",
        order: 0,
        type: FieldType.NUMBER,
        required: true,
      },
    ];
    const db = createDbMock({ fieldDefs, target: BlueprintTarget.PROJECT });

    await expect(
      assertBlueprintDynamicFields({
        db,
        blueprintId: "bp_project",
        dynamicFields: { costCenter: "abc" },
        target: BlueprintTarget.PROJECT,
      }),
    ).rejects.toMatchObject({ code: "UNPROCESSABLE_CONTENT" } satisfies Partial<TRPCError>);
  });

  it("accepts valid dynamic field values", async () => {
    const fieldDefs: BlueprintFieldDefinition[] = [
      {
        id: "cost-center",
        key: "costCenter",
        label: "Cost Center",
        order: 0,
        type: FieldType.NUMBER,
        required: true,
      },
    ];
    const db = createDbMock({ fieldDefs, target: BlueprintTarget.PROJECT });

    await expect(
      assertBlueprintDynamicFields({
        db,
        blueprintId: "bp_project",
        dynamicFields: { costCenter: 42 },
        target: BlueprintTarget.PROJECT,
      }),
    ).resolves.toBeUndefined();
  });
});
