// src/serialization/fromHtml.ts
// Parse HTML representation to Matrix component schema
//
// Parses custom elements created by toHtml back into component schemas.
//
// Reference: Vision/14-HOMOICONIC-ARCHITECTURE.md, Vision/20-COMPOSITION-DSL.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';

/**
 * Parse HTML string to component schema
 *
 * @example
 * fromHtml('<matrix-button mount="app.btn"><mx-accepts method="click"></mx-accepts></matrix-button>')
 * // Returns: { success: true, value: { name: 'Button', mount: 'app.btn', accepts: { click: {} }, emits: {} } }
 */
export function fromHtml(html: string): IParseResult<IComponentSchema> {
  try {
    // Use DOMParser if available (browser), otherwise parse manually
    if (typeof DOMParser !== 'undefined') {
      return parseWithDOM(html);
    } else {
      return parseManually(html);
    }
  } catch (err) {
    return {
      success: false,
      error: `Failed to parse HTML: ${err instanceof Error ? err.message : String(err)}`,
    };
  }
}

/**
 * Parse using DOM API (browser environment)
 */
function parseWithDOM(html: string): IParseResult<IComponentSchema> {
  const parser = new DOMParser();
  const doc = parser.parseFromString(html, 'text/html');
  const root = doc.body.firstElementChild;

  if (!root) {
    return { success: false, error: 'No root element found' };
  }

  const schema = parseElement(root);
  return { success: true, value: schema };
}

/**
 * Parse element to schema
 */
function parseElement(el: Element): IComponentSchema {
  const tagName = el.tagName.toLowerCase();

  // Extract component name from tag (matrix-button -> Button)
  const name = tagName.startsWith('matrix-')
    ? toPascalCase(tagName.slice(7))
    : toPascalCase(tagName);

  const schema: IComponentSchema = {
    name,
    accepts: {},
    emits: {},
  };

  // Get mount attribute
  const mount = el.getAttribute('mount');
  if (mount) {
    schema.mount = mount;
  }

  // Parse child elements
  const children: IComponentSchema[] = [];

  for (const child of Array.from(el.children)) {
    const childTag = child.tagName.toLowerCase();

    if (childTag === 'mx-accepts') {
      const method = child.getAttribute('method');
      if (method) {
        schema.accepts[method] = parseMethodAttrs(child);
      }
    } else if (childTag === 'mx-emits') {
      const event = child.getAttribute('event');
      if (event) {
        schema.emits[event] = parseEventAttrs(child);
      }
    } else if (childTag.startsWith('matrix-')) {
      children.push(parseElement(child));
    }
  }

  if (children.length > 0) {
    schema.children = children;
  }

  return schema;
}

/**
 * Parse method attributes from element
 */
function parseMethodAttrs(el: Element): IMethodSchema {
  const method: IMethodSchema = { params: {} };

  for (const attr of Array.from(el.attributes)) {
    if (attr.name === 'method') continue;

    // Parse param: name="type?" -> { type, optional }
    const optional = attr.value.endsWith('?');
    const type = optional ? attr.value.slice(0, -1) : attr.value;

    method.params[attr.name] = {
      type: type as IParamSchema['type'],
      optional,
    };
  }

  return method;
}

/**
 * Parse event attributes from element
 */
function parseEventAttrs(el: Element): IEventSchema {
  const event: IEventSchema = { payload: {} };

  for (const attr of Array.from(el.attributes)) {
    if (attr.name === 'event') continue;

    const optional = attr.value.endsWith('?');
    const type = optional ? attr.value.slice(0, -1) : attr.value;

    event.payload[attr.name] = {
      type: type as IParamSchema['type'],
      optional,
    };
  }

  return event;
}

/**
 * Manual parsing for non-browser environments
 */
function parseManually(html: string): IParseResult<IComponentSchema> {
  // Simple regex-based parsing for basic cases
  const tagMatch = html.match(/<(matrix-[\w-]+)([^>]*)>/);
  if (!tagMatch) {
    return { success: false, error: 'No matrix element found' };
  }

  const tagName = tagMatch[1];
  const attrs = tagMatch[2];
  const name = toPascalCase(tagName.slice(7));

  const schema: IComponentSchema = {
    name,
    accepts: {},
    emits: {},
  };

  // Extract mount
  const mountMatch = attrs.match(/mount="([^"]+)"/);
  if (mountMatch) {
    schema.mount = mountMatch[1];
  }

  // Extract accepts
  const acceptsMatches = html.matchAll(/<mx-accepts\s+method="([^"]+)"([^>]*)>/g);
  for (const match of acceptsMatches) {
    schema.accepts[match[1]] = { params: {} };
  }

  // Extract emits
  const emitsMatches = html.matchAll(/<mx-emits\s+event="([^"]+)"([^>]*)>/g);
  for (const match of emitsMatches) {
    schema.emits[match[1]] = { payload: {} };
  }

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

/**
 * Convert kebab-case to PascalCase
 */
function toPascalCase(str: string): string {
  return str
    .split('-')
    .map(part => part.charAt(0).toUpperCase() + part.slice(1))
    .join('');
}
