/**
 * Component runtime for lifecycle-managed UI modules.
 *
 * Contract:
 * - component init signature: init(root, config)
 * - init result must expose destroy(): void
 */
import { warnOnce, resolveHost } from './app-dom.js';
import { readPageConfig } from './app-page-config.js';

const toObject = (value) => {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return {};
  }
  return value;
};

const readByPath = (value, path) => {
  const normalizedPath = String(path || '').trim();
  if (normalizedPath === '') {
    return undefined;
  }

  const segments = normalizedPath.split('.').map((item) => item.trim()).filter(Boolean);
  let current = value;
  for (const segment of segments) {
    if (!current || typeof current !== 'object' || Array.isArray(current)) {
      return undefined;
    }
    current = current[segment];
  }
  return current;
};

export function createComponentRuntime(options = {}) {
  const {
    root = document,
    configId = 'app-components',
  } = options;
  const host = resolveHost(root);
  const pageConfig = toObject(readPageConfig(configId, document) || {});
  const componentRegistry = new Map();

  const register = (name, init, componentOptions = {}) => {
    const key = String(name || '').trim();
    if (key === '') {
      warnOnce('UI_COMPONENT_INVALID', 'Cannot register component without name', { module: 'component-runtime' });
      return false;
    }
    if (typeof init !== 'function') {
      warnOnce('UI_COMPONENT_INVALID', `Component "${key}" init is not a function`, { module: 'component-runtime', component: key });
      return false;
    }
    componentRegistry.set(key, {
      name: key,
      init,
      options: toObject(componentOptions),
      api: null,
    });
    return true;
  };

  const mount = (name) => {
    const key = String(name || '').trim();
    const definition = componentRegistry.get(key);
    if (!definition) {
      return null;
    }

    const destroyDefinition = () => {
      if (Array.isArray(definition.api)) {
        definition.api.forEach((instance) => {
          if (instance && typeof instance.destroy === 'function') {
            instance.destroy();
          }
        });
        definition.api = null;
        return;
      }
      if (definition.api && typeof definition.api.destroy === 'function') {
        definition.api.destroy();
      }
      definition.api = null;
    };

    destroyDefinition();

    const configPath = String(definition.options.configPath || '').trim();
    const fallbackConfig = toObject(definition.options.defaultConfig || {});
    const resolvedConfig = toObject(
      configPath !== '' ? readByPath(pageConfig, configPath) : undefined
    );
    const mergedConfig = { ...fallbackConfig, ...resolvedConfig };

    const selector = String(definition.options.selector || '').trim();
    const scope = String(definition.options.scope || '').trim().toLowerCase();

    const targets = scope === 'global' || selector === ''
      ? [host]
      : Array.from(host.querySelectorAll(selector));
    if (targets.length === 0) {
      return null;
    }

    const instances = [];
    targets.forEach((target) => {
      const api = definition.init(target, mergedConfig);
      if (!api || typeof api.destroy !== 'function') {
        warnOnce(
          'UI_COMPONENT_CONTRACT',
          `Component "${key}" must return an API with destroy()`,
          { module: 'component-runtime', component: key }
        );
        return;
      }
      instances.push(api);
    });

    if (instances.length === 0) {
      return null;
    }

    definition.api = instances;
    return instances.length === 1 ? instances[0] : instances;
  };

  const mountAll = () => {
    componentRegistry.forEach((_, key) => {
      mount(key);
    });
  };

  const destroy = (name = null) => {
    const destroyDefinition = (definition) => {
      if (Array.isArray(definition.api)) {
        definition.api.forEach((instance) => {
          if (instance && typeof instance.destroy === 'function') {
            instance.destroy();
          }
        });
      } else if (definition.api && typeof definition.api.destroy === 'function') {
        definition.api.destroy();
      }
      definition.api = null;
    };

    if (name !== null) {
      const key = String(name || '').trim();
      const definition = componentRegistry.get(key);
      if (definition) {
        destroyDefinition(definition);
      }
      return;
    }

    componentRegistry.forEach((definition) => {
      destroyDefinition(definition);
    });
  };

  return {
    register,
    mount,
    mountAll,
    destroy,
    getPageConfig: () => pageConfig,
  };
}
