"""
OMNIWEB Application - Core framework class
Author: Juste Elysée MALANDILA
"""

from typing import Callable, Dict, List, Optional, Any
import asyncio
from starlette.applications import Starlette
from starlette.routing import Route, Mount
from starlette.middleware import Middleware as StarletteMiddleware
from starlette.middleware.cors import CORSMiddleware
import uvicorn

from .routing import Router
from .middleware import Middleware
from .request import Request
from .response import Response, JSONResponse
from .exceptions import HTTPException


class OmniWeb:
    """
    The main OMNIWEB application class.
    
    One Framework, Every Platform - Web + Desktop with Django's power and FastAPI's speed.
    
    Example:
        >>> app = OmniWeb()
        >>> 
        >>> @app.get("/")
        >>> async def home():
        >>>     return {"message": "Hello from OMNIWEB!"}
        >>> 
        >>> if __name__ == "__main__":
        >>>     app.run()  # Web mode
        >>>     # app.run_desktop()  # Desktop mode
    """
    
    def __init__(
        self,
        title: str = "OMNIWEB App",
        version: str = "0.1.0",
        description: str = "",
        debug: bool = False,
        cors: bool = True,
        cors_origins: List[str] = ["*"],
    ):
        """
        Initialize OMNIWEB application.
        
        Args:
            title: Application title
            version: Application version
            description: Application description
            debug: Enable debug mode
            cors: Enable CORS middleware
            cors_origins: Allowed CORS origins
        """
        self.title = title
        self.version = version
        self.description = description
        self.debug = debug
        
        # Routing
        self.router = Router()
        self.routes: List[Route] = []
        
        # Middleware
        self.middlewares: List[Middleware] = []
        self._middleware_stack: List[StarletteMiddleware] = []
        
        # Add CORS if enabled
        if cors:
            self.add_cors(origins=cors_origins)
        
        # State & Config
        self.state: Dict[str, Any] = {}
        self.config: Dict[str, Any] = {}
        
        # Starlette app (internal)
        self._app: Optional[Starlette] = None
        
    def add_cors(
        self,
        origins: List[str] = ["*"],
        methods: List[str] = ["*"],
        headers: List[str] = ["*"],
    ):
        """Add CORS middleware."""
        self._middleware_stack.append(
            StarletteMiddleware(
                CORSMiddleware,
                allow_origins=origins,
                allow_credentials=True,
                allow_methods=methods,
                allow_headers=headers,
            )
        )
        
    def route(
        self,
        path: str,
        methods: List[str] = ["GET"],
        name: Optional[str] = None,
    ):
        """
        Route decorator.
        
        Example:
            >>> @app.route("/users/{id}", methods=["GET", "POST"])
            >>> async def user(id: int):
            >>>     return {"id": id}
        """
        def decorator(func: Callable):
            self.router.add_route(path, func, methods=methods, name=name)
            return func
        return decorator
    
    def get(self, path: str, name: Optional[str] = None):
        """GET route decorator."""
        return self.route(path, methods=["GET"], name=name)
    
    def post(self, path: str, name: Optional[str] = None):
        """POST route decorator."""
        return self.route(path, methods=["POST"], name=name)
    
    def put(self, path: str, name: Optional[str] = None):
        """PUT route decorator."""
        return self.route(path, methods=["PUT"], name=name)
    
    def delete(self, path: str, name: Optional[str] = None):
        """DELETE route decorator."""
        return self.route(path, methods=["DELETE"], name=name)
    
    def patch(self, path: str, name: Optional[str] = None):
        """PATCH route decorator."""
        return self.route(path, methods=["PATCH"], name=name)
    
    def middleware(self, name: Optional[str] = None):
        """
        Middleware decorator.
        
        Example:
            >>> @app.middleware()
            >>> async def auth_middleware(request, call_next):
            >>>     # Before request
            >>>     response = await call_next(request)
            >>>     # After request
            >>>     return response
        """
        def decorator(func: Callable):
            middleware = Middleware(func, name=name)
            self.middlewares.append(middleware)
            return func
        return decorator
    
    def include_router(
        self,
        router: Router,
        prefix: str = "",
        tags: Optional[List[str]] = None,
    ):
        """
        Include a router with optional prefix.
        
        Example:
            >>> users_router = Router()
            >>> @users_router.get("/")
            >>> async def list_users():
            >>>     return []
            >>> 
            >>> app.include_router(users_router, prefix="/users")
        """
        for route in router.routes:
            path = f"{prefix}{route.path}"
            self.router.add_route(
                path,
                route.endpoint,
                methods=route.methods,
                name=route.name,
            )
    
    def _build_starlette_app(self) -> Starlette:
        """Build internal Starlette application."""
        # Convert routes
        starlette_routes = []
        for route in self.router.routes:
            starlette_routes.append(
                Route(
                    route.path,
                    route.endpoint,
                    methods=route.methods,
                    name=route.name,
                )
            )
        
        # Build app
        app = Starlette(
            debug=self.debug,
            routes=starlette_routes,
            middleware=self._middleware_stack,
        )
        
        return app
    
    async def __call__(self, scope, receive, send):
        """ASGI application callable."""
        if self._app is None:
            self._app = self._build_starlette_app()
        await self._app(scope, receive, send)
    
    def run(
        self,
        host: str = "0.0.0.0",
        port: int = 8000,
        reload: bool = False,
        workers: int = 1,
    ):
        """
        Run the OMNIWEB application.
        
        Args:
            host: Host to bind to
            port: Port to bind to
            reload: Enable auto-reload (dev mode)
            workers: Number of worker processes
        
        Example:
            >>> app = Nexium()
            >>> app.run(host="127.0.0.1", port=8000, reload=True)
        """
        if self.debug and not reload:
            reload = True
        
        print(f"""
╔══════════════════════════════════════════════════════════╗
║                                                          ║
║  🌐 OMNIWEB - One Framework, Every Platform             ║
║                                                          ║
║  Created by: Juste Elysée MALANDILA                     ║
║  Version: {self.version:<44}║
║                                                          ║
╚══════════════════════════════════════════════════════════╝

🚀 Starting server...
📍 URL: http://{host}:{port}
🔧 Debug: {self.debug}
🔄 Reload: {reload}
👷 Workers: {workers}
        """)
        
        uvicorn.run(
            "omniweb.app:app" if reload else self,
            host=host,
            port=port,
            reload=reload,
            workers=workers,
            log_level="info" if self.debug else "warning",
        )


# Global app instance (for CLI and auto-discovery)
app = OmniWeb()
