/**
 * el-ui — Activation-based frontend framework.
 *
 * State is an Engram graph. Reactivity is spreading activation.
 *
 * Core exports:
 *   Graph     — in-browser Engram graph (nodes, edges, activation)
 *   Renderer  — DOM mounting and patching
 *   Router    — graph-based routing
 *   Component — base class for el-ui components
 *   mount()   — attach a component to the DOM
 *
 * @module el-ui
 */

export { Graph } from './graph.js';
export { Renderer } from './renderer.js';
export { Router } from './router.js';
export {
    spreadActivation,
    activationStrength,
    reachableNodes,
    PRUNE_THRESHOLD,
} from './activation.js';

import { Graph } from './graph.js';
import { Renderer } from './renderer.js';

/**
 * Base class for all el-ui components.
 *
 * Subclasses override:
 *   render()   → returns HTML string
 *   onMount()  → called after first mount (optional)
 *
 * Subclasses call:
 *   setState(name, value)  → update state, trigger activation, patch DOM
 */
export class Component {
    constructor(props = {}) {
        /** @type {Graph} */
        this._graph = new Graph();

        /** @type {Record<string, string>} state node IDs */
        this._stateNodes = {};

        /** @type {Record<string, any>} current state values */
        this._state = {};

        /** @type {Record<string, any>} current prop values */
        this.props = props;

        /** @type {Renderer|null} */
        this._renderer = null;
    }

    /**
     * Update a named state variable.
     * Triggers spreading activation in the graph, then patches the DOM.
     *
     * @param {string} name
     * @param {any} value
     */
    setState(name, value) {
        if (this._stateNodes[name] !== undefined) {
            this._graph.update(this._stateNodes[name], value);
            // Graph.update() notifies subscribers, which call _renderer.patch().
            // Belt-and-suspenders: also patch directly in case no subscribers yet.
            if (this._renderer) this._renderer.patch();
        }
    }

    /**
     * Render a child component inline, sharing the parent's activation graph.
     * Used by the compiler to embed child components in template strings.
     *
     * @param {typeof Component} ComponentClass
     * @param {Record<string, any>} props
     * @returns {string}
     */
    _child(ComponentClass, props = {}) {
        try {
            const instance = new ComponentClass(props);
            // Share the parent graph so child state participates in the same
            // activation surface. Child state changes can propagate upward.
            instance._graph = this._graph;
            return instance.render();
        } catch (e) {
            console.warn(`el-ui: failed to render child ${ComponentClass?.name}`, e);
            return `<!-- el-ui: render error in ${ComponentClass?.name} -->`;
        }
    }

    /**
     * Override to return the component's HTML string.
     * All state and props are accessible via this._state / this.props.
     * @returns {string}
     */
    render() {
        return '';
    }

    /**
     * Called once after the component is first mounted to the DOM.
     * Override for side effects (timers, fetch calls, subscriptions).
     */
    onMount() {}
}

/**
 * Mount a component to a DOM element and start the el-ui runtime.
 *
 * @param {typeof Component} ComponentClass  the component class to mount
 * @param {string} selector  CSS selector for the root element
 * @param {Record<string, any>} [props={}]  initial props
 * @returns {Component}  the live component instance
 *
 * @example
 * import { mount } from './el-ui.js';
 * import { Counter } from './counter.js';
 * mount(Counter, '#app');
 */
export function mount(ComponentClass, selector, props = {}) {
    const root = document.querySelector(selector);
    if (!root) {
        throw new Error(`el-ui: no element found for selector '${selector}'`);
    }

    const component = new ComponentClass(props);
    const renderer = new Renderer(root, component);
    component._renderer = renderer;
    renderer.mount();
    return component;
}

export default { mount, Component, Graph, Renderer };
