// src/runtime/LocalRuntimeControl.ts

import type {
  IRuntimeControl,
  ISpawnComponentOptions,
  ISpawnResult,
} from './RuntimeControl';
import type { MatrixRuntime } from './MatrixRuntime';
import type { IMatrixElement } from './IMatrixElement';
import type { IRuntimeComponent } from './IRuntimeComponent';

/**
 * Helper type for components that may expose mount path in different ways.
 */
type MountableElement = {
  context?: { mount?: string; mountPath?: string };
  mountPath?: string;
};

/**
 * Extract mount path from component element (handles various patterns).
 */
function extractMountPath(element: unknown): string | undefined {
  const el = element as MountableElement;
  return el.context?.mount ?? el.context?.mountPath ?? el.mountPath;
}

/**
 * In-process implementation of RuntimeControl that wraps a MatrixRuntime.
 */
export class LocalRuntimeControl implements IRuntimeControl {
  constructor(
    public readonly name: string,
    public readonly mount: string,
    private readonly runtime: MatrixRuntime
  ) {}

  async spawnComponent(opts: ISpawnComponentOptions): Promise<ISpawnResult> {
    const attributes: Record<string, string> = {
      ...(opts.attributes ?? {}),
    };

    if (opts.mount) {
      // Honor explicit mount by passing it as the element's "mount" attribute.
      attributes['mount'] = opts.mount;
    }

    const element =
      await this.runtime.createElement<IMatrixElement>(opts.tag, attributes);

    await this.runtime.mount(element);

    const mountPath = extractMountPath(element);

    if (!mountPath) {
      throw new Error(
        `[LocalRuntimeControl] Spawned component '${opts.tag}' did not expose a mount`
      );
    }

    return { mount: mountPath };
  }

  async destroyComponent(mount: string): Promise<void> {
    const active = this.runtime.getActiveComponents();
    const target = active.find((c: IRuntimeComponent) => c.context.mount === mount);
    if (!target) return;
    await this.runtime.unmount(target.element);
  }

  async destroyAll(): Promise<void> {
    const active = this.runtime.getActiveComponents();
    for (const component of active) {
      await this.runtime.unmount(component.element);
    }
  }
}
