import type { Interface, Widget, SignalRef } from './types';

/** Escape a string for use as an XML attribute value. */
function xmlEsc(s: string): string {
  return s
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}

/** Serialize an Interface object to an XML string. */
export function serializeInterface(iface: Interface): string {
  const lines: string[] = [];
  lines.push(`<?xml version="1.0" encoding="UTF-8"?>`);
  lines.push(
    `<interface id="${xmlEsc(iface.id)}" name="${xmlEsc(iface.name)}" ` +
    `version="${iface.version}" width="${iface.w}" height="${iface.h}">`
  );
  for (const w of iface.widgets) {
    lines.push(
      `  <widget id="${xmlEsc(w.id)}" type="${xmlEsc(w.type)}" ` +
      `x="${w.x}" y="${w.y}" w="${w.w}" h="${w.h}">`
    );
    for (const sig of w.signals) {
      const colorAttr = sig.color ? ` color="${xmlEsc(sig.color)}"` : '';
      lines.push(`    <signal ds="${xmlEsc(sig.ds)}" name="${xmlEsc(sig.name)}"${colorAttr}/>`);
    }
    for (const [key, value] of Object.entries(w.options)) {
      lines.push(`    <option key="${xmlEsc(key)}" value="${xmlEsc(value)}"/>`);
    }
    lines.push(`  </widget>`);
  }
  lines.push(`</interface>`);
  return lines.join('\n');
}

/**
 * Parse an interface XML string into an Interface object.
 *
 * Expected XML format:
 * <interface name="..." version="1">
 *   <widget id="w1" type="textview" x="100" y="50" w="200" h="60">
 *     <signal ds="stub" name="sine_1hz" color="#ff0000"/>
 *     <option key="label" value="Sine 1Hz"/>
 *   </widget>
 * </interface>
 */
export function parseInterface(xml: string): Interface {
  const parser = new DOMParser();
  const doc = parser.parseFromString(xml, 'application/xml');

  // Check for parse errors
  const parseError = doc.querySelector('parsererror');
  if (parseError) {
    throw new Error(`XML parse error: ${parseError.textContent}`);
  }

  const root = doc.documentElement;
  if (root.tagName !== 'interface') {
    throw new Error(`Expected root element <interface>, got <${root.tagName}>`);
  }

  const ifaceId = root.getAttribute('id') ?? '';
  const name = root.getAttribute('name') ?? 'Untitled';
  const version = parseInt(root.getAttribute('version') ?? '1', 10);
  const w = parseInt(root.getAttribute('width') ?? '1200', 10);
  const h = parseInt(root.getAttribute('height') ?? '800', 10);

  const widgets: Widget[] = [];

  for (const el of Array.from(root.children)) {
    if (el.tagName !== 'widget') continue;

    const id = el.getAttribute('id') ?? '';
    const type = el.getAttribute('type') ?? 'unknown';
    const x = parseFloat(el.getAttribute('x') ?? '0');
    const y = parseFloat(el.getAttribute('y') ?? '0');
    const w = parseFloat(el.getAttribute('w') ?? '100');
    const h = parseFloat(el.getAttribute('h') ?? '60');

    const signals: Array<SignalRef & { color?: string }> = [];
    const options: Record<string, string> = {};

    for (const child of Array.from(el.children)) {
      if (child.tagName === 'signal') {
        const ds = child.getAttribute('ds') ?? '';
        const sigName = child.getAttribute('name') ?? '';
        const color = child.getAttribute('color') ?? undefined;
        signals.push({ ds, name: sigName, ...(color ? { color } : {}) });
      } else if (child.tagName === 'option') {
        const key = child.getAttribute('key');
        const value = child.getAttribute('value');
        if (key !== null && value !== null) {
          options[key] = value;
        }
      }
    }

    widgets.push({ id, type, x, y, w, h, signals, options });
  }

  return { id: ifaceId, name, version, w, h, widgets };
}
