/**
 * Zod Schema Generator for Kumo Component Registry
 *
 * Generates TypeScript file with Zod schemas from component-registry.json.
 * This enables runtime validation of AI-generated UI trees.
 *
 * Output: catalog/schemas.ts
 */

import type {
  ComponentRegistry,
  ComponentSchema,
  PropSchema,
} from "./types.js";

/**
 * Convert a PropSchema type to its Zod equivalent
 */
function propTypeToZod(prop: PropSchema): string {
  const { type, values, required, optional } = prop;

  let zodType: string;

  // Handle enum types with explicit values
  if (type === "enum" && values && values.length > 0) {
    const enumValues = values.map((v) => `"${v}"`).join(", ");
    zodType = `z.enum([${enumValues}])`;
  }
  // Handle common types
  else if (type === "string") {
    zodType = "z.string()";
  } else if (type === "number") {
    zodType = "z.number()";
  } else if (type === "boolean") {
    zodType = "z.boolean()";
  } else if (type === "ReactNode") {
    // ReactNode can be string, number, boolean, null, or a dynamic path reference
    zodType =
      "z.union([z.string(), z.number(), z.boolean(), z.null(), DynamicValueSchema])";
  } else if (type.endsWith("[]")) {
    // Array types
    const itemType = type.slice(0, -2);
    const itemZod = itemType === "string" ? "z.string()" : "z.unknown()";
    zodType = `z.array(${itemZod})`;
  } else if (type.includes("|")) {
    // Union types - simplified handling
    zodType = "z.unknown()";
  } else {
    // Fallback for complex types
    zodType = "z.unknown()";
  }

  // Make optional if not required
  if (optional || !required) {
    zodType = `${zodType}.optional()`;
  }

  return zodType;
}

/**
 * Generate Zod schema for a single component's props
 */
function generateComponentPropsSchema(
  componentName: string,
  schema: ComponentSchema,
): string {
  const propsEntries = Object.entries(schema.props);

  if (propsEntries.length === 0) {
    return `export const ${componentName}PropsSchema = z.object({});`;
  }

  const propsLines = propsEntries.map(([propName, propSchema]) => {
    const zodType = propTypeToZod(propSchema);
    // Replace newlines with spaces to keep comments on a single line
    const sanitizedDescription = propSchema.description
      ?.replace(/\n+/g, " ")
      .trim();
    const comment = sanitizedDescription ? ` // ${sanitizedDescription}` : "";
    return `  ${propName}: ${zodType},${comment}`;
  });

  return `export const ${componentName}PropsSchema = z.object({
${propsLines.join("\n")}
});`;
}

/**
 * Generate the complete schemas.ts file content
 */
export function generateSchemasFile(registry: ComponentRegistry): string {
  const componentNames = Object.keys(registry.components).sort();

  const lines: string[] = [
    "/**",
    " * Auto-generated Zod schemas for Kumo components",
    " * DO NOT EDIT - Generated by scripts/component-registry/index.ts",
    " *",
    " * These schemas enable runtime validation of AI-generated UI trees.",
    " */",
    "",
    'import { z } from "zod";',
    "",
    "// SafeParseReturnType was removed in Zod v4, but we still want stable typing.",
    "export type SafeParseResult<T> =",
    "  | { success: true; data: T; error?: never }",
    "  | { success: false; error: z.ZodError<any>; data?: never };",
    "",
    "// =============================================================================",
    "// Dynamic Value Schema (for data binding)",
    "// =============================================================================",
    "",
    "/**",
    " * A value that can either be a literal or a reference to the data model.",
    ' * Example: "Hello" or { path: "/user/name" }',
    " */",
    "export const DynamicValueSchema = z.union([",
    "  z.string(),",
    "  z.number(),",
    "  z.boolean(),",
    "  z.null(),",
    "  z.object({ path: z.string() }),",
    "]);",
    "",
    "export type DynamicValue<T = unknown> = T | { path: string };",
    "",
    "// =============================================================================",
    "// Visibility Condition Schemas",
    "// =============================================================================",
    "",
    "/**",
    " * Visibility conditions for conditional rendering.",
    " * Components can be shown/hidden based on data, auth, or logic expressions.",
    " */",
    "",
    "// Forward declaration for recursive types",
    "// Note: Numeric comparisons use DynamicValue (not DynamicValue<number>) for Zod compatibility",
    "export type LogicExpression =",
    "  | { and: LogicExpression[] }",
    "  | { or: LogicExpression[] }",
    "  | { not: LogicExpression }",
    "  | { path: string }",
    "  | { eq: [DynamicValue, DynamicValue] }",
    "  | { neq: [DynamicValue, DynamicValue] }",
    "  | { gt: [DynamicValue, DynamicValue] }",
    "  | { gte: [DynamicValue, DynamicValue] }",
    "  | { lt: [DynamicValue, DynamicValue] }",
    "  | { lte: [DynamicValue, DynamicValue] };",
    "",
    "// Lazy schema for recursive logic expressions",
    "const LogicExpressionSchema: z.ZodType<LogicExpression> = z.lazy(() =>",
    "  z.union([",
    "    z.object({ and: z.array(LogicExpressionSchema) }),",
    "    z.object({ or: z.array(LogicExpressionSchema) }),",
    "    z.object({ not: LogicExpressionSchema }),",
    "    z.object({ path: z.string() }),",
    "    z.object({ eq: z.tuple([DynamicValueSchema, DynamicValueSchema]) }),",
    "    z.object({ neq: z.tuple([DynamicValueSchema, DynamicValueSchema]) }),",
    "    z.object({ gt: z.tuple([DynamicValueSchema, DynamicValueSchema]) }),",
    "    z.object({ gte: z.tuple([DynamicValueSchema, DynamicValueSchema]) }),",
    "    z.object({ lt: z.tuple([DynamicValueSchema, DynamicValueSchema]) }),",
    "    z.object({ lte: z.tuple([DynamicValueSchema, DynamicValueSchema]) }),",
    "  ])",
    ");",
    "",
    "export const VisibilityConditionSchema = z.union([",
    "  z.boolean(),",
    "  z.object({ path: z.string() }),",
    '  z.object({ auth: z.enum(["signedIn", "signedOut"]) }),',
    "  LogicExpressionSchema,",
    "]);",
    "",
    "export type VisibilityCondition = z.infer<typeof VisibilityConditionSchema>;",
    "",
    "// =============================================================================",
    "// Action Schemas",
    "// =============================================================================",
    "",
    "/**",
    " * Action confirmation dialog configuration",
    " */",
    "export const ActionConfirmSchema = z.object({",
    "  title: z.string(),",
    "  message: z.string(),",
    '  variant: z.enum(["default", "danger"]).optional(),',
    "  confirmLabel: z.string().optional(),",
    "  cancelLabel: z.string().optional(),",
    "});",
    "",
    "/**",
    " * Action definition that AI can declare",
    " */",
    "export const ActionSchema = z.object({",
    "  name: z.string(),",
    "  params: z.record(z.string(), DynamicValueSchema).optional(),",
    "  confirm: ActionConfirmSchema.optional(),",
    "  onSuccess: z.object({ set: z.record(z.string(), DynamicValueSchema) }).optional(),",
    "  onError: z.object({ set: z.record(z.string(), DynamicValueSchema) }).optional(),",
    "});",
    "",
    "export type Action = z.infer<typeof ActionSchema>;",
    "",
    "// =============================================================================",
    "// Component Props Schemas",
    "// =============================================================================",
    "",
  ];

  // Generate schema for each component
  for (const name of componentNames) {
    const schema = registry.components[name];
    lines.push(generateComponentPropsSchema(name, schema));
    lines.push("");
  }

  // Generate the component type union
  lines.push(
    "// =============================================================================",
  );
  lines.push("// Component Type Union");
  lines.push(
    "// =============================================================================",
  );
  lines.push("");
  lines.push("/**");
  lines.push(" * All valid component type names");
  lines.push(" */");
  const typeUnion = componentNames.map((n) => `"${n}"`).join(" | ");
  lines.push(`export type KumoComponentType = ${typeUnion};`);
  lines.push("");
  lines.push("export const KumoComponentTypeSchema = z.enum([");
  for (const name of componentNames) {
    lines.push(`  "${name}",`);
  }
  lines.push("]);");
  lines.push("");

  // Generate props schema map
  lines.push("/**");
  lines.push(" * Map of component type to its props schema");
  lines.push(" */");
  lines.push("export const ComponentPropsSchemas = {");
  for (const name of componentNames) {
    lines.push(`  ${name}: ${name}PropsSchema,`);
  }
  lines.push("} as const;");
  lines.push("");

  // Generate UIElement schema
  lines.push(
    "// =============================================================================",
  );
  lines.push("// UI Element & Tree Schemas");
  lines.push(
    "// =============================================================================",
  );
  lines.push("");
  lines.push("/**");
  lines.push(" * Base UI element structure");
  lines.push(" */");
  lines.push("export const UIElementBaseSchema = z.object({");
  lines.push("  key: z.string(),");
  lines.push("  type: KumoComponentTypeSchema,");
  lines.push("  props: z.record(z.string(), z.unknown()),");
  lines.push("  children: z.array(z.string()).optional(),");
  lines.push("  parentKey: z.string().nullable().optional(),");
  lines.push("  visible: VisibilityConditionSchema.optional(),");
  lines.push("  action: ActionSchema.optional(),");
  lines.push("});");
  lines.push("");
  lines.push("export type UIElement = z.infer<typeof UIElementBaseSchema>;");
  lines.push("");
  lines.push("/**");
  lines.push(" * Flat UI tree structure (optimized for LLM generation)");
  lines.push(" */");
  lines.push("export const UITreeSchema = z.object({");
  lines.push("  root: z.string(),");
  lines.push("  elements: z.record(z.string(), UIElementBaseSchema),");
  lines.push("});");
  lines.push("");
  lines.push("export type UITree = z.infer<typeof UITreeSchema>;");
  lines.push("");

  // Generate validation helpers
  lines.push(
    "// =============================================================================",
  );
  lines.push("// Validation Helpers");
  lines.push(
    "// =============================================================================",
  );
  lines.push("");
  lines.push("/**");
  lines.push(" * Validate an element's props against its component schema");
  lines.push(" */");
  lines.push(
    "export function validateElementProps(element: UIElement): SafeParseResult<unknown> {",
  );
  lines.push(
    "  const schema = ComponentPropsSchemas[element.type as keyof typeof ComponentPropsSchemas];",
  );
  lines.push("  if (!schema) {");
  lines.push(
    "    return { success: false, error: new z.ZodError([{ code: 'custom', message: `Unknown component type: ${element.type}`, path: ['type'] }]) };",
  );
  lines.push("  }");
  lines.push("  return schema.safeParse(element.props);");
  lines.push("}");
  lines.push("");
  lines.push("/**");
  lines.push(" * Validate a complete UI tree");
  lines.push(" */");
  lines.push(
    "export function validateUITree(tree: unknown): SafeParseResult<UITree> {",
  );
  lines.push("  return UITreeSchema.safeParse(tree);");
  lines.push("}");
  lines.push("");

  // Export component names for catalog generation
  lines.push("/**");
  lines.push(" * List of all component names (for catalog generation)");
  lines.push(" */");
  lines.push(
    `export const KUMO_COMPONENT_NAMES = [${componentNames.map((n) => `"${n}"`).join(", ")}] as const;`,
  );
  lines.push("");

  return lines.join("\n");
}
