#!/usr/bin/env python3
"""
Central Model Registry for Service Finder

Automatically discovers and imports all SQLAlchemy models from the models directory,
ensuring Base.metadata is fully populated with tables, constraints, and indexes.

Usage:
    from app.models.registry import Base, get_all_models, ensure_models_loaded
"""

import importlib
import os
import sys
from pathlib import Path
from typing import Dict, List, Type

from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.orm import DeclarativeBase

# Import the Base from database (circular dependency will be resolved later)
# We'll define our own Base if needed, but better to reuse existing one.
# We'll import after path setup.

# Add backend to path if not already
backend_dir = Path(__file__).parent.parent.parent
if str(backend_dir) not in sys.path:
    sys.path.insert(0, str(backend_dir))

# Import Base from database (this will be the same Base used everywhere)
from app.database import Base

def discover_model_files() -> List[Path]:
    """
    Walk through models directory and collect all .py files except __init__.py and registry.py.
    """
    models_dir = Path(__file__).parent
    model_files = []
    for root, _, files in os.walk(models_dir):
        for file in files:
            if file.endswith('.py') and file not in ('__init__.py', 'registry.py'):
                full_path = Path(root) / file
                model_files.append(full_path)
    return model_files

def import_module_from_file(file_path: Path) -> str:
    """
    Import a Python module from its file path.
    Returns the module name.
    """
    # Compute module name relative to backend/app
    rel_path = file_path.relative_to(backend_dir)
    module_name = str(rel_path).replace(os.sep, '.').replace('.py', '')
    
    try:
        spec = importlib.util.spec_from_file_location(module_name, file_path)
        if spec is None:
            raise ImportError(f"Could not load spec for {module_name}")
        module = importlib.util.module_from_spec(spec)
        sys.modules[module_name] = module
        spec.loader.exec_module(module)
        return module_name
    except Exception as e:
        # Silently skip import errors (maybe due to missing dependencies)
        # but log for debugging
        print(f"⚠️  Could not import {module_name}: {e}", file=sys.stderr)
        return None

def load_all_models() -> List[str]:
    """
    Dynamically import all model files to populate Base.metadata.
    Returns list of successfully imported module names.
    """
    model_files = discover_model_files()
    imported = []
    for file in model_files:
        module_name = import_module_from_file(file)
        if module_name:
            imported.append(module_name)
    # Also ensure the __init__.py is loaded (it imports many models manually)
    try:
        import app.models
        imported.append('app.models')
    except ImportError:
        pass
    print(f"✅ Registry loaded {len(imported)} model modules. Total tables in metadata: {len(Base.metadata.tables)}")
    return imported

def get_all_models() -> Dict[str, Type[DeclarativeMeta]]:
    """
    Return a mapping of class name to model class for all registered SQLAlchemy models.
    This works only after models have been imported.
    """
    # This is a heuristic: find all subclasses of Base in loaded modules
    from sqlalchemy.orm import DeclarativeBase
    models = {}
    for cls in Base.__subclasses__():
        models[cls.__name__] = cls
    # Also check deeper inheritance (if models inherit from other models that inherit from Base)
    for module_name, module in sys.modules.items():
        if module_name.startswith('app.models.'):
            for attr_name in dir(module):
                attr = getattr(module, attr_name)
                if isinstance(attr, type) and issubclass(attr, Base) and attr is not Base:
                    models[attr.__name__] = attr
    return models

def ensure_models_loaded():
    """
    Ensure that all models are loaded into Base.metadata.
    This is idempotent and can be called multiple times.
    """
    if len(Base.metadata.tables) == 0:
        load_all_models()
    else:
        # Already loaded
        pass

# Auto-load models when this module is imported (optional, but useful)
# We'll make it explicit via a function call to avoid side effects.
# Instead, we'll provide a function to trigger loading.

# Export
__all__ = ['Base', 'discover_model_files', 'load_all_models', 'get_all_models', 'ensure_models_loaded']