// src/lisp/primitives/serialization.ts
// Serialization Primitives: to-sexpr, to-html, to-json, to-fluent, to-topics, serialize
//
// These primitives expose the 5-representation parity system to LISP evaluation.
// Reference: Vision/14-HOMOICONIC-ARCHITECTURE.md (Pillars 15-16: SERIALIZE/EVAL)

import type { SExpr } from '../SExpr';
import type { IPrimitiveDefinition } from './mccarthy';

// Import serialization functions
import {
  toSExpr as componentToSExpr,
  toHtml,
  toJson,
  toFluent,
  toTopics,
  serialize,
} from '../../serialization';
import type { IComponentSchema, SerializationFormat, IMethodSchema, IEventSchema, IParamSchema } from '../../serialization';

/**
 * TO-SEXPR - Convert component schema to S-expression syntax
 * (to-sexpr schema) → "(component \"Button\" ...)"
 *
 * @example
 * (to-sexpr (introspect "flowpad.db"))
 * ; → "(component \"MockDbService\" (accepts \"Query\" ...) (emits \"queryComplete\" ...))"
 */
export const toSexpr: IPrimitiveDefinition = {
  name: 'to-sexpr',
  aliases: ['to-s-expr', 'schema->sexpr'],
  handler: async (args, evaluate) => {
    const schema = await evaluate(args[0]);
    if (!schema || typeof schema !== 'object') {
      throw new Error('to-sexpr: expected component schema object');
    }
    const componentSchema = normalizeToSchema(schema as Record<string, unknown>);
    return componentToSExpr(componentSchema);
  },
};

/**
 * TO-HTML - Convert component schema to HTML custom element syntax
 * (to-html schema) → "<matrix-button>...</matrix-button>"
 *
 * @example
 * (to-html (introspect "flowpad.tree"))
 */
export const toHtmlPrim: IPrimitiveDefinition = {
  name: 'to-html',
  aliases: ['schema->html'],
  handler: async (args, evaluate) => {
    const schema = await evaluate(args[0]);
    if (!schema || typeof schema !== 'object') {
      throw new Error('to-html: expected component schema object');
    }
    const componentSchema = normalizeToSchema(schema as Record<string, unknown>);
    return toHtml(componentSchema);
  },
};

/**
 * TO-JSON - Convert component schema to JSON string
 * (to-json schema) → "{\"name\": \"Button\", ...}"
 *
 * @example
 * (to-json (introspect "flowpad.db"))
 */
export const toJsonPrim: IPrimitiveDefinition = {
  name: 'to-json',
  aliases: ['schema->json'],
  handler: async (args, evaluate) => {
    const schema = await evaluate(args[0]);
    if (!schema || typeof schema !== 'object') {
      throw new Error('to-json: expected component schema object');
    }
    const componentSchema = normalizeToSchema(schema as Record<string, unknown>);
    return toJson(componentSchema);
  },
};

/**
 * TO-FLUENT - Convert component schema to Fluent DSL syntax
 * (to-fluent schema) → "component('Button').accepts('click')..."
 *
 * @example
 * (to-fluent (introspect "flowpad.db"))
 */
export const toFluentPrim: IPrimitiveDefinition = {
  name: 'to-fluent',
  aliases: ['schema->fluent'],
  handler: async (args, evaluate) => {
    const schema = await evaluate(args[0]);
    if (!schema || typeof schema !== 'object') {
      throw new Error('to-fluent: expected component schema object');
    }
    const componentSchema = normalizeToSchema(schema as Record<string, unknown>);
    return toFluent(componentSchema);
  },
};

/**
 * TO-TOPICS - Convert component schema to Matrix pub/sub topics
 * (to-topics schema) → "button.accepts.click\nbutton.emits.clicked"
 *
 * @example
 * (to-topics (introspect "flowpad.db"))
 */
export const toTopicsPrim: IPrimitiveDefinition = {
  name: 'to-topics',
  aliases: ['schema->topics'],
  handler: async (args, evaluate) => {
    const schema = await evaluate(args[0]);
    if (!schema || typeof schema !== 'object') {
      throw new Error('to-topics: expected component schema object');
    }
    const componentSchema = normalizeToSchema(schema as Record<string, unknown>);
    return toTopics(componentSchema);
  },
};

/**
 * SERIALIZE - Convert component schema to any format
 * (serialize schema "html") → format-specific output
 *
 * @example
 * (serialize (introspect "flowpad.db") "sexpr")
 * (serialize (introspect "flowpad.db") "html")
 */
export const serializePrim: IPrimitiveDefinition = {
  name: 'serialize',
  handler: async (args, evaluate) => {
    const schema = await evaluate(args[0]);
    const format = await evaluate(args[1]) as SerializationFormat;

    if (!schema || typeof schema !== 'object') {
      throw new Error('serialize: expected component schema object as first argument');
    }
    if (!format || typeof format !== 'string') {
      throw new Error('serialize: expected format string as second argument (html, fluent, sexpr, topics, json)');
    }

    const componentSchema = normalizeToSchema(schema as Record<string, unknown>);
    return serialize(componentSchema, format);
  },
};

/**
 * Normalize introspect result to IComponentSchema format.
 * The introspect result may have different field names than IComponentSchema expects.
 */
function normalizeToSchema(input: Record<string, unknown>): IComponentSchema {
  // If it already looks like a IComponentSchema, use it with proper typing
  if (input.name && typeof input.accepts === 'object' && typeof input.emits === 'object') {
    return {
      name: input.name as string,
      mount: input.mount as string | undefined,
      accepts: normalizeMethods(input.accepts as Record<string, unknown>),
      emits: normalizeEvents(input.emits as Record<string, unknown>),
      children: input.children as IComponentSchema[] | undefined,
    };
  }

  // Convert from introspect result format (componentClass instead of name)
  return {
    name: (input.componentClass as string) || (input.name as string) || 'Unknown',
    mount: input.mount as string | undefined,
    accepts: normalizeMethods(input.accepts as Record<string, unknown> || {}),
    emits: normalizeEvents(input.emits as Record<string, unknown> || {}),
    children: input.children as IComponentSchema[] | undefined,
  };
}

/**
 * Normalize methods object to IMethodSchema format
 */
function normalizeMethods(obj: Record<string, unknown>): Record<string, IMethodSchema> {
  const result: Record<string, IMethodSchema> = {};

  for (const [name, value] of Object.entries(obj)) {
    if (typeof value === 'object' && value !== null) {
      const methodDef = value as Record<string, unknown>;
      result[name] = {
        params: normalizeParams(methodDef.params as Record<string, unknown> | undefined),
      };
    } else {
      // Simple definition without params
      result[name] = { params: {} };
    }
  }

  return result;
}

/**
 * Normalize events object to IEventSchema format
 */
function normalizeEvents(obj: Record<string, unknown>): Record<string, IEventSchema> {
  const result: Record<string, IEventSchema> = {};

  for (const [name, value] of Object.entries(obj)) {
    if (typeof value === 'object' && value !== null) {
      const eventDef = value as Record<string, unknown>;
      result[name] = {
        payload: normalizeParams(eventDef.payload as Record<string, unknown> | undefined),
      };
    } else {
      // Simple definition without payload
      result[name] = { payload: {} };
    }
  }

  return result;
}

/**
 * Normalize params/payload to IParamSchema format
 */
function normalizeParams(params: Record<string, unknown> | undefined): Record<string, IParamSchema> {
  if (!params) return {};

  const result: Record<string, IParamSchema> = {};

  for (const [name, value] of Object.entries(params)) {
    if (typeof value === 'object' && value !== null) {
      const paramDef = value as Record<string, unknown>;
      result[name] = {
        type: (paramDef.type as IParamSchema['type']) || 'any',
        optional: paramDef.optional as boolean | undefined,
      };
    } else if (typeof value === 'string') {
      // Simple type string like "string" or "number"
      result[name] = { type: value as IParamSchema['type'] };
    } else {
      result[name] = { type: 'any' };
    }
  }

  return result;
}

/**
 * All serialization primitives
 */
export const serializationPrimitives: IPrimitiveDefinition[] = [
  toSexpr,
  toHtmlPrim,
  toJsonPrim,
  toFluentPrim,
  toTopicsPrim,
  serializePrim,
];
