import importlib
import inspect
from pathlib import Path
import threading
from typing import Callable, Dict, List, Optional, Union
from fastapi import Request, Response
import logging
from .config_manager import ConfigManager

logger = logging.getLogger(__name__)

class HttpMiddlewareManager:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._init()
        return cls._instance

    def _init(self):
        self._project_root = None
        self._config = ConfigManager()
        self._middlewares: Dict[str, Dict[str, List[Callable]]] = {
            'global': {'before': [], 'after': [], 'all': []},
            'modules': {}
        }
        self._lock = threading.RLock()

    def set_project_root(self, path: Path):
        with self._lock:
            self._project_root = path.resolve()

    async def load_all(self):
        """Charge tous les middlewares HTTP"""
        with self._lock:
            # Global middlewares
            global_mw_dir = self._project_root / 'middlewares'
            if global_mw_dir.exists():
                self._load_middlewares('global', global_mw_dir)

            # Module middlewares
            modules_metadata = self._config.get_modules_metadata()
            modules_dir = self._project_root / 'modules'
            if modules_dir.exists():
                for module_name in modules_metadata.keys():
                    module_mw_dir = modules_dir / module_name / 'middlewares'
                    if module_mw_dir.exists():
                        self._load_middlewares(module_name, module_mw_dir)

    def _load_middlewares(self, context: str, mw_dir: Path):
        """Charge les middlewares depuis un répertoire"""
        for mw_file in mw_dir.glob('*.py'):
            if mw_file.name.startswith(('_', '.')):
                continue

            try:
                module_path = '.'.join(mw_file.relative_to(mw_dir.parent).with_suffix('').parts)
                module = importlib.import_module(module_path)

                for name, obj in inspect.getmembers(module):
                    if name.startswith(('before_', 'after_', 'all_')):
                        phase = name.split('_')[0]
                        self._register_middleware(context, phase, obj)

            except Exception as e:
                logger.error(f"Failed to load middleware {mw_file}: {e}")

    def _register_middleware(self, context: str, phase: str, middleware: Callable):
        """Enregistre un middleware"""
        with self._lock:
            if context not in self._middlewares['modules']:
                self._middlewares['modules'][context] = {'before': [], 'after': [], 'all': []}
            
            self._middlewares['modules'][context][phase].append(middleware)
            logger.info(f"Registered HTTP {phase} middleware for {context}")

    async def apply_middlewares(self, request: Request, call_next, context: str = 'global') -> Response:
        """Applique la chaîne de middlewares"""
        # Récupère les middlewares dans l'ordre
        with self._lock:
            middlewares = {
                'before': self._middlewares['global']['before'] + 
                         self._middlewares['modules'].get(context, {}).get('before', []),
                'all': self._middlewares['global']['all'] + 
                       self._middlewares['modules'].get(context, {}).get('all', []),
                'after': self._middlewares['global']['after'] + 
                        self._middlewares['modules'].get(context, {}).get('after', [])
            }

        # Exécute les middlewares 'before' et 'all' (avant)
        for phase in ['before', 'all']:
            for middleware in middlewares[phase]:
                request = await self._execute_middleware(middleware, request, None)

        # Exécute la route principale
        response = await call_next(request)

        # Exécute les middlewares 'after' et 'all' (après)
        for phase in ['after', 'all']:
            for middleware in middlewares[phase]:
                response = await self._execute_middleware(middleware, request, response)

        return response

    async def _execute_middleware(self, middleware: Callable, request: Request, response: Optional[Response]):
        """Exécute un middleware individuel"""
        try:
            if inspect.iscoroutinefunction(middleware):
                if response is None:
                    return await middleware(request)
                else:
                    return await middleware(request, response)
            else:
                if response is None:
                    return middleware(request)
                else:
                    return middleware(request, response)
        except Exception as e:
            logger.error(f"Middleware execution failed: {e}")
            raise