const MODULE_ID = /^vendoo\.[a-z][a-z0-9-]*$/;

export class FrontendModuleRegistry {
  #modules = new Map();
  #active = null;

  register(definition) {
    const id = String(definition?.id || '');
    if (!MODULE_ID.test(id)) throw new Error(`Ungültige Frontend-Modul-ID: ${id}`);
    if (this.#modules.has(id)) throw new Error(`Frontend-Modul bereits registriert: ${id}`);
    if (typeof definition.mount !== 'function') throw new Error(`Frontend-Modul benötigt mount(): ${id}`);
    this.#modules.set(id, Object.freeze({
      id,
      version: String(definition.version || '1.0.0'),
      status: String(definition.status || 'native'),
      mount: definition.mount,
      unmount: typeof definition.unmount === 'function' ? definition.unmount : async () => {},
    }));
  }

  async mount(id, root, services = {}) {
    if (!(root instanceof Element)) throw new Error('Frontend-Modul benötigt ein gültiges Root-Element.');
    const module = this.#modules.get(id);
    if (!module) throw new Error(`Frontend-Modul ist nicht registriert: ${id}`);
    await this.unmount();
    const controller = new AbortController();
    const context = Object.freeze({
      root,
      services: Object.freeze({ ...services }),
      signal: controller.signal,
      listen(target, event, handler, options = {}) {
        target.addEventListener(event, handler, { ...options, signal: controller.signal });
      },
    });
    await module.mount(context);
    this.#active = { module, context, controller };
    return module;
  }

  async unmount() {
    if (!this.#active) return;
    const active = this.#active;
    this.#active = null;
    active.controller.abort();
    await active.module.unmount(active.context);
  }

  list() {
    return [...this.#modules.values()].map(({ mount: _mount, unmount: _unmount, ...module }) => ({ ...module }));
  }
}
