import { z } from "zod";
import { BlueprintTarget, FieldType } from "../types/enums.js";

export const FieldOptionSchema = z.object({
  value: z.string().min(1),
  label: z.string().min(1),
  color: z.string().optional(),
});

export const FieldValidationSchema = z.object({
  min: z.number().optional(),
  max: z.number().optional(),
  minLength: z.number().int().optional(),
  maxLength: z.number().int().optional(),
  pattern: z.string().optional(),
  message: z.string().optional(),
});

export const BlueprintFieldDefinitionSchema = z.object({
  id: z.string().min(1),
  label: z.string().min(1).max(200),
  key: z.string().min(1).max(100).regex(/^[a-z_][a-z0-9_]*$/, "Must be snake_case"),
  type: z.nativeEnum(FieldType),
  required: z.boolean().default(false),
  description: z.string().optional(),
  placeholder: z.string().optional(),
  defaultValue: z.unknown().optional(),
  options: z.array(FieldOptionSchema).optional(),
  validation: FieldValidationSchema.optional(),
  order: z.number().int().min(0),
  group: z.string().optional(),
});

export const CreateBlueprintSchema = z.object({
  name: z.string().min(1).max(200),
  target: z.nativeEnum(BlueprintTarget),
  description: z.string().optional(),
  fieldDefs: z.array(BlueprintFieldDefinitionSchema).default([]),
  defaults: z.record(z.string(), z.unknown()).default({}),
  validationRules: z.array(z.object({
    field: z.string(),
    rule: z.enum(["required_if", "unique", "min", "max"]),
    params: z.unknown().optional(),
    message: z.string().optional(),
  })).default([]),
});

export const UpdateBlueprintSchema = CreateBlueprintSchema.partial();

export type CreateBlueprintInput = z.infer<typeof CreateBlueprintSchema>;
export type UpdateBlueprintInput = z.infer<typeof UpdateBlueprintSchema>;

/** Generate a Zod schema from blueprint field definitions at runtime */
export function generateDynamicZodSchema(
  fieldDefs: z.infer<typeof BlueprintFieldDefinitionSchema>[],
): z.ZodObject<Record<string, z.ZodTypeAny>> {
  const shape: Record<string, z.ZodTypeAny> = {};

  for (const field of fieldDefs) {
    let fieldSchema: z.ZodTypeAny;

    switch (field.type) {
      case FieldType.TEXT:
      case FieldType.TEXTAREA:
      case FieldType.URL:
      case FieldType.EMAIL:
        fieldSchema = z.string();
        if (field.validation?.minLength !== undefined) {
          fieldSchema = (fieldSchema as z.ZodString).min(field.validation.minLength);
        }
        if (field.validation?.maxLength !== undefined) {
          fieldSchema = (fieldSchema as z.ZodString).max(field.validation.maxLength);
        }
        if (field.type === FieldType.EMAIL) {
          fieldSchema = (fieldSchema as z.ZodString).email();
        }
        if (field.type === FieldType.URL) {
          fieldSchema = (fieldSchema as z.ZodString).url();
        }
        break;
      case FieldType.NUMBER:
        fieldSchema = z.number();
        if (field.validation?.min !== undefined) {
          fieldSchema = (fieldSchema as z.ZodNumber).min(field.validation.min);
        }
        if (field.validation?.max !== undefined) {
          fieldSchema = (fieldSchema as z.ZodNumber).max(field.validation.max);
        }
        break;
      case FieldType.BOOLEAN:
        fieldSchema = z.boolean();
        break;
      case FieldType.DATE:
        fieldSchema = z.coerce.date();
        break;
      case FieldType.SELECT:
        if (field.options && field.options.length > 0) {
          const values = field.options.map((o) => o.value) as [string, ...string[]];
          fieldSchema = z.enum(values);
        } else {
          fieldSchema = z.string();
        }
        break;
      case FieldType.MULTI_SELECT:
        if (field.options && field.options.length > 0) {
          const values = field.options.map((o) => o.value) as [string, ...string[]];
          fieldSchema = z.array(z.enum(values));
        } else {
          fieldSchema = z.array(z.string());
        }
        break;
      default:
        fieldSchema = z.unknown();
    }

    if (!field.required) {
      fieldSchema = fieldSchema.optional();
    }

    shape[field.key] = fieldSchema;
  }

  return z.object(shape);
}
