import { NodeFunction, NodeMetadata } from '../types/config';
import { BaseNode } from '../nodes/baseNode';
import { ConfigurationError } from '../errors';

type NodeImplementation = NodeFunction | BaseNode;

interface RegisteredNode {
    id: string;
    implementation: NodeImplementation;
    isClassInstance: boolean;
    description?: string;
}

export class NodeRegistry {
  private nodes = new Map<string, RegisteredNode>();

  register(node: BaseNode): void;
  register(id: string, func: NodeFunction, description?: string): void;
  register(idOrNode: string | BaseNode, func?: NodeFunction, description?: string): void {
    let id: string;
    let implementation: NodeImplementation;
    let isClassInstance = false;
    let nodeDescription = description;

    if (typeof idOrNode === 'string') {
      // Function registration
      id = idOrNode;
      if (!func) throw new ConfigurationError(`Node function is required when registering by ID '${id}'.`);
      implementation = func;
      nodeDescription = description;
    } else if (idOrNode instanceof BaseNode) {
      // Class instance registration
      id = idOrNode.metadata.id;
      implementation = idOrNode;
      isClassInstance = true;
      nodeDescription = idOrNode.metadata.description || description; // Prefer metadata description
    } else {
      throw new ConfigurationError('Invalid arguments for node registration.');
    }

    if (this.nodes.has(id)) {
      console.warn(`[NodeRegistry] Node with ID '${id}' is already registered. Overwriting.`);
    }

    this.nodes.set(id, { id, implementation, isClassInstance, description: nodeDescription });
    console.log(`[NodeRegistry] Node '${id}' registered.`);
  }

  get(id: string): RegisteredNode | undefined {
    return this.nodes.get(id);
  }

  getNodeImplementation(id: string): NodeImplementation | undefined {
    return this.nodes.get(id)?.implementation;
  }

  listNodeIds(): string[] {
      return Array.from(this.nodes.keys());
  }
}