import { validateCustomFields } from "@capakraken/engine";
import { BlueprintTarget, type BlueprintFieldDefinition } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { findUniqueOrThrow } from "../db/helpers.js";

interface BlueprintLookup {
  blueprint: {
    findUnique: (args: {
      where: { id: string };
      select: { fieldDefs: true; target: true };
    }) => Promise<{ fieldDefs: unknown; target: string } | null>;
    findMany: (args: {
      where: { target: BlueprintTarget; isGlobal: boolean; isActive: boolean };
      select: { fieldDefs: true };
    }) => Promise<Array<{ fieldDefs: unknown }>>;
  };
}

interface AssertBlueprintDynamicFieldsInput {
  db: BlueprintLookup;
  blueprintId: string | undefined;
  dynamicFields: Record<string, unknown>;
  target: BlueprintTarget;
}

export async function assertBlueprintDynamicFields({
  db,
  blueprintId,
  dynamicFields,
  target,
}: AssertBlueprintDynamicFieldsInput): Promise<void> {
  // Collect field defs from the entity's specific blueprint (if any)
  let specificFieldDefs: BlueprintFieldDefinition[] = [];

  if (blueprintId) {
    const blueprint = await findUniqueOrThrow(
      db.blueprint.findUnique({
        where: { id: blueprintId },
        select: { fieldDefs: true, target: true },
      }),
      "Blueprint",
    );

    if (blueprint.target !== target) {
      throw new TRPCError({
        code: "BAD_REQUEST",
        message: `${target} entities require a ${target.toLowerCase()} blueprint`,
      });
    }

    specificFieldDefs = blueprint.fieldDefs as BlueprintFieldDefinition[];
  }

  // Also collect field defs from all active global blueprints for this target
  const globalBlueprints = await db.blueprint.findMany({
    where: { target, isGlobal: true, isActive: true },
    select: { fieldDefs: true },
  });
  const globalFieldDefs = globalBlueprints.flatMap(
    (bp) => bp.fieldDefs as BlueprintFieldDefinition[],
  );

  // Merge: specific blueprint fields + global fields (specific takes precedence for same key)
  const specificKeys = new Set(specificFieldDefs.map((f) => f.key));
  const mergedFieldDefs = [
    ...specificFieldDefs,
    ...globalFieldDefs.filter((f) => !specificKeys.has(f.key)),
  ];

  if (mergedFieldDefs.length === 0) return;

  const errors = validateCustomFields(mergedFieldDefs, dynamicFields);

  if (errors.length > 0) {
    throw new TRPCError({
      code: "UNPROCESSABLE_CONTENT",
      message: errors.map((error) => error.message).join("; "),
    });
  }
}
