// src/serialization/fromSExpr.ts
// Parse S-expression representation to Matrix component schema
//
// Parses LISP-style component definitions into component schemas.
//
// Reference: Vision/14-HOMOICONIC-ARCHITECTURE.md, Vision/15-LISP-ON-PROTOCOL.md

import type { IComponentSchema } from './IComponentSchema';
import type { IMethodSchema } from './IMethodSchema';
import type { IEventSchema } from './IEventSchema';
import type { IParamSchema } from './IParamSchema';
import type { IParseResult } from './IParseResult';
import type { SExpr } from '../lisp/SExpr';
import { parseSExpr } from '../lisp/core/SExprParser';

/**
 * Parse S-expression string to component schema
 *
 * @example
 * fromSExpr('(component "Button" (accepts "click") (emits "clicked"))')
 * // Returns: { success: true, value: { name: 'Button', accepts: { click: {} }, emits: { clicked: {} } } }
 */
export function fromSExpr(source: string): IParseResult<IComponentSchema> {
  try {
    const parsed = parseSExpr(source);
    return fromSExprValue(parsed);
  } catch (err) {
    return {
      success: false,
      error: `Parse error: ${err instanceof Error ? err.message : String(err)}`,
    };
  }
}

/**
 * Convert parsed S-expression to component schema
 */
export function fromSExprValue(expr: SExpr): IParseResult<IComponentSchema> {
  if (!Array.isArray(expr)) {
    return { success: false, error: 'Expected array expression' };
  }

  // Handle multiple expressions - take first component definition
  if (Array.isArray(expr[0]) && expr[0][0] === 'component') {
    expr = expr[0] as SExpr[];
  }

  const [head, ...rest] = expr;

  if (head !== 'component') {
    return { success: false, error: `Expected 'component', got '${head}'` };
  }

  // Parse component name
  const name = rest[0] as string;
  if (typeof name !== 'string') {
    return { success: false, error: 'Component name must be a string' };
  }

  const schema: IComponentSchema = {
    name: name.replace(/^"|"$/g, ''),  // Remove quotes if present
    accepts: {},
    emits: {},
  };

  // Parse remaining elements
  for (let i = 1; i < rest.length; i++) {
    const item = rest[i];

    // Check for :mount keyword
    if (item === ':mount' && typeof rest[i + 1] === 'string') {
      schema.mount = (rest[i + 1] as string).replace(/^"|"$/g, '');
      i++;
      continue;
    }

    if (!Array.isArray(item)) continue;

    const [type, ...args] = item;

    if (type === 'mount') {
      // Handle (mount "value") form
      schema.mount = (args[0] as string).replace(/^"|"$/g, '');
    } else if (type === 'accepts') {
      const methodName = (args[0] as string).replace(/^"|"$/g, '');
      schema.accepts[methodName] = parseMethodSExpr(args.slice(1) as SExpr[]);
    } else if (type === 'emits') {
      const eventName = (args[0] as string).replace(/^"|"$/g, '');
      schema.emits[eventName] = parseEventSExpr(args.slice(1) as SExpr[]);
    } else if (type === 'component') {
      // Nested component
      const childResult = fromSExprValue(item);
      if (childResult.success && childResult.value) {
        schema.children = schema.children || [];
        schema.children.push(childResult.value);
      }
    }
  }

  return { success: true, value: schema };
}

/**
 * Parse method definition from S-expression
 */
function parseMethodSExpr(args: SExpr[]): IMethodSchema {
  const method: IMethodSchema = { params: {} };

  for (const arg of args) {
    if (!Array.isArray(arg)) continue;

    const [type, ...rest] = arg;
    if (type === 'params') {
      method.params = parseParamsSExpr(rest as SExpr[]);
    }
  }

  return method;
}

/**
 * Parse event definition from S-expression
 */
function parseEventSExpr(args: SExpr[]): IEventSchema {
  const event: IEventSchema = { payload: {} };

  for (const arg of args) {
    if (!Array.isArray(arg)) continue;

    const [type, ...rest] = arg;
    if (type === 'params' || type === 'payload') {
      event.payload = parseParamsSExpr(rest as SExpr[]);
    }
  }

  return event;
}

/**
 * Parse params list from S-expression
 */
function parseParamsSExpr(args: SExpr[]): Record<string, IParamSchema> {
  const params: Record<string, IParamSchema> = {};

  for (const arg of args) {
    if (!Array.isArray(arg)) continue;

    const [name, typeStr] = arg;
    if (typeof name !== 'string' || typeof typeStr !== 'string') continue;

    const cleanName = name.replace(/^"|"$/g, '');
    const optional = typeStr.endsWith('?');
    const type = optional ? typeStr.slice(0, -1) : typeStr;

    params[cleanName] = {
      type: type as IParamSchema['type'],
      optional,
    };
  }

  return params;
}

/**
 * Parse S-expression value to JavaScript value
 */
export function sexprToValue(expr: SExpr): unknown {
  if (expr === 'nil' || expr === null) {
    return null;
  }
  if (typeof expr === 'string') {
    // Remove quotes if present
    if (expr.startsWith('"') && expr.endsWith('"')) {
      return expr.slice(1, -1);
    }
    return expr;
  }
  if (typeof expr === 'number' || typeof expr === 'boolean') {
    return expr;
  }
  if (Array.isArray(expr)) {
    const [head, ...rest] = expr;

    // (list a b c) -> [a, b, c]
    if (head === 'list') {
      return rest.map(sexprToValue);
    }

    // (obj ("key" value) ...) -> { key: value, ... }
    if (head === 'obj') {
      const result: Record<string, unknown> = {};
      for (const item of rest) {
        if (Array.isArray(item) && item.length >= 2) {
          const key = sexprToValue(item[0]) as string;
          const value = sexprToValue(item[1]);
          result[key] = value;
        }
      }
      return result;
    }

    // (quote x) -> x as-is
    if (head === 'quote') {
      return rest[0];
    }

    // Default: array
    return expr.map(sexprToValue);
  }
  return expr;
}
