
import type { IChildManagementBehavior, IAddChildOptions } from './IChildManagementBehavior';
import type { IMatrixContext } from '../../engine/core/IMatrixContext';
import { ChildManager, type IComponentClass } from '../children/ChildManager';
import type { IDynamicComponentDefinition } from '../composite';
import { MatrixActor } from '../MatrixActor';

/**
 * Implements child management logic.
 * Wraps ChildManager and handles the high-level coordination.
 */
export class ChildManagementBehavior implements IChildManagementBehavior {
    private readonly children: ChildManager;
    private readonly context: IMatrixContext;

    constructor(context: IMatrixContext) {
        this.context = context;
        this.children = new ChildManager(context);
    }

    get childManager(): ChildManager {
        return this.children;
    }

    async addChild(
        id: string,
        ComponentClass: IComponentClass,
        options: IAddChildOptions = {}
    ): Promise<string> {
        if (!this.context.runtime) {
            throw new Error('[ChildManagementBehavior] No runtime - cannot create children');
        }

        const mount = await this.children.addChild(id, ComponentClass, {
            slot: options.slot,
            props: options.props,
            position: options.position
        });

        return mount;
    }

    async removeChild(id: string): Promise<void> {
        await this.children.removeChild(id);
    }

    async addDynamicChild(params: IDynamicComponentDefinition): Promise<{
        mount: string;
        adapterId: string;
    }> {
        const { id, code, template, styles, tag, shellTag } = params;
        const customTag = tag || shellTag;

        if (this.children.hasChild(id)) {
            throw new Error(`Child ${id} already exists`);
        }

        const ComponentClass = this._compileDynamicComponent(code, template, styles);

        // Hash code for versioning
        const codeHash = this._hashCode(code);
        const requestedId = customTag || `dynamic-${id}-${codeHash}`;

        const runtimeAdapterId = this.context.runtime.registerExternalAdapter?.(
            requestedId,
            ComponentClass,
            { template, styles, tag: requestedId }
        );
        const adapterId = runtimeAdapterId || requestedId;

        const mount = await this.addChild(id, ComponentClass, {
            shellTag: adapterId, // Pass generic options just in case (legacy)
        });

        return { mount, adapterId };
    }

    listDynamicChildren() {
        // ChildManager doesn't track 'isDynamic' explicitly yet.
        // We can infer or add it if needed later. For now returning all children is "safe enough" or we filter by convention.
        return this.children.getAllChildren().map(c => ({
            id: c.id,
            mount: c.mount,
            createdAt: c.createdAt
        }));
    }

    getChildMount(id: string): string | undefined {
        return this.children.getChildMount(id);
    }

    hasChild(id: string): boolean {
        return this.children.hasChild(id);
    }

    // Helpers

    private _compileDynamicComponent(code: string, template?: string, styles?: string): IComponentClass {
        if (!this.context.runtime.isDynamicCompilationAllowed?.()) {
            throw new Error('Dynamic compilation disabled');
        }

        const factory = new Function(
            'MatrixActor',
            `"use strict"; ${code}`
        );

        const ComponentClass = factory(MatrixActor);

        if (template) ComponentClass.template = styles ? `<style>${styles}</style>${template}` : template;

        return ComponentClass as IComponentClass;
    }

    private _hashCode(str: string): string {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            hash = ((hash << 5) - hash) + str.charCodeAt(i);
            hash |= 0;
        }
        return Math.abs(hash).toString(36);
    }
}
