// src/serialization/toJson.ts
// Serialize Matrix component to JSON Schema representation
//
// JSON Schema provides a standard format for component definition
// that can be used for validation and documentation.
//
// Reference: Vision/14-HOMOICONIC-ARCHITECTURE.md (Pillars 15-16: SERIALIZE/EVAL)

import type { IComponentSchema } from './IComponentSchema';
import type { ISerializationOptions } from './ISerializationOptions';
import type { IParamSchema } from './IParamSchema';

/**
 * Convert component schema to JSON Schema representation
 *
 * @example
 * toJson({ name: 'Button', accepts: { click: { params: { id: { type: 'string' } } } }, emits: { clicked: {} } })
 * // Returns JSON Schema for the component
 */
export function toJson(
  schema: IComponentSchema,
  options: ISerializationOptions = {}
): string {
  const { pretty = true, indent = '  ' } = options;
  const jsonSchema = toJsonSchema(schema);
  return JSON.stringify(jsonSchema, null, pretty ? 2 : undefined);
}

/**
 * Convert to JSON Schema object
 */
export function toJsonSchema(schema: IComponentSchema): JsonSchemaComponent {
  const result: JsonSchemaComponent = {
    $schema: 'https://json-schema.org/draft/2020-12/schema',
    $id: schema.mount || `schema/${toKebabCase(schema.name)}`,
    title: schema.name,
    type: 'object',
    properties: {
      accepts: {
        type: 'object',
        properties: {},
      },
      emits: {
        type: 'object',
        properties: {},
      },
    },
  };

  // Add mount if present
  if (schema.mount) {
    result.properties.mount = {
      type: 'string',
      const: schema.mount,
    };
  }

  // Convert accepts
  for (const [name, method] of Object.entries(schema.accepts)) {
    (result.properties.accepts as JsonSchemaObject).properties[name] = {
      type: 'object',
      description: method.description,
      properties: {
        params: convertParamsToJsonSchema(method.params),
      },
    };
  }

  // Convert emits
  for (const [name, event] of Object.entries(schema.emits)) {
    (result.properties.emits as JsonSchemaObject).properties[name] = {
      type: 'object',
      description: event.description,
      properties: {
        payload: convertParamsToJsonSchema(event.payload),
      },
    };
  }

  // Add children if present
  if (schema.children && schema.children.length > 0) {
    result.properties.children = {
      type: 'array',
      items: schema.children.map(toJsonSchema),
    };
  }

  return result;
}

/**
 * Convert params to JSON Schema properties
 */
function convertParamsToJsonSchema(
  params: Record<string, IParamSchema> | undefined
): JsonSchemaObject {
  const properties: Record<string, JsonSchemaProperty> = {};
  const required: string[] = [];

  if (params) {
    for (const [name, param] of Object.entries(params)) {
      properties[name] = {
        type: mapType(param.type),
        description: param.description,
      };

      if (param.default !== undefined) {
        properties[name].default = param.default;
      }

      if (!param.optional) {
        required.push(name);
      }
    }
  }

  return {
    type: 'object',
    properties,
    required: required.length > 0 ? required : undefined,
  };
}

/**
 * Map internal type to JSON Schema type
 */
function mapType(type: string): string | string[] {
  switch (type) {
    case 'string':
      return 'string';
    case 'number':
      return 'number';
    case 'boolean':
      return 'boolean';
    case 'object':
      return 'object';
    case 'array':
      return 'array';
    case 'any':
      return ['string', 'number', 'boolean', 'object', 'array', 'null'];
    default:
      return 'string';
  }
}

/**
 * Convert to kebab-case
 */
function toKebabCase(str: string): string {
  return str
    .replace(/([a-z])([A-Z])/g, '$1-$2')
    .replace(/[\s_]+/g, '-')
    .toLowerCase();
}

// JSON Schema types
interface JsonSchemaComponent {
  $schema: string;
  $id: string;
  title: string;
  type: string;
  properties: Record<string, JsonSchemaProperty | JsonSchemaObject>;
}

interface JsonSchemaObject {
  type: 'object';
  properties: Record<string, JsonSchemaProperty>;
  required?: string[];
  description?: string;
}

interface JsonSchemaProperty {
  type: string | string[];
  description?: string;
  default?: unknown;
  const?: string;
  items?: JsonSchemaComponent | JsonSchemaComponent[];
  properties?: Record<string, JsonSchemaProperty>;
  required?: string[];
}
