"""Node type registry"""
from __future__ import annotations

from typing import Any, Optional

from .node import WorkflowNode, get_pending_registrations, clear_pending_registrations


class NodeTypeRegistry:
    """
    Central registry for all workflow node types.
    Supports both built-in and plugin-provided nodes.
    """
    
    _instance: Optional['NodeTypeRegistry'] = None
    
    def __init__(self):
        self._nodes: dict[str, type[WorkflowNode]] = {}
        self._categories: dict[str, list[str]] = {
            'trigger': [],
            'process': [],
            'control': [],
            'action': [],
            'integration': [],
            'misc': [],
        }
    
    @classmethod
    def instance(cls) -> 'NodeTypeRegistry':
        """Get singleton instance"""
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance
    
    def register(self, node_type: str, node_class: type[WorkflowNode]):
        """
        Register a node type.
        
        Args:
            node_type: Unique type identifier
            node_class: WorkflowNode subclass
        """
        self._nodes[node_type] = node_class
        
        # Add to category
        category = getattr(node_class, 'category', 'misc')
        if category not in self._categories:
            self._categories[category] = []
        if node_type not in self._categories[category]:
            self._categories[category].append(node_type)
    
    def unregister(self, node_type: str):
        """Unregister a node type"""
        if node_type in self._nodes:
            node_class = self._nodes[node_type]
            category = getattr(node_class, 'category', 'misc')
            if category in self._categories and node_type in self._categories[category]:
                self._categories[category].remove(node_type)
            del self._nodes[node_type]
    
    def get(self, node_type: str) -> Optional[type[WorkflowNode]]:
        """Get node class by type. Supports both 'category.type_name' and short 'type_name' formats."""
        # First try exact match (category.type_name format)
        if node_type in self._nodes:
            return self._nodes[node_type]
        
        # Try short name format (e.g., 'dify_workflow' -> 'integration.dify_workflow')
        # Search through all registered nodes for a matching type_name
        for registered_type, node_class in self._nodes.items():
            if node_class.type_name == node_type:
                return node_class
        
        return None
    
    def create_instance(self, node_type: str, node_id: str, config: dict[str, Any], ap: Optional['app.Application'] = None) -> Optional[WorkflowNode]:
        """Create a node instance. Supports both 'category.type_name' and short 'type_name' formats."""
        node_class = self.get(node_type)
        if node_class:
            return node_class(node_id, config, ap=ap)
        return None
    
    def list_all(self) -> list[dict[str, Any]]:
        """
        Get all registered node types as schema list.
        
        Returns:
            List of node schemas
        """
        return [
            node_class.to_schema()
            for node_class in self._nodes.values()
        ]
    
    def list_by_category(self, category: str) -> list[dict[str, Any]]:
        """
        Get node types by category.
        
        Args:
            category: Category name (trigger, process, control, action, integration, misc)
            
        Returns:
            List of node schemas in the category
        """
        if category not in self._categories:
            return []
        return [
            self._nodes[node_type].to_schema()
            for node_type in self._categories[category]
            if node_type in self._nodes
        ]
    
    def get_categories(self) -> dict[str, list[dict[str, Any]]]:
        """
        Get all nodes organized by category.
        
        Returns:
            Dictionary mapping category names to lists of node schemas
        """
        return {
            category: self.list_by_category(category)
            for category in self._categories.keys()
        }
    
    def has_type(self, node_type: str) -> bool:
        """Check if a node type is registered. Supports both formats."""
        return self.get(node_type) is not None
    
    def process_pending_registrations(self):
        """Process all pending node registrations from decorators"""
        for node_type, node_class in get_pending_registrations():
            # Use category.type_name format for consistency with frontend
            category = getattr(node_class, 'category', 'misc')
            full_type = f'{category}.{node_type}'
            self.register(full_type, node_class)
        clear_pending_registrations()
    
    def count(self) -> int:
        """Get total number of registered node types"""
        return len(self._nodes)
    
    def clear(self):
        """Clear all registrations (for testing)"""
        self._nodes.clear()
        for category in self._categories:
            self._categories[category] = []


# Convenience functions for module-level access
def register_node(node_type: str, node_class: type[WorkflowNode]):
    """Register a node type to the global registry"""
    NodeTypeRegistry.instance().register(node_type, node_class)


def get_node_class(node_type: str) -> Optional[type[WorkflowNode]]:
    """Get a node class from the global registry"""
    return NodeTypeRegistry.instance().get(node_type)


def list_node_types() -> list[dict[str, Any]]:
    """List all registered node types"""
    return NodeTypeRegistry.instance().list_all()
